@@ -441,13 +441,15 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
441
441
}
442
442
const api = new AtelierAPI ( uri ) ;
443
443
let created = false ;
444
+ let update = false ;
445
+ const isCls = ! csp && fileName . split ( "." ) . pop ( ) . toLowerCase ( ) == "cls" ;
444
446
// Use _lookup() instead of _lookupAsFile() so we send
445
447
// our cached mtime with the GET /doc request if we have it
446
448
return this . _lookup ( uri )
447
449
. then (
448
450
async ( entry : File ) => {
449
451
// 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 ) {
451
453
// Check if the class name and file name match
452
454
let clsname = "" ;
453
455
const match = new TextDecoder ( ) . decode ( content ) . match ( classNameRegex ) ;
@@ -491,7 +493,10 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
491
493
} ,
492
494
true
493
495
)
494
- . then ( ( ) => entry )
496
+ . then ( ( data ) => {
497
+ update = isCls || data . result . content . length > 0 ;
498
+ return entry ;
499
+ } )
495
500
. catch ( ( error ) => {
496
501
// Throw all failures
497
502
throw vscode . FileSystemError . Unavailable ( stringifyError ( error ) || uri ) ;
@@ -533,25 +538,52 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
533
538
// Create an entry in our cache for the document
534
539
return this . _lookupAsFile ( uri ) . then ( ( entry ) => {
535
540
created = true ;
541
+ update = isCls || data . result . content . length > 0 ;
536
542
this . _fireSoon ( { type : vscode . FileChangeType . Created , uri } ) ;
537
543
return entry ;
538
544
} ) ;
539
545
} ) ;
540
546
}
541
547
)
542
548
. then ( ( entry ) => {
549
+ if ( ! entry ) return ; // entry is only empty when uri is open in a low-code editor
543
550
// Compile the document if required
544
551
if (
545
552
! uri . path . includes ( "/_vscode/" ) &&
546
553
vscode . workspace . getConfiguration ( "objectscript" , uri ) . get ( "compileOnSave" )
547
554
) {
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 ) ;
549
560
} else if ( ! created ) {
550
561
this . _fireSoon ( { type : vscode . FileChangeType . Changed , uri } ) ;
551
562
}
552
563
} ) ;
553
564
}
554
565
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
+
555
587
/** Process a Document object that was successfully deleted. */
556
588
private async processDeletedDoc ( doc : Document , uri : vscode . Uri , csp : boolean , project : boolean ) : Promise < void > {
557
589
const events : vscode . FileChangeEvent [ ] = [ ] ;
@@ -763,9 +795,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
763
795
/**
764
796
* If `uri` is a file, compile it.
765
797
* 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()`.
767
799
*/
768
- public async compile ( uri : vscode . Uri , file ?: File ) : Promise < void > {
800
+ public async compile ( uri : vscode . Uri , file ?: File , update ?: boolean ) : Promise < void > {
769
801
if ( ! uri || uri . scheme != FILESYSTEM_SCHEMA ) return ;
770
802
uri = redirectDotvscodeRoot ( uri ) ;
771
803
const compileList : string [ ] = [ ] ;
@@ -792,7 +824,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
792
824
if ( ! compileList . length ) return ;
793
825
const api = new AtelierAPI ( uri ) ;
794
826
const conf = vscode . workspace . getConfiguration ( "objectscript" ) ;
795
- const filesToUpdate : Set < string > = new Set ( compileList ) ;
827
+ const filesToUpdate : string [ ] = [ ] ;
796
828
// Compile the files
797
829
await vscode . window . withProgress (
798
830
{
@@ -810,34 +842,41 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
810
842
} else if ( ! conf . get ( "suppressCompileMessages" ) ) {
811
843
vscode . window . showInformationMessage ( `${ info } Compilation succeeded.` , "Dismiss" ) ;
812
844
}
813
- data . result . content . forEach ( ( f ) => filesToUpdate . add ( f . name ) ) ;
845
+ data . result . content . forEach ( ( f ) => filesToUpdate . push ( f . name ) ) ;
814
846
} )
815
847
. catch ( ( ) => compileErrorMsg ( conf ) )
816
848
) ;
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
818
855
this . _fireSoon (
819
- ...filesToUpdate . values ( ) . map ( ( f ) => {
856
+ ...filesToUpdate . map ( ( f ) => {
820
857
return {
821
858
type : vscode . FileChangeType . Changed ,
822
859
uri : DocumentContentProvider . getUri ( f , undefined , undefined , undefined , uri ) ,
823
860
} ;
824
861
} )
825
862
) ;
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 {
838
876
type : vscode . FileChangeType . Changed ,
839
877
uri : DocumentContentProvider . getUri ( f , undefined , undefined , undefined , uri ) ,
840
- } )
878
+ } ;
879
+ } )
841
880
) ;
842
881
}
843
882
0 commit comments