Skip to content

Commit ed6d6ec

Browse files
authored
Merge branch 'main' into hz/info
2 parents 8259aee + c4ff02b commit ed6d6ec

19 files changed

+1045
-1008
lines changed

client/src/extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function activate(context: ExtensionContext) {
3838
// Options to control the language client
3939
const clientOptions: LanguageClientOptions = {
4040
// Register the server for plain text documents
41-
documentSelector: [{ scheme: 'file', language: 'sourcejs' }],
41+
documentSelector: [{ scheme: 'file', language: 'source' }],
4242
synchronize: {
4343
// Notify the server about file changes to '.clientrc files contained in the workspace
4444
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
@@ -62,7 +62,7 @@ export function activate(context: ExtensionContext) {
6262
})();
6363

6464
context.subscriptions.push(
65-
commands.registerCommand("sourcejs.setLanguageVersion", async () => {
65+
commands.registerCommand("source.setLanguageVersion", async () => {
6666
const versions = [`Source ${SECTION}1`, `Source ${SECTION}2`, `Source ${SECTION}3`, `Source ${SECTION}4`]
6767
const selectedVersion = await window.showQuickPick(versions, {
6868
placeHolder: "Select the language version",

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "source-lsp",
33
"displayName": "Source-LSP",
44
"description": "lsp server for source",
5-
"version": "0.1.0",
5+
"version": "0.1.2",
66
"main": "./client/out/extension",
77
"engines": {
88
"vscode": "^1.93.0"
@@ -13,22 +13,22 @@
1313
"contributes": {
1414
"languages": [
1515
{
16-
"id": "sourcejs",
16+
"id": "source",
1717
"aliases": [
1818
"Source",
19-
"sourcejs"
19+
"source"
2020
],
2121
"extensions": [
22-
".sourcejs"
22+
".source"
2323
],
2424
"configuration": "./language-configuration.json"
2525
}
2626
],
2727
"grammars": [
2828
{
29-
"language": "sourcejs",
30-
"scopeName": "source.sourcejs",
31-
"path": "./syntaxes/sourcejs.tmLanguage.json"
29+
"language": "source",
30+
"scopeName": "source.source",
31+
"path": "./syntaxes/source.tmLanguage.json"
3232
}
3333
],
3434
"configuration": {
@@ -50,8 +50,8 @@
5050
},
5151
"commands": [
5252
{
53-
"command": "sourcejs.setLanguageVersion",
54-
"title": "Sourcejs: Set Source Version Used"
53+
"command": "source.setLanguageVersion",
54+
"title": "Source: Set Source Version Used"
5555
}
5656
]
5757
},

server/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/ast.ts

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ export class AST {
1616
[module_name: string]: Map<string, { range: Range, local: string }>
1717
} = {};
1818
private uri: string;
19+
private prependLines: number;
1920
private diagnostics: Diagnostic[] = [];
2021
// Array of callbacks to call once parsing is done
2122
// Needed for things like checking if an variable name has already been declared
2223
private diagnosticsCallbacks: Array<[(child: Node) => void, Node]> = [];
2324

24-
constructor(text: string, context: Context, uri: string) {
25+
// If prepend is supplied, prepend it to text, and offset all locations returned back to client
26+
constructor(text: string, context: Context, uri: string, prependLines: number = 0) {
27+
this.prependLines = prependLines;
2528
const acornOptions: Options = {
2629
ecmaVersion: DEFAULT_ECMA_VERSION,
2730
sourceType: "module",
@@ -327,15 +330,17 @@ export class AST {
327330
else return undefined;
328331
}
329332

330-
public getOccurences(pos: Position): DocumentHighlight[] {
331-
const identifier = this.findIdentifierNode(vsPosToEsPos(pos));
333+
public getOccurences(pos: Position, identifier?: Identifier, declaration?: DeclarationSymbol): Range[] {
334+
if (identifier === undefined)
335+
identifier = this.findIdentifierNode(vsPosToEsPos(pos));
332336
if (!identifier) return [];
333337

334-
const declaration = this.findDeclarationByName(identifier.name, identifier.loc!);
338+
if (declaration === undefined)
339+
declaration = this.findDeclarationByName(identifier.name, identifier.loc!);
335340
if (!declaration) return [];
336341

337342
let scopeFound = false;
338-
const ret: DocumentHighlight[] = [];
343+
const ret: Range[] = [];
339344
const queue: Node[] = [this.ast];
340345

341346
while (queue.length > 0) {
@@ -345,7 +350,7 @@ export class AST {
345350
if (!scopeFound && node.loc && sourceLocEquals(node.loc, declaration.scope))
346351
scopeFound = true;
347352
if (scopeFound && node.type === NODES.IDENTIFIER && node.name === identifier.name)
348-
ret.push({ range: sourceLocToRange(node.loc!) })
353+
ret.push(sourceLocToRange(node.loc!))
349354

350355
getNodeChildren(node, true).forEach(node => {
351356
if (
@@ -364,7 +369,6 @@ export class AST {
364369
}
365370

366371
public getDocumentSymbols(): DocumentSymbol[] {
367-
368372
let ret: DocumentSymbol[] = []
369373
this.declarations.forEach((value, key) => {
370374
ret = ret.concat(value.filter(x => x.showInDocumentSymbols !== false).map((declaration: DeclarationSymbol): DocumentSymbol => mapDeclarationSymbolToDocumentSymbol(declaration, this.context)))
@@ -374,12 +378,27 @@ export class AST {
374378
}
375379

376380
public renameSymbol(pos: Position, newName: string): WorkspaceEdit | null {
377-
const occurences = this.getOccurences(pos);
378-
if (occurences.length === 0) return null;
381+
// Currently, trying to rename an imported function has two issues
382+
// The first issue is that we should be replacing the import statement with import { <old name> as <new name> } from "<module name>";
383+
// The second issue is that in a import specifier, there is an imported and local field.
384+
// Imported is an identifier node for actual name of the function that was imported
385+
// Local is also an identifier node for the alias of the function that was imported
386+
// If the function was not renamed, both imported and local will have the same location
387+
// This leads to an error in the client, as there are two changes to the program that have overlapping ranges
388+
// For now, if the name that the user is trying to rename is an imported name, we don't allow renames
389+
// I will leave this for a future CP3108 student to fix :)
390+
const identifier = this.findIdentifierNode(vsPosToEsPos(pos));
391+
if (!identifier) return null;
392+
393+
const declaration = this.findDeclarationByName(identifier.name, identifier.loc!);
394+
if (!declaration || declaration.declarationKind === DeclarationKind.KIND_IMPORT) return null;
395+
396+
const occurences = this.getOccurences(pos, identifier, declaration);
397+
if (occurences.length === 0 || occurences.some(x => x.start.line < this.prependLines)) return null;
379398

380399
return {
381400
changes: {
382-
[this.uri]: occurences.map(loc => TextEdit.replace(loc.range, newName))
401+
[this.uri]: occurences.map(loc => TextEdit.replace(loc, newName))
383402
}
384403
};
385404
}
@@ -405,7 +424,8 @@ export class AST {
405424
}
406425
if (value === undefined)
407426
return null;
408-
else return {
427+
428+
return {
409429
contents: {
410430
kind: "markdown",
411431
value: value
@@ -443,33 +463,30 @@ export class AST {
443463
return autocomplete_labels[this.context.chapter - 1]
444464
.concat(ret)
445465
.concat(module_autocomplete.map((item: CompletionItem): CompletionItem => {
446-
if (this.imported_names[item.data.module_name]) {
447-
if (this.imported_names[item.data.module_name].has(item.label)) {
466+
// Logic for auto imports
467+
const map = this.imported_names[item.data.module_name];
468+
if (map) {
469+
if (map.has(item.label)) {
448470
return {
449471
...item,
450-
label: this.imported_names[item.data.module_name].get(item.label)!.local,
472+
label: map.get(item.label)!.local,
451473
detail: `Imported from ${item.data.module_name}`,
452474
data: { type: AUTOCOMPLETE_TYPES.SYMBOL, ...item.data }
453475
};
454476
}
455-
else {
456-
// Not sure if the map preserves the order that names were inserted
457-
let last_imported_range: Range = { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
458-
this.imported_names[item.data.module_name].forEach(range => {
459-
last_imported_range = findLastRange(last_imported_range, range.range);
460-
});
477+
else if ([...map][map.size-1][1].range.start.line >= this.prependLines){
461478
return {
462479
...item,
463480
additionalTextEdits: [
464-
TextEdit.insert(last_imported_range.end, `, ${item.label}`)
481+
TextEdit.insert([...map][map.size-1][1].range.end, `, ${item.label}`)
465482
]
466483
};
467484
};
468485
}
469-
else return {
486+
return {
470487
...item,
471488
additionalTextEdits: [
472-
TextEdit.insert({ line: 0, character: 0 }, `import { ${item.label} } from "${item.data.module_name}";\n`)
489+
TextEdit.insert({ line: this.prependLines, character: 0 }, `import { ${item.label} } from "${item.data.module_name}";\n`)
473490
]
474491
};
475492
}))

server/src/docs/build_docs.mjs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ function processConstant(names, element, document) {
5050
const descriptionDiv = document.createElement('div');
5151
descriptionDiv.appendChild(titleNode);
5252
descriptionDiv.appendChild(descriptionNode);
53-
const markdown = buildDescriptionMarkdown(descriptionDiv);
53+
54+
let markdown = buildDescriptionMarkdown(descriptionDiv);
55+
const lines = markdown.split("\n");
56+
lines.unshift("```source");
57+
lines[1] = lines[1].substring(5);
58+
lines.splice(2, 0, "```");
59+
markdown = lines.join("\n");
5460

5561
names.push({ label: name, title, description: markdown, meta: CONST_DECL });
5662
}
@@ -66,10 +72,16 @@ function processFunction(names, element, document) {
6672
const descriptionDiv = document.createElement('div');
6773
descriptionDiv.appendChild(titleNode);
6874
descriptionDiv.appendChild(descriptionNode);
69-
const html = buildDescriptionMarkdown(descriptionDiv);
75+
76+
let markdown = buildDescriptionMarkdown(descriptionDiv);
77+
const lines = markdown.split("\n");
78+
lines.unshift("```source");
79+
lines[1] = lines[1].substring(5);
80+
lines.splice(2, 0, "```");
81+
markdown = lines.join("\n");
7082

7183
const params = (Object.keys(patches["rename_params"])).includes(name) ? patches["rename_params"][name] : [...title.matchAll(/\w+\(([^)]*)\)/g)][0][1].split(",").map(s => s.trim());
72-
const autocomplete = { label: name, title, description: html, meta: FUNC_DECL, parameters: params[0] === '' ? [] : params };
84+
const autocomplete = { label: name, title, description: markdown, meta: FUNC_DECL, parameters: params[0] === '' ? [] : params };
7385

7486
if (Object.keys(patches["optional_params"]).includes(name))
7587
autocomplete["optional_params"] = patches["optional_params"][name];
@@ -148,7 +160,7 @@ async function buildDoc(name) {
148160
item["description"] = `#### ${key}:${doc[key]['type']}\n${turndownService.turndown(doc[key]["description"])}`;
149161
else if (doc[key]["kind"] === "function") {
150162
const params = doc[key]['params'].map(x => x[0]);
151-
item["description"] = `#### ${key}(${params.join(', ')}) → ${doc[key]['retType']}\n${turndownService.turndown(doc[key]["description"])}`;
163+
item["description"] = `\`\`\`source\n${key}(${params.join(', ')}) → ${doc[key]['retType']}\n\`\`\`\n${turndownService.turndown(doc[key]["description"])}`;
152164
item["parameters"] = params
153165
}
154166

server/src/docs/keywords.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"for": {
7575
"label": "for",
7676
"documentation": "```js\nfor(let index = 0; i < n; index++) {\n\t// For body\n}\n```",
77-
"insertText": "for(let ${1:index} = 0; i < ${2:n}; ${1:index}++) {\n\t${3:}\n}"
77+
"insertText": "for(let ${1:index} = 0; ${1:index} < ${2:n}; ${1:index}++) {\n\t${3:}\n}"
7878
},
7979
"break": {
8080
"label": "break",

0 commit comments

Comments
 (0)