@@ -441,13 +441,15 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
441441 }
442442 const api = new AtelierAPI ( uri ) ;
443443 let created = false ;
444+ let update = false ;
445+ const isCls = ! csp && fileName . split ( "." ) . pop ( ) . toLowerCase ( ) == "cls" ;
444446 // Use _lookup() instead of _lookupAsFile() so we send
445447 // our cached mtime with the GET /doc request if we have it
446448 return this . _lookup ( uri )
447449 . then (
448450 async ( entry : File ) => {
449451 // Check cases for which we should fail the write and leave the document dirty if changed
450- if ( ! csp && fileName . split ( "." ) . pop ( ) . toLowerCase ( ) == "cls" ) {
452+ if ( isCls ) {
451453 // Check if the class name and file name match
452454 let clsname = "" ;
453455 const match = new TextDecoder ( ) . decode ( content ) . match ( classNameRegex ) ;
@@ -491,7 +493,10 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
491493 } ,
492494 true
493495 )
494- . then ( ( ) => entry )
496+ . then ( ( data ) => {
497+ update = isCls || data . result . content . length > 0 ;
498+ return entry ;
499+ } )
495500 . catch ( ( error ) => {
496501 // Throw all failures
497502 throw vscode . FileSystemError . Unavailable ( stringifyError ( error ) || uri ) ;
@@ -533,25 +538,52 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
533538 // Create an entry in our cache for the document
534539 return this . _lookupAsFile ( uri ) . then ( ( entry ) => {
535540 created = true ;
541+ update = isCls || data . result . content . length > 0 ;
536542 this . _fireSoon ( { type : vscode . FileChangeType . Created , uri } ) ;
537543 return entry ;
538544 } ) ;
539545 } ) ;
540546 }
541547 )
542548 . then ( ( entry ) => {
549+ if ( ! entry ) return ; // entry is only empty when uri is open in a low-code editor
543550 // Compile the document if required
544551 if (
545552 ! uri . path . includes ( "/_vscode/" ) &&
546553 vscode . workspace . getConfiguration ( "objectscript" , uri ) . get ( "compileOnSave" )
547554 ) {
548- this . compile ( uri , entry ) ;
555+ this . compile ( uri , entry , update ) ;
556+ } else if ( update ) {
557+ // The file's contents may have changed as a result of the save,
558+ // so make sure we notify VS Code and any watchers of the change
559+ this . _notifyOfFileChange ( uri ) ;
549560 } else if ( ! created ) {
550561 this . _fireSoon ( { type : vscode . FileChangeType . Changed , uri } ) ;
551562 }
552563 } ) ;
553564 }
554565
566+ /**
567+ * Notify VS Code and any watchers that the contents of `uri` changed.
568+ * Use this function instead of firing the file change event directly
569+ * when we need to force the VS Code UI to show the change. For example,
570+ * if the server changed the document during a save.
571+ */
572+ private _notifyOfFileChange ( uri : vscode . Uri ) : void {
573+ // The file's contents may have changed as a result of the save,
574+ // so make sure we notify VS Code and any watchers of the change
575+ const uriString = uri . toString ( ) ;
576+ if ( vscode . window . activeTextEditor ?. document . uri . toString ( ) == uriString ) {
577+ setTimeout ( ( ) => {
578+ const activeDoc = vscode . window . activeTextEditor ?. document ;
579+ if ( activeDoc && ! activeDoc . isDirty && ! activeDoc . isClosed && activeDoc . uri . toString ( ) == uriString ) {
580+ // Force VS Code to refresh the file's contents in the editor UI
581+ vscode . commands . executeCommand ( "workbench.action.files.revert" ) ;
582+ }
583+ } , 25 ) ;
584+ }
585+ }
586+
555587 /** Process a Document object that was successfully deleted. */
556588 private async processDeletedDoc ( doc : Document , uri : vscode . Uri , csp : boolean , project : boolean ) : Promise < void > {
557589 const events : vscode . FileChangeEvent [ ] = [ ] ;
@@ -763,9 +795,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
763795 /**
764796 * If `uri` is a file, compile it.
765797 * If `uri` is a directory, compile its contents.
766- * `file` is passed if called from `writeFile()`.
798+ * `file` and `update` are passed if called from `writeFile()`.
767799 */
768- public async compile ( uri : vscode . Uri , file ?: File ) : Promise < void > {
800+ public async compile ( uri : vscode . Uri , file ?: File , update ?: boolean ) : Promise < void > {
769801 if ( ! uri || uri . scheme != FILESYSTEM_SCHEMA ) return ;
770802 uri = redirectDotvscodeRoot ( uri ) ;
771803 const compileList : string [ ] = [ ] ;
@@ -792,7 +824,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
792824 if ( ! compileList . length ) return ;
793825 const api = new AtelierAPI ( uri ) ;
794826 const conf = vscode . workspace . getConfiguration ( "objectscript" ) ;
795- const filesToUpdate : Set < string > = new Set ( compileList ) ;
827+ const filesToUpdate : string [ ] = [ ] ;
796828 // Compile the files
797829 await vscode . window . withProgress (
798830 {
@@ -810,34 +842,41 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
810842 } else if ( ! conf . get ( "suppressCompileMessages" ) ) {
811843 vscode . window . showInformationMessage ( `${ info } Compilation succeeded.` , "Dismiss" ) ;
812844 }
813- data . result . content . forEach ( ( f ) => filesToUpdate . add ( f . name ) ) ;
845+ data . result . content . forEach ( ( f ) => filesToUpdate . push ( f . name ) ) ;
814846 } )
815847 . catch ( ( ) => compileErrorMsg ( conf ) )
816848 ) ;
817- // Fire file changed events for all files affected by compilation, including "other" files
849+ if ( update && ! filesToUpdate . includes ( compileList [ 0 ] ) ) {
850+ // This file was just written, the write may have changed its contents, and the compilation
851+ // did not change the contents further. Therefore, we must force VS Code to update it.
852+ this . _notifyOfFileChange ( uri ) ;
853+ }
854+ // Fire file changed events for all files changed by compilation
818855 this . _fireSoon (
819- ...filesToUpdate . values ( ) . map ( ( f ) => {
856+ ...filesToUpdate . map ( ( f ) => {
820857 return {
821858 type : vscode . FileChangeType . Changed ,
822859 uri : DocumentContentProvider . getUri ( f , undefined , undefined , undefined , uri ) ,
823860 } ;
824861 } )
825862 ) ;
826- (
827- await api
828- . actionIndex ( Array . from ( filesToUpdate ) )
829- . then ( ( data ) => data . result . content . flatMap ( ( idx ) => ( ! idx . status . length ? idx . others : [ ] ) ) )
830- . catch ( ( ) => {
831- // Index API returned an error. This should never happen.
832- return [ ] ;
833- } )
834- ) . forEach (
835- ( f ) =>
836- ! filesToUpdate . has ( f ) &&
837- this . _fireSoon ( {
863+ // Fire file changed events for the "other" documents related to
864+ // the files that were compiled, or the files changed by compilation
865+ this . _fireSoon (
866+ ...(
867+ await api
868+ . actionIndex ( Array . from ( new Set ( ...compileList . concat ( filesToUpdate ) ) ) )
869+ . then ( ( data ) => data . result . content . flatMap ( ( idx ) => ( ! idx . status . length ? idx . others : [ ] ) ) )
870+ . catch ( ( ) => {
871+ // Index API returned an error. This should never happen.
872+ return [ ] ;
873+ } )
874+ ) . map ( ( f : string ) => {
875+ return {
838876 type : vscode . FileChangeType . Changed ,
839877 uri : DocumentContentProvider . getUri ( f , undefined , undefined , undefined , uri ) ,
840- } )
878+ } ;
879+ } )
841880 ) ;
842881 }
843882
0 commit comments