@@ -446,13 +446,15 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
446446 }
447447 const api = new AtelierAPI ( uri ) ;
448448 let created = false ;
449+ let update = false ;
450+ const isCls = ! csp && fileName . split ( "." ) . pop ( ) . toLowerCase ( ) == "cls" ;
449451 // Use _lookup() instead of _lookupAsFile() so we send
450452 // our cached mtime with the GET /doc request if we have it
451453 return this . _lookup ( uri )
452454 . then (
453455 async ( entry : File ) => {
454456 // Check cases for which we should fail the write and leave the document dirty if changed
455- if ( ! csp && fileName . split ( "." ) . pop ( ) . toLowerCase ( ) == "cls" ) {
457+ if ( isCls ) {
456458 // Check if the class name and file name match
457459 let clsname = "" ;
458460 const match = new TextDecoder ( ) . decode ( content ) . match ( classNameRegex ) ;
@@ -496,7 +498,10 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
496498 } ,
497499 true
498500 )
499- . then ( ( ) => entry )
501+ . then ( ( data ) => {
502+ update = isCls || data . result . content . length > 0 ;
503+ return entry ;
504+ } )
500505 . catch ( ( error ) => {
501506 // Throw all failures
502507 throw vscode . FileSystemError . Unavailable ( stringifyError ( error ) || uri ) ;
@@ -538,25 +543,52 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
538543 // Create an entry in our cache for the document
539544 return this . _lookupAsFile ( uri ) . then ( ( entry ) => {
540545 created = true ;
546+ update = isCls || data . result . content . length > 0 ;
541547 this . _fireSoon ( { type : vscode . FileChangeType . Created , uri } ) ;
542548 return entry ;
543549 } ) ;
544550 } ) ;
545551 }
546552 )
547553 . then ( ( entry ) => {
554+ if ( ! entry ) return ; // entry is only empty when uri is open in a low-code editor
548555 // Compile the document if required
549556 if (
550557 ! uri . path . includes ( "/_vscode/" ) &&
551558 vscode . workspace . getConfiguration ( "objectscript" , uri ) . get ( "compileOnSave" )
552559 ) {
553- this . compile ( uri , entry ) ;
560+ this . compile ( uri , entry , update ) ;
561+ } else if ( update ) {
562+ // The file's contents may have changed as a result of the save,
563+ // so make sure we notify VS Code and any watchers of the change
564+ this . _notifyOfFileChange ( uri ) ;
554565 } else if ( ! created ) {
555566 this . _fireSoon ( { type : vscode . FileChangeType . Changed , uri } ) ;
556567 }
557568 } ) ;
558569 }
559570
571+ /**
572+ * Notify VS Code and any watchers that the contents of `uri` changed.
573+ * Use this function instead of firing the file change event directly
574+ * when we need to force the VS Code UI to show the change. For example,
575+ * if the server changed the document during a save.
576+ */
577+ private _notifyOfFileChange ( uri : vscode . Uri ) : void {
578+ // The file's contents may have changed as a result of the save,
579+ // so make sure we notify VS Code and any watchers of the change
580+ const uriString = uri . toString ( ) ;
581+ if ( vscode . window . activeTextEditor ?. document . uri . toString ( ) == uriString ) {
582+ setTimeout ( ( ) => {
583+ const activeDoc = vscode . window . activeTextEditor ?. document ;
584+ if ( activeDoc && ! activeDoc . isDirty && ! activeDoc . isClosed && activeDoc . uri . toString ( ) == uriString ) {
585+ // Force VS Code to refresh the file's contents in the editor UI
586+ vscode . commands . executeCommand ( "workbench.action.files.revert" ) ;
587+ }
588+ } , 25 ) ;
589+ }
590+ }
591+
560592 /** Process a Document object that was successfully deleted. */
561593 private async processDeletedDoc ( doc : Document , uri : vscode . Uri , csp : boolean , project : boolean ) : Promise < void > {
562594 const events : vscode . FileChangeEvent [ ] = [ ] ;
@@ -768,9 +800,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
768800 /**
769801 * If `uri` is a file, compile it.
770802 * If `uri` is a directory, compile its contents.
771- * `file` is passed if called from `writeFile()`.
803+ * `file` and `update` are passed if called from `writeFile()`.
772804 */
773- public async compile ( uri : vscode . Uri , file ?: File ) : Promise < void > {
805+ public async compile ( uri : vscode . Uri , file ?: File , update ?: boolean ) : Promise < void > {
774806 if ( ! uri || uri . scheme != FILESYSTEM_SCHEMA ) return ;
775807 uri = redirectDotvscodeRoot ( uri ) ;
776808 const compileList : string [ ] = [ ] ;
@@ -797,7 +829,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
797829 if ( ! compileList . length ) return ;
798830 const api = new AtelierAPI ( uri ) ;
799831 const conf = vscode . workspace . getConfiguration ( "objectscript" ) ;
800- const filesToUpdate : Set < string > = new Set ( compileList ) ;
832+ const filesToUpdate : string [ ] = [ ] ;
801833 // Compile the files
802834 await vscode . window . withProgress (
803835 {
@@ -815,34 +847,41 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
815847 } else if ( ! conf . get ( "suppressCompileMessages" ) ) {
816848 vscode . window . showInformationMessage ( `${ info } Compilation succeeded.` , "Dismiss" ) ;
817849 }
818- data . result . content . forEach ( ( f ) => filesToUpdate . add ( f . name ) ) ;
850+ data . result . content . forEach ( ( f ) => filesToUpdate . push ( f . name ) ) ;
819851 } )
820852 . catch ( ( ) => compileErrorMsg ( conf ) )
821853 ) ;
822- // Fire file changed events for all files affected by compilation, including "other" files
854+ if ( update && ! filesToUpdate . includes ( compileList [ 0 ] ) ) {
855+ // This file was just written, the write may have changed its contents, and the compilation
856+ // did not change the contents further. Therefore, we must force VS Code to update it.
857+ this . _notifyOfFileChange ( uri ) ;
858+ }
859+ // Fire file changed events for all files changed by compilation
823860 this . _fireSoon (
824- ...filesToUpdate . values ( ) . map ( ( f ) => {
861+ ...filesToUpdate . map ( ( f ) => {
825862 return {
826863 type : vscode . FileChangeType . Changed ,
827864 uri : DocumentContentProvider . getUri ( f , undefined , undefined , undefined , uri ) ,
828865 } ;
829866 } )
830867 ) ;
831- (
832- await api
833- . actionIndex ( Array . from ( filesToUpdate ) )
834- . then ( ( data ) => data . result . content . flatMap ( ( idx ) => ( ! idx . status . length ? idx . others : [ ] ) ) )
835- . catch ( ( ) => {
836- // Index API returned an error. This should never happen.
837- return [ ] ;
838- } )
839- ) . forEach (
840- ( f ) =>
841- ! filesToUpdate . has ( f ) &&
842- this . _fireSoon ( {
868+ // Fire file changed events for the "other" documents related to
869+ // the files that were compiled, or the files changed by compilation
870+ this . _fireSoon (
871+ ...(
872+ await api
873+ . actionIndex ( Array . from ( new Set ( ...compileList . concat ( filesToUpdate ) ) ) )
874+ . then ( ( data ) => data . result . content . flatMap ( ( idx ) => ( ! idx . status . length ? idx . others : [ ] ) ) )
875+ . catch ( ( ) => {
876+ // Index API returned an error. This should never happen.
877+ return [ ] ;
878+ } )
879+ ) . map ( ( f : string ) => {
880+ return {
843881 type : vscode . FileChangeType . Changed ,
844882 uri : DocumentContentProvider . getUri ( f , undefined , undefined , undefined , uri ) ,
845- } )
883+ } ;
884+ } )
846885 ) ;
847886 }
848887
0 commit comments