Skip to content

Commit f2a556a

Browse files
committed
Update the VS Code editor UI if a server-side document was changed during save
1 parent a4e89ee commit f2a556a

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
@@ -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

Comments
 (0)