Skip to content

Commit 810ceed

Browse files
authored
Merge pull request #200 from intersystems-community/respect_server_changes
Respect changes on server
2 parents 49bc685 + 81e29e7 commit 810ceed

File tree

8 files changed

+115
-14
lines changed

8 files changed

+115
-14
lines changed

src/api/atelier.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface Document {
3131
status: string;
3232
enc: boolean;
3333
flags: number;
34-
content: string[];
34+
content: string[] | Buffer;
3535
ext: string;
3636
}
3737

src/api/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ export class AtelierAPI {
365365
}
366366

367367
// api v1+
368-
public getDoc(name: string, format?: string): Promise<Atelier.Response> {
368+
public getDoc(name: string, format?: string): Promise<Atelier.Response<Atelier.Document>> {
369369
let params = {};
370370
if (format) {
371371
params = {
@@ -384,12 +384,16 @@ export class AtelierAPI {
384384
// v1+
385385
public putDoc(
386386
name: string,
387-
data: { enc: boolean; content: string[] },
387+
data: { enc: boolean; content: string[]; mtime: number },
388388
ignoreConflict?: boolean
389389
): Promise<Atelier.Response> {
390390
const params = { ignoreConflict };
391391
name = this.transformNameIfCsp(name);
392-
return this.request(1, "PUT", `${this.ns}/doc/${name}`, data, params);
392+
const headers = {};
393+
if (!ignoreConflict && data.mtime && data.mtime > 0) {
394+
headers["IF_NONE_MATCH"] = new Date(data.mtime).toISOString().replace(/T|Z/g, " ").trim();
395+
}
396+
return this.request(1, "PUT", `${this.ns}/doc/${name}`, data, params, headers);
393397
}
394398

395399
// v1+

src/commands/compile.ts

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
documentContentProvider,
99
FILESYSTEM_SCHEMA,
1010
FILESYSTEM_READONLY_SCHEMA,
11+
OBJECTSCRIPT_FILE_SCHEMA,
1112
fileSystemProvider,
13+
workspaceState,
14+
schemas,
1215
} from "../extension";
1316
import { DocumentContentProvider } from "../providers/DocumentContentProvider";
1417
import { currentFile, CurrentFile, outputChannel } from "../utils";
@@ -25,18 +28,91 @@ async function compileFlags(): Promise<string> {
2528
});
2629
}
2730

28-
async function importFile(file: CurrentFile): Promise<any> {
31+
export async function checkChangedOnServer(file: CurrentFile): Promise<number> {
32+
if (!file || !file.uri || schemas.includes(file.uri.scheme)) {
33+
return -1;
34+
}
35+
const api = new AtelierAPI(file.uri);
36+
const mtime =
37+
workspaceState.get(`${file.uniqueId}:mtime`, null) ||
38+
(await api
39+
.getDoc(file.name)
40+
.then((data) => data.result)
41+
.then(({ ts, content }) => {
42+
const fileContent = file.content.split(/\r?\n/);
43+
const serverTime = Number(new Date(ts + "Z"));
44+
const sameContent = content.every((line, index) => line.trim() == (fileContent[index] || "").trim());
45+
const mtime = sameContent ? serverTime : Math.max(Number(fs.statSync(file.fileName).mtime), serverTime);
46+
return mtime;
47+
})
48+
.catch(() => -1));
49+
workspaceState.update(`${file.uniqueId}:mtime`, mtime > 0 ? mtime : undefined);
50+
return mtime;
51+
}
52+
53+
async function importFile(file: CurrentFile, ignoreConflict?: boolean): Promise<any> {
2954
const api = new AtelierAPI(file.uri);
55+
const content = file.content.split(/\r?\n/);
56+
const mtime = await checkChangedOnServer(file);
57+
ignoreConflict = ignoreConflict || mtime < 0;
3058
return api
3159
.putDoc(
3260
file.name,
3361
{
34-
content: file.content.split(/\r?\n/),
62+
content,
3563
enc: false,
64+
mtime,
3665
},
37-
true
66+
ignoreConflict
3867
)
39-
.catch((error) => vscode.window.showErrorMessage(error.message));
68+
.catch((error) => {
69+
if (error.statusCode == 400) {
70+
outputChannel.appendLine(error.error.result.status);
71+
vscode.window.showErrorMessage(error.error.result.status);
72+
return Promise.reject();
73+
}
74+
if (error.statusCode == 409) {
75+
return vscode.window
76+
.showErrorMessage(
77+
`Failed to import '${file.name}': The version of the file on the server is newer.
78+
What do you want to do?`,
79+
"Compare",
80+
"Overwrite on Server",
81+
"Pull Server Changes",
82+
"Cancel"
83+
)
84+
.then((action) => {
85+
switch (action) {
86+
case "Compare":
87+
return vscode.commands
88+
.executeCommand(
89+
"vscode.diff",
90+
file.uri,
91+
vscode.Uri.file(file.name).with({
92+
scheme: OBJECTSCRIPT_FILE_SCHEMA,
93+
authority: file.workspaceFolder,
94+
}),
95+
`Local • ${file.fileName} ↔ Server • ${file.name}`
96+
)
97+
.then(() => Promise.reject());
98+
case "Overwrite on Server":
99+
return importFile(file, true);
100+
case "Pull Server Changes":
101+
outputChannel.appendLine(`${file.name}: Loading changes from server`);
102+
outputChannel.show(true);
103+
loadChanges([file]);
104+
return Promise.reject();
105+
case "Cancel":
106+
outputChannel.appendLine(`${file.name}: Import and Compile canceled by user`);
107+
outputChannel.show(true);
108+
return Promise.reject();
109+
}
110+
return Promise.reject();
111+
});
112+
}
113+
vscode.window.showErrorMessage(error.message);
114+
return Promise.reject();
115+
});
40116
}
41117

42118
function updateOthers(others: string[]) {
@@ -57,6 +133,8 @@ export async function loadChanges(files: CurrentFile[]): Promise<any> {
57133
.getDoc(file.name)
58134
.then((data) => {
59135
const content = (data.result.content || []).join(file.eol === vscode.EndOfLine.LF ? "\n" : "\r\n");
136+
const mtime = Number(new Date(data.result.ts + "Z"));
137+
workspaceState.update(`${file.uniqueId}:mtime`, mtime > 0 ? mtime : undefined);
60138
if (file.uri.scheme === "file") {
61139
fs.writeFileSync(file.fileName, content);
62140
} else if (file.uri.scheme === FILESYSTEM_SCHEMA || file.uri.scheme === FILESYSTEM_READONLY_SCHEMA) {

src/commands/export.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export async function exportFile(
9898

9999
return promise
100100
.then((res: any) => {
101-
let joinedContent = (content || []).join("\n").toString("utf8");
101+
let joinedContent = content instanceof Buffer ? content.toString("utf-8") : (content || []).join("\n");
102102
let isSkipped = "";
103103

104104
if (res.found) {

src/commands/studio.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ class StudioActions {
352352
const query = "select * from %Atelier_v1_Utils.Extension_GetStatus(?)";
353353
this.api.actionQuery(query, [this.name]).then((statusObj) => {
354354
const docStatus = statusObj.result.content.pop();
355-
if (!docStatus.editable) {
355+
if (docStatus && !docStatus.editable) {
356356
vscode.commands.executeCommand("undo");
357357
this.userAction(actionObject, false, "", "", 1);
358358
}

src/extension.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
importFolder as importFileOrFolder,
2323
namespaceCompile,
2424
compileExplorerItem,
25+
checkChangedOnServer,
2526
} from "./commands/compile";
2627
import { deleteItem } from "./commands/delete";
2728
import { exportAll, exportExplorerItem } from "./commands/export";
@@ -62,7 +63,14 @@ import { ObjectScriptExplorerProvider } from "./explorer/explorer";
6263
import { WorkspaceNode } from "./explorer/models/workspaceNode";
6364
import { FileSystemProvider } from "./providers/FileSystemPovider/FileSystemProvider";
6465
import { WorkspaceSymbolProvider } from "./providers/WorkspaceSymbolProvider";
65-
import { currentWorkspaceFolder, outputChannel, portFromDockerCompose, terminalWithDocker, notNull } from "./utils";
66+
import {
67+
currentWorkspaceFolder,
68+
outputChannel,
69+
portFromDockerCompose,
70+
terminalWithDocker,
71+
notNull,
72+
currentFile,
73+
} from "./utils";
6674
import { ObjectScriptDiagnosticProvider } from "./providers/ObjectScriptDiagnosticProvider";
6775
import { DocumentRangeFormattingEditProvider } from "./providers/DocumentRangeFormattingEditProvider";
6876

@@ -338,7 +346,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
338346
workspace.onDidSaveTextDocument((file) => {
339347
if (schemas.includes(file.uri.scheme) || languages.includes(file.languageId)) {
340348
if (documentBeingProcessed !== file) {
341-
// return vscode.commands.executeCommand("vscode-objectscript.compile");
342349
return importAndCompile(false, file);
343350
}
344351
}
@@ -408,6 +415,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
408415
) {
409416
fireOtherStudioAction(OtherStudioAction.AttemptedEdit, event.document.uri);
410417
}
418+
if (!event.document.isDirty) {
419+
checkChangedOnServer(currentFile(event.document));
420+
}
411421
}),
412422
window.onDidChangeActiveTextEditor((editor) => {
413423
if (editor) {

src/providers/FileSystemPovider/FileSystemProvider.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
122122
{
123123
content: [content.toString("base64")],
124124
enc: true,
125+
mtime: Date.now(),
125126
},
126127
false
127128
);
@@ -134,6 +135,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
134135
{
135136
content: [`Class ${className} {}`],
136137
enc: false,
138+
mtime: Date.now(),
137139
},
138140
false
139141
);
@@ -146,6 +148,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
146148
{
147149
content: [`ROUTINE ${routineName} ${routineType}`],
148150
enc: false,
151+
mtime: Date.now(),
149152
},
150153
false
151154
);
@@ -249,8 +252,8 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
249252
name,
250253
fileName,
251254
ts,
252-
Array.isArray(content) ? content.join("\n").length : content.length,
253-
Array.isArray(content) ? content.join("\n") : content
255+
Array.isArray(content) ? content.join("\n").length : String(content).length,
256+
Array.isArray(content) ? content.join("\n") : String(content)
254257
)
255258
)
256259
.then((entry) =>

src/utils/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface CurrentFile {
2020
content: string;
2121
uri: vscode.Uri;
2222
eol: vscode.EndOfLine;
23+
workspaceFolder: string;
24+
uniqueId: string;
2325
}
2426

2527
export function currentFile(document?: vscode.TextDocument): CurrentFile {
@@ -67,13 +69,17 @@ export function currentFile(document?: vscode.TextDocument): CurrentFile {
6769
return null;
6870
}
6971
name += ext ? "." + ext.toLowerCase() : "";
72+
const workspaceFolder = currentWorkspaceFolder(document);
73+
const uniqueId = `${workspaceFolder}:${name}`;
7074

7175
return {
7276
content,
7377
fileName,
7478
name,
7579
uri,
7680
eol,
81+
workspaceFolder,
82+
uniqueId,
7783
};
7884
}
7985

0 commit comments

Comments
 (0)