Skip to content

Commit 23afff3

Browse files
authored
Update the VS Code editor UI if a server-side document was changed during save (#1573)
1 parent 61ced17 commit 23afff3

File tree

1 file changed

+61
-22
lines changed

1 file changed

+61
-22
lines changed

src/providers/FileSystemProvider/FileSystemProvider.ts

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)