Skip to content

Commit 590b138

Browse files
committed
server hook
1 parent efda009 commit 590b138

File tree

3 files changed

+141
-98
lines changed

3 files changed

+141
-98
lines changed

src/api/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,19 @@ export class AtelierAPI {
674674
name: string,
675675
data: { enc: boolean; content: string[]; mtime: number },
676676
ignoreConflict?: boolean
677-
): Promise<Atelier.Response> {
677+
): Promise<
678+
Atelier.Response<{
679+
name: string;
680+
db: string;
681+
ts: string;
682+
cat: "CLS" | "CSP" | "RTN" | "OTH";
683+
status: string;
684+
enc: boolean;
685+
flag?: "1";
686+
content: string[];
687+
ext: any;
688+
}>
689+
> {
678690
const params = { ignoreConflict };
679691
name = this.transformNameIfCsp(name);
680692
const headers = {};

src/commands/compile.ts

Lines changed: 114 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export async function checkChangedOnServer(file: CurrentTextFile | CurrentBinary
8282
return mtime;
8383
}
8484

85+
// Synchronize the client version and the server version of the same file
8586
export async function importFile(
8687
file: CurrentTextFile | CurrentBinaryFile,
8788
ignoreConflict?: boolean,
@@ -116,78 +117,83 @@ export async function importFile(
116117
mtime < 0 ||
117118
(notIsfs(file.uri) &&
118119
vscode.workspace.getConfiguration("objectscript", file.uri).get<boolean>("overwriteServerChanges"));
119-
return api
120-
.putDoc(
120+
try {
121+
const data = await api.putDoc(
121122
file.name,
122123
{
123124
content,
124125
enc,
125126
mtime,
126127
},
127128
ignoreConflict
128-
)
129-
.then((data) => {
130-
// Update cache entry
131-
workspaceState.update(`${file.uniqueId}:mtime`, Number(new Date(data.result.ts + "Z")));
132-
133-
// In case another extension has used an 'objectscript://' uri to load a document read-only from the server,
134-
// make it reload with what we just imported to the server.
135-
const serverUri = DocumentContentProvider.getUri(
136-
file.name,
137-
file.workspaceFolder,
138-
undefined,
139-
false,
140-
undefined,
141-
true
129+
);
130+
workspaceState.update(`${file.uniqueId}:mtime`, Number(new Date(data.result.ts + "Z")));
131+
if (data.result.flag === "1") {
132+
// putDoc returns new Storage definitions and the file must be a CLS
133+
let content: string[];
134+
(content = new TextDecoder("utf-8").decode(await vscode.workspace.fs.readFile(file.uri)).split(/\r?\n/g)),
135+
(content = updateStorage(content, data.result.content));
136+
await vscode.workspace.fs.writeFile(
137+
file.uri,
138+
new TextEncoder().encode(
139+
content.join(((<CurrentTextFile>file)?.eol ?? vscode.EndOfLine.LF) == vscode.EndOfLine.CRLF ? "\r\n" : "\n")
140+
)
142141
);
143-
documentContentProvider.update(serverUri.with({ scheme: OBJECTSCRIPT_FILE_SCHEMA }));
144-
})
145-
.catch((error) => {
146-
if (error?.statusCode == 409) {
147-
const choices: string[] = [];
148-
if (!enc) {
149-
choices.push("Compare");
150-
}
151-
choices.push("Overwrite on Server", "Pull Server Changes", "Cancel");
152-
return vscode.window
153-
.showErrorMessage(
154-
`Failed to import '${file.name}': The version of the file on the server is newer.
142+
}
143+
// In case another extension has used an 'objectscript://' uri to load a document read-only from the server,
144+
// make it reload with what we just imported to the server.
145+
const serverUri = DocumentContentProvider.getUri(
146+
file.name,
147+
file.workspaceFolder,
148+
undefined,
149+
false,
150+
undefined,
151+
true
152+
);
153+
documentContentProvider.update(serverUri.with({ scheme: OBJECTSCRIPT_FILE_SCHEMA }));
154+
return;
155+
} catch (error) {
156+
if (error?.statusCode == 409) {
157+
const choices: string[] = [];
158+
if (!enc) {
159+
choices.push("Compare");
160+
}
161+
choices.push("Overwrite on Server", "Pull Server Changes", "Cancel");
162+
const action = await vscode.window.showErrorMessage(
163+
`Failed to import '${file.name}': The version of the file on the server is newer.
155164
What do you want to do?`,
156-
...choices
157-
)
158-
.then((action) => {
159-
switch (action) {
160-
case "Compare":
161-
return vscode.commands
162-
.executeCommand(
163-
"vscode.diff",
164-
vscode.Uri.file(file.name).with({
165-
scheme: OBJECTSCRIPT_FILE_SCHEMA,
166-
authority: file.workspaceFolder,
167-
query: file.name.includes("/") ? "csp" : "",
168-
}),
169-
file.uri,
170-
`Server • ${file.name} ↔ Local • ${file.fileName}`
171-
)
172-
.then(() => Promise.reject());
173-
case "Overwrite on Server":
174-
// Clear cache entry
175-
workspaceState.update(`${file.uniqueId}:mtime`, undefined);
176-
// Overwrite
177-
return importFile(file, true, true);
178-
case "Pull Server Changes":
179-
loadChanges([file]);
180-
return Promise.reject();
181-
case "Cancel":
182-
return Promise.reject();
183-
}
184-
return Promise.reject();
185-
});
186-
} else {
187-
handleError(error, `Failed to save file '${file.name}' on the server.`);
188-
return Promise.reject();
165+
...choices
166+
);
167+
switch (action) {
168+
case "Compare":
169+
await vscode.commands.executeCommand(
170+
"vscode.diff",
171+
vscode.Uri.file(file.name).with({
172+
scheme: OBJECTSCRIPT_FILE_SCHEMA,
173+
authority: file.workspaceFolder,
174+
query: file.name.includes("/") ? "csp" : "",
175+
}),
176+
file.uri,
177+
`Server • ${file.name} ↔ Local • ${file.fileName}`
178+
);
179+
return Promise.reject();
180+
case "Overwrite on Server":
181+
// Clear cache entry
182+
workspaceState.update(`${file.uniqueId}:mtime`, undefined);
183+
// Overwrite
184+
return importFile(file, true, true);
185+
case "Pull Server Changes":
186+
loadChanges([file]);
187+
return Promise.reject();
188+
case "Cancel":
189+
return Promise.reject();
189190
}
190-
});
191+
return Promise.reject();
192+
} else {
193+
handleError(error, `Failed to save file '${file.name}' on the server.`);
194+
return Promise.reject();
195+
}
196+
}
191197
}
192198

193199
function updateOthers(others: string[], baseUri: vscode.Uri) {
@@ -231,29 +237,9 @@ export async function loadChanges(files: (CurrentTextFile | CurrentBinaryFile)[]
231237
} else {
232238
// Insert/update the storage part of class definition.
233239
content = new TextDecoder("utf-8").decode(await vscode.workspace.fs.readFile(file.uri)).split(/\r?\n/g);
234-
let storageBegin: number; // the last "Storage ..." line
235-
let storageEnd: number; // the first "}" after storageBegin
236-
let classEnd: number; // the last "}"
237-
for (let i = 0; i < content.length; i++) {
238-
if (content[i].startsWith("Storage ")) {
239-
storageBegin = i;
240-
} else if (storageBegin !== undefined && storageEnd === undefined && content[i].startsWith("}")) {
241-
storageEnd = i;
242-
} else if (content[i].startsWith("}")) {
243-
classEnd = i;
244-
}
245-
}
246240
let storage = (await api.getDoc(file.name, file.uri, undefined, true)).result.content;
247241
storage = Buffer.isBuffer(storage) ? new TextDecoder().decode(storage).split(/\r?\n/g) : storage;
248-
if ((storageBegin && storageEnd) !== undefined) {
249-
// when replacing an existing storage definition, we don't need extra empty lines (if any).
250-
while (storage[storage.length - 1] == "") {
251-
storage.pop();
252-
}
253-
content.splice(storageBegin, 1 + storageEnd - storageBegin, ...storage);
254-
} else {
255-
content.splice(classEnd, 0, ...storage);
256-
}
242+
content = updateStorage(content, storage);
257243
}
258244
exportedUris.add(file.uri.toString()); // Set optimistically
259245
await vscode.workspace.fs
@@ -286,6 +272,51 @@ export async function loadChanges(files: (CurrentTextFile | CurrentBinaryFile)[]
286272
);
287273
}
288274

275+
function updateStorage(content: string[], storage: string[]): string[] {
276+
const storageMap = storageToMap(storage);
277+
let contentString = content.join("\n");
278+
contentString = contentString
279+
// update existing Storages
280+
.replaceAll(/\n(\s*storage\s+(\w+)\s*{\s*)([^}]*?)(\s*})/gim, (_match, beforeXML, name, _oldXML, afterXML) => {
281+
const newXML = storageMap.get(name);
282+
if (newXML === undefined) {
283+
return "";
284+
}
285+
storageMap.delete(name);
286+
return "\n" + beforeXML + newXML + afterXML;
287+
});
288+
contentString = contentString
289+
// insert remaining Storages
290+
.replace(/}\s*$/m, (m) => {
291+
for (const [name, content] of storageMap.entries()) {
292+
m = `Storage ${name}\n{\n${content}\n}\n\n${m}`;
293+
}
294+
return m;
295+
});
296+
return contentString.split("\n");
297+
}
298+
299+
function storageToMap(storage: string[]): Map<string, string> {
300+
const map: Map<string, string> = new Map();
301+
let k: string;
302+
let v = [];
303+
for (const line of storage) {
304+
if (line.startsWith("Storage ")) {
305+
k = line.slice("Storage ".length, line.length);
306+
v = [];
307+
} else if (line === "{") {
308+
continue;
309+
} else if (line === "}") {
310+
map.set(k, v.join("\n"));
311+
} else if (line === "") {
312+
continue;
313+
} else {
314+
v.push(line);
315+
}
316+
}
317+
return map;
318+
}
319+
289320
export async function compile(docs: (CurrentTextFile | CurrentBinaryFile)[], askFlags = false): Promise<any> {
290321
docs = docs.filter(notNull);
291322
if (!docs.length) return;

src/utils/documentIndex.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -262,22 +262,22 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr
262262
if (!sync || (!change.addedOrChanged && !change.removed)) return;
263263
if (change.addedOrChanged) {
264264
// Create or update the document on the server
265-
importFile(change.addedOrChanged)
266-
.then(() => {
267-
outputImport(change.addedOrChanged.name, uri);
268-
if (conf.get("compileOnSave") && isCompilable(change.addedOrChanged.name)) {
269-
// Compile right away if this document is in the active text editor.
270-
// This is needed to avoid noticeable latency when a user is editing
271-
// a client-side file, saves it, and the auto-compile kicks in.
272-
if (vscodeChange && vscode.window.activeTextEditor?.document.uri.toString() == uriString) {
273-
compile([change.addedOrChanged]);
274-
} else {
275-
debouncedCompile(change.addedOrChanged);
276-
}
265+
try {
266+
await importFile(change.addedOrChanged);
267+
outputImport(change.addedOrChanged.name, uri);
268+
if (conf.get("compileOnSave") && isCompilable(change.addedOrChanged.name)) {
269+
// Compile right away if this document is in the active text editor.
270+
// This is needed to avoid noticeable latency when a user is editing
271+
// a client-side file, saves it, and the auto-compile kicks in.
272+
if (vscodeChange && vscode.window.activeTextEditor?.document.uri.toString() == uriString) {
273+
compile([change.addedOrChanged]);
274+
} else {
275+
debouncedCompile(change.addedOrChanged);
277276
}
278-
})
277+
}
278+
} catch (_) {
279279
// importFile handles any server errors
280-
.catch(() => {});
280+
}
281281
}
282282
if (change.removed) {
283283
// Delete document on the server

0 commit comments

Comments
 (0)