Skip to content

Commit dab9d5c

Browse files
committed
Only reload contents of client-side file after compilation if compilation changed the file
1 parent 039d8d2 commit dab9d5c

File tree

3 files changed

+91
-70
lines changed

3 files changed

+91
-70
lines changed

src/commands/compile.ts

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import {
1717
classNameRegex,
1818
compileErrorMsg,
1919
cspAppsForUri,
20-
CurrentBinaryFile,
2120
currentFile,
2221
currentFileFromContent,
2322
CurrentTextFile,
23+
EitherCurrentFile,
2424
exportedUris,
2525
getWsFolder,
2626
handleError,
@@ -57,7 +57,7 @@ async function compileFlags(): Promise<string> {
5757
* @param force If passed true, use server mtime.
5858
* @return mtime timestamp or -1.
5959
*/
60-
export async function checkChangedOnServer(file: CurrentTextFile | CurrentBinaryFile, force = false): Promise<number> {
60+
export async function checkChangedOnServer(file: EitherCurrentFile, force = false): Promise<number> {
6161
if (!file || !file.uri || schemas.includes(file.uri.scheme)) {
6262
return -1;
6363
}
@@ -88,7 +88,7 @@ export async function checkChangedOnServer(file: CurrentTextFile | CurrentBinary
8888
}
8989

9090
export async function importFile(
91-
file: CurrentTextFile | CurrentBinaryFile,
91+
file: EitherCurrentFile,
9292
ignoreConflict?: boolean,
9393
skipDeplCheck = false
9494
): Promise<any> {
@@ -213,61 +213,79 @@ function updateOthers(others: string[], baseUri: vscode.Uri) {
213213
});
214214
}
215215

216-
export async function loadChanges(files: (CurrentTextFile | CurrentBinaryFile)[]): Promise<any> {
217-
if (!files.length) {
218-
return;
219-
}
216+
/**
217+
* Reload the contents of `files` from the server. This will also a trigger a
218+
* refresh of in-memory copies of "other" documents related to `files`. Files
219+
* in the `onlyUpdateOthersFiles` array will not have their contents reloaded.
220+
* Only their "other" documents will be refreshed.
221+
*/
222+
export async function loadChanges(
223+
files: EitherCurrentFile[],
224+
onlyUpdateOthersFiles: EitherCurrentFile[] = []
225+
): Promise<void> {
226+
if (!files?.length) return;
220227
const api = new AtelierAPI(files[0].uri);
221228
// Use allSettled so we attempt to load changes for all files, even if some fail
222-
return api.actionIndex(files.map((f) => f.name)).then((data) =>
223-
Promise.allSettled(
224-
data.result.content.map(async (doc) => {
225-
if (doc.status.length) return;
226-
const file = files.find((f) => f.name == doc.name);
227-
const mtime = Number(new Date(doc.ts + "Z"));
228-
workspaceState.update(`${file.uniqueId}:mtime`, mtime > 0 ? mtime : undefined);
229-
if (notIsfs(file.uri)) {
230-
const content = await api.getDoc(file.name, file.uri).then((data) => data.result.content);
231-
exportedUris.add(file.uri.toString()); // Set optimistically
232-
await vscode.workspace.fs
233-
.writeFile(
234-
file.uri,
235-
Buffer.isBuffer(content)
236-
? content
237-
: new TextEncoder().encode(
238-
content.join(
239-
((<CurrentTextFile>file)?.eol ?? vscode.EndOfLine.LF) == vscode.EndOfLine.CRLF ? "\r\n" : "\n"
240-
)
241-
)
242-
)
243-
.then(undefined, (e) => {
244-
// Save failed, so remove this URI from the set
245-
exportedUris.delete(file.uri.toString());
246-
// Re-throw the error
247-
throw e;
248-
});
249-
if (isClassOrRtn(file.uri)) {
250-
// Update the document index
251-
updateIndexForDocument(file.uri, undefined, undefined, content);
229+
await api
230+
.actionIndex(Array.from(new Set(files.map((f) => f.name).concat(onlyUpdateOthersFiles.map((f) => f.name)))))
231+
.then((data) =>
232+
Promise.allSettled(
233+
data.result.content.map(async (doc) => {
234+
if (doc.status.length) return;
235+
let file = files.find((f) => f.name == doc.name);
236+
if (file) {
237+
// This is a file that requires a content reload
238+
if (notIsfs(file.uri)) {
239+
const mtime = Number(new Date(doc.ts + "Z"));
240+
workspaceState.update(`${file.uniqueId}:mtime`, mtime > 0 ? mtime : undefined);
241+
const content = await api.getDoc(file.name, file.uri).then((data) => data.result.content);
242+
exportedUris.add(file.uri.toString()); // Set optimistically
243+
await vscode.workspace.fs
244+
.writeFile(
245+
file.uri,
246+
Buffer.isBuffer(content)
247+
? content
248+
: new TextEncoder().encode(
249+
content.join(
250+
((<CurrentTextFile>file)?.eol ?? vscode.EndOfLine.LF) == vscode.EndOfLine.CRLF ? "\r\n" : "\n"
251+
)
252+
)
253+
)
254+
.then(undefined, (e) => {
255+
// Save failed, so remove this URI from the set
256+
exportedUris.delete(file.uri.toString());
257+
// Re-throw the error
258+
throw e;
259+
});
260+
if (isClassOrRtn(file.uri)) {
261+
// Update the document index
262+
updateIndexForDocument(file.uri, undefined, undefined, content);
263+
}
264+
} else {
265+
fileSystemProvider.fireFileChanged(file.uri);
266+
}
267+
} else {
268+
// The contents of this file did not change, but its "other" documents still need to be updated
269+
file = onlyUpdateOthersFiles.find((f) => f.name == doc.name);
270+
if (!file) return;
252271
}
253-
} else if (filesystemSchemas.includes(file.uri.scheme)) {
254-
fileSystemProvider.fireFileChanged(file.uri);
255-
}
256-
updateOthers(doc.others, file.uri);
257-
})
258-
)
259-
);
272+
updateOthers(doc.others, file.uri);
273+
})
274+
)
275+
);
260276
}
261277

262-
export async function compile(docs: (CurrentTextFile | CurrentBinaryFile)[], flags?: string): Promise<any> {
278+
export async function compile(docs: EitherCurrentFile[], flags?: string): Promise<any> {
263279
const wsFolder = vscode.workspace.getWorkspaceFolder(docs[0].uri);
264280
const conf = vscode.workspace.getConfiguration("objectscript", wsFolder || docs[0].uri);
265281
flags = flags || conf.get("compileFlags");
266282
const api = new AtelierAPI(docs[0].uri);
267283
const docNames = docs.map((d) => d.name);
268-
// Determine the line ending to use for other documents affected
284+
// Determine the line ending to use for documents affected
269285
// by compilation so we don't need to read their contents
270286
const eol = (<CurrentTextFile>docs.find((d) => (<CurrentTextFile>d)?.eol))?.eol ?? vscode.EndOfLine.LF;
287+
const docsToReload: EitherCurrentFile[] = [];
288+
const docsToRefreshOthers: EitherCurrentFile[] = [...docs];
271289
return vscode.window
272290
.withProgress(
273291
{
@@ -285,13 +303,18 @@ export async function compile(docs: (CurrentTextFile | CurrentBinaryFile)[], fla
285303
} else if (!conf.get("suppressCompileMessages")) {
286304
vscode.window.showInformationMessage(`${info}Compilation succeeded.`, "Dismiss");
287305
}
288-
if (wsFolder) {
289-
// Make sure that we update the content for any
290-
// other documents affected by this compilation
291-
data.result.content.forEach((f) => {
292-
if (docNames.includes(f.name)) return;
306+
data.result.content.forEach((f) => {
307+
// Reload the contents of files that were changed by compilation
308+
if (docNames.includes(f.name)) {
309+
docsToReload.push(
310+
...docsToRefreshOthers.splice(
311+
docsToRefreshOthers.findIndex((d) => d.name == f.name),
312+
1
313+
)
314+
);
315+
} else if (wsFolder) {
293316
getUrisForDocument(f.name, wsFolder).forEach((u) => {
294-
docs.push({
317+
docsToReload.push({
295318
name: f.name,
296319
uri: u,
297320
uniqueId: `${wsFolder.name}:${f.name}`,
@@ -302,17 +325,14 @@ export async function compile(docs: (CurrentTextFile | CurrentBinaryFile)[], fla
302325
content: "",
303326
});
304327
});
305-
});
306-
}
307-
return docs;
308-
})
309-
.catch(() => {
310-
compileErrorMsg(conf);
311-
// Always fetch server changes, even when compile failed or got cancelled
312-
return docs;
328+
}
329+
});
313330
})
331+
.catch(() => compileErrorMsg(conf))
314332
)
315-
.then(loadChanges);
333+
.then(() => {
334+
return loadChanges(docsToReload, docsToRefreshOthers);
335+
});
316336
}
317337

318338
export async function importAndCompile(
@@ -429,7 +449,7 @@ export async function namespaceCompile(askFlags = false): Promise<any> {
429449
}
430450

431451
async function importFiles(files: vscode.Uri[], noCompile = false) {
432-
const toCompile: (CurrentTextFile | CurrentBinaryFile)[] = [];
452+
const toCompile: EitherCurrentFile[] = [];
433453
const rateLimiter = new RateLimiter(50);
434454
await Promise.allSettled<void>(
435455
files.map((uri) =>

src/utils/documentIndex.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as vscode from "vscode";
22
import {
3-
CurrentBinaryFile,
4-
CurrentTextFile,
3+
EitherCurrentFile,
54
RateLimiter,
65
currentFileFromContent,
76
exportedUris,
@@ -27,7 +26,7 @@ interface WSFolderIndex {
2726

2827
interface WSFolderIndexChange {
2928
/** InterSystems document added to the index or changed on disk, if any */
30-
addedOrChanged?: CurrentTextFile | CurrentBinaryFile;
29+
addedOrChanged?: EitherCurrentFile;
3130
/** InterSystems document removed from the index, if any */
3231
removed?: string;
3332
}
@@ -49,7 +48,7 @@ async function getCurrentFile(
4948
uri: vscode.Uri,
5049
forceText = false,
5150
content?: string[] | Buffer
52-
): Promise<CurrentTextFile | CurrentBinaryFile | undefined> {
51+
): Promise<EitherCurrentFile | undefined> {
5352
if (content) {
5453
// forceText is always true when content is passed
5554
return currentFileFromContent(uri, Buffer.isBuffer(content) ? textDecoder.decode(content) : content.join("\n"));
@@ -76,11 +75,11 @@ async function getCurrentFile(
7675
}
7776

7877
/** Generate a debounced compile function */
79-
function generateCompileFn(): (doc: CurrentTextFile | CurrentBinaryFile) => void {
78+
function generateCompileFn(): (doc: EitherCurrentFile) => void {
8079
let timeout: NodeJS.Timeout;
81-
const docs: (CurrentTextFile | CurrentBinaryFile)[] = [];
80+
const docs: EitherCurrentFile[] = [];
8281

83-
return (doc: CurrentTextFile | CurrentBinaryFile): void => {
82+
return (doc: EitherCurrentFile): void => {
8483
docs.push(doc);
8584

8685
// Clear the previous timeout to reset the debounce timer

src/utils/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ export interface CurrentBinaryFile extends CurrentFile {
121121
content: Buffer;
122122
}
123123

124+
export type EitherCurrentFile = CurrentTextFile | CurrentBinaryFile;
125+
124126
/**
125127
* For workspace roots in the local filesystem, configName is the root's name
126128
* which defaults to the folder name, and apiTarget is the same.
@@ -216,7 +218,7 @@ export const classNameRegex = /^[ \t]*Class[ \t]+(%?[\p{L}\d\u{100}-\u{ffff}]+(?
216218
/** A regex for extracting the name and type of a routine from its content */
217219
export const routineNameTypeRegex = /^ROUTINE ([^\s]+)(?:\s*\[\s*Type\s*=\s*\b([a-z]{3})\b)?/i;
218220

219-
export function currentFileFromContent(uri: vscode.Uri, content: string | Buffer): CurrentTextFile | CurrentBinaryFile {
221+
export function currentFileFromContent(uri: vscode.Uri, content: string | Buffer): EitherCurrentFile {
220222
const fileName = uri.fsPath;
221223
const workspaceFolder = workspaceFolderOfUri(uri);
222224
if (!workspaceFolder) {

0 commit comments

Comments
 (0)