Skip to content

Commit b4d9e56

Browse files
heyzecshirsho-12
andauthored
feat: improve handling of prepend (#37)
* refactor: create prependUtils.ts * fix: add newline after prepend Fixes #35 * fix: clearer prepend markers Fixes #31 * feat: remove prepend when sending to frontend --------- Co-authored-by: Shirshajit Sen Gupta <[email protected]>
1 parent 538a13b commit b4d9e56

File tree

5 files changed

+102
-62
lines changed

5 files changed

+102
-62
lines changed

language-configuration.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
],
2626
"folding": {
2727
"markers": {
28-
"start": "// PREPEND -- DO NOT EDIT",
29-
"end": "// END PREPEND"
28+
"start": "// PREPEND",
29+
"end": "// END PREPEND -- DO NOT EDIT PREPEND"
3030
}
3131
},
3232
"wordPattern": "[_$a-zA-Z][_$a-zA-Z0-9]*",

src/commands/showPanel.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { LANGUAGES } from "../utils/languages";
77
import { setWebviewContent } from "../utils/webview";
88
import config from "../utils/config";
99
import { FRONTEND_ELEMENT_ID } from "../constants";
10+
import _ from "lodash";
1011
import { MessageHandler } from "../utils/messageHandler";
12+
import { SOURCE_ACADEMY_ICON_URI } from "../extension";
1113

1214
let messageHandler = MessageHandler.getInstance();
13-
import { SOURCE_ACADEMY_ICON_URI } from "../extension";
1415

1516
export async function showPanel(
1617
context: vscode.ExtensionContext,

src/utils/editor.ts

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
// TODO: Move this file to src/features/editor/editor.ts
12
import * as vscode from "vscode";
23

34
import config from "../utils/config";
45
import Messages, { VscWorkspaceLocation } from "./messages";
56
import path from "path";
67
import { sendToFrontendWrapped } from "../commands/showPanel";
78
import { canonicaliseLocation } from "./misc";
9+
import { codeAddPrepend, codeRemovePrepend } from "./editorUtils";
810

911
export class Editor {
1012
editor?: vscode.TextEditor;
@@ -62,58 +64,53 @@ export class Editor {
6264
const uri = vscode.Uri.file(filePath);
6365
self.uri = uri.toString();
6466

65-
const contents =
66-
prepend !== ""
67-
? [
68-
"// PREPEND -- DO NOT EDIT",
69-
prepend,
70-
"// END PREPEND",
71-
initialCode,
72-
].join("\n")
73-
: initialCode;
74-
75-
await vscode.workspace.fs.readFile(vscode.Uri.file(filePath)).then(
76-
(value) => {
77-
if (value.toString() !== contents) {
78-
self.log(
79-
"EXTENSION: Conflict detected between local and remote, prompting user to choose one",
67+
const contents = codeAddPrepend(initialCode, prepend);
68+
69+
await vscode.workspace.fs
70+
.readFile(vscode.Uri.file(filePath))
71+
.then((value) => value.toString())
72+
.then(
73+
(localCode) => {
74+
if (localCode !== contents) {
75+
self.log(
76+
"EXTENSION: Conflict detected between local and remote, prompting user to choose one",
77+
);
78+
vscode.window
79+
.showInformationMessage(
80+
[
81+
"The local file differs from the version on the Source Academy servers.",
82+
"Discard the local file and use the one on the server?",
83+
].join(" "),
84+
{ modal: true },
85+
"Yes",
86+
)
87+
.then(async (answer) => {
88+
// By default the code displayed is the local one
89+
if (answer === "Yes") {
90+
self.log("EXTENSION: Saving program from server to file");
91+
await vscode.workspace.fs.writeFile(
92+
uri,
93+
new TextEncoder().encode(contents),
94+
);
95+
} else if (answer === undefined) {
96+
// Modal cancelled
97+
const message = Messages.Text(
98+
self.workspaceLocation,
99+
codeRemovePrepend(localCode),
100+
);
101+
sendToFrontendWrapped(message);
102+
}
103+
});
104+
}
105+
},
106+
async () => {
107+
self.log(`Opening file failed, creating at ${filePath}`);
108+
await vscode.workspace.fs.writeFile(
109+
uri,
110+
new TextEncoder().encode(contents),
80111
);
81-
vscode.window
82-
.showInformationMessage(
83-
[
84-
"The local file differs from the version on the Source Academy servers.",
85-
"Discard the local file and use the one on the server?",
86-
].join(" "),
87-
{ modal: true },
88-
"Yes",
89-
)
90-
.then(async (answer) => {
91-
// By default the code displayed is the local one
92-
if (answer === "Yes") {
93-
self.log("EXTENSION: Saving program from server to file");
94-
await vscode.workspace.fs.writeFile(
95-
uri,
96-
new TextEncoder().encode(contents),
97-
);
98-
} else if (answer === undefined) {
99-
// Modal cancelled
100-
const message = Messages.Text(
101-
self.workspaceLocation,
102-
value.toString(),
103-
);
104-
sendToFrontendWrapped(message);
105-
}
106-
});
107-
}
108-
},
109-
async () => {
110-
self.log(`Opening file failed, creating at ${filePath}`);
111-
await vscode.workspace.fs.writeFile(
112-
uri,
113-
new TextEncoder().encode(contents),
114-
);
115-
},
116-
);
112+
},
113+
);
117114

118115
const editor = await vscode.window.showTextDocument(uri, {
119116
preview: false,

src/utils/editorUtils.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// TODO: Move this file to src/features/editor/utils.ts
2+
3+
// ================================================================================
4+
// Prepend
5+
// ================================================================================
6+
7+
// If this is edited, also change folding.markers.{start,end} in language-configuration.json
8+
const PREPEND_MARKER_START = "// PREPEND";
9+
const PREPEND_MARKER_END = "// END PREPEND -- DO NOT EDIT PREPEND";
10+
11+
export function codeAddPrepend(code: string, prepend: string) {
12+
if (prepend === "") {
13+
return code;
14+
}
15+
return [
16+
PREPEND_MARKER_START,
17+
prepend,
18+
PREPEND_MARKER_END,
19+
"", // Visual separation between prepend and code
20+
code,
21+
].join("\n");
22+
}
23+
24+
export function getNumPrependLines(prepend: string) {
25+
if (prepend === "") {
26+
return 0;
27+
}
28+
return prepend.split("\n").length + 3; // account for start/end markers
29+
}
30+
31+
export function codeRemovePrepend(code: string) {
32+
const lines = code.split("\n");
33+
const start = lines.indexOf(PREPEND_MARKER_START);
34+
const end = lines.indexOf(PREPEND_MARKER_END);
35+
36+
if (start === -1 || end === -1 || end < start) {
37+
return code;
38+
}
39+
40+
// If line spacing between prepend and code accidentally removed, do not delete code!
41+
const skip = end + (lines[end + 1] === "" ? 1 : 0);
42+
43+
return lines.slice(skip + 1).join("\n");
44+
}

src/utils/messageHandler.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { client } from "../extension";
1414
import _ from "lodash";
1515
import { treeDataProvider } from "../treeview";
1616
import McqPanel from "../webview/components/McqPanel";
17+
import { codeRemovePrepend, getNumPrependLines } from "./editorUtils";
1718

1819
/*
1920
* MessageHandler is a singleton class that handles messages from the frontend
@@ -122,18 +123,12 @@ export class MessageHandler {
122123
this.activeEditor.uri;
123124
const info = context.globalState.get("info") ?? {};
124125
if (this.activeEditor.uri) {
125-
// TODO: Write our own wrapper to set nested keys easily, removing lodash
126-
// @ts-ignore
127126
_.set(
128127
info,
129128
`["${this.activeEditor.uri}"].chapter`,
130129
message.chapter ?? 1,
131130
);
132-
// TODO: message.prepend can be undefined in runtime, investigate
133-
const nPrependLines =
134-
message.prepend && message.prepend !== ""
135-
? message.prepend.split("\n").length + 2 // account for start/end markers
136-
: 0;
131+
const nPrependLines = getNumPrependLines(message.prepend);
137132
_.set(info, `["${this.activeEditor.uri}"].prepend`, nPrependLines);
138133
context.globalState.update("info", info);
139134
client.sendRequest("source/publishInfo", info);
@@ -155,7 +150,10 @@ export class MessageHandler {
155150
`EXTENSION: Editor ${editor.assessmentName}_${editor.questionId}_${editor.assessmentType} is no longer active, skipping onChange`,
156151
);
157152
}
158-
const message = Messages.Text(workspaceLocation, code);
153+
const message = Messages.Text(
154+
workspaceLocation,
155+
codeRemovePrepend(code),
156+
);
159157
console.log(`Sending message: ${JSON.stringify(message)}`);
160158
sendToFrontend(this.panel, message);
161159
});

0 commit comments

Comments
 (0)