Skip to content

Commit de413e5

Browse files
committed
keywords autocomplete
1 parent 7cd7198 commit de413e5

File tree

5 files changed

+118
-6
lines changed

5 files changed

+118
-6
lines changed

server/src/ast.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Identifier, Literal, SourceLocation, Node, Position as EsPosition } from 'estree';
22
import { AUTOCOMPLETE_TYPES, Chapter, Context, DeclarationKind, DECLARATIONS, DeclarationSymbol, Documentation, EXPRESSIONS, ImportedSymbol, NODES, ParameterSymbol } from "./types";
3-
import { autocomplete_labels, builtin_constants, builtin_functions, esPosInSourceLoc, findLastRange, getImportedName, getNodeChildren, isBuiltinConst, isBuiltinFunction, mapDeclarationSymbolToDocumentSymbol, mapMetaToCompletionItemKind, module_autocomplete, moduleExists, rangeToSourceLoc, sourceLocEquals, sourceLocInSourceLoc, sourceLocToRange, vsPosInSourceLoc, vsPosToEsPos } from "./utils";
3+
import { autocomplete_labels, builtin_constants, builtin_functions, esPosInSourceLoc, findLastRange, getImportedName, getNodeChildren, isBuiltinConst, isBuiltinFunction, keyword_autocomplete, mapDeclarationSymbolToDocumentSymbol, mapMetaToCompletionItemKind, module_autocomplete, moduleExists, rangeToSourceLoc, sourceLocEquals, sourceLocInSourceLoc, sourceLocToRange, vsPosInSourceLoc, vsPosToEsPos } from "./utils";
44
import { CompletionItem, Diagnostic, DiagnosticSeverity, DiagnosticTag, DocumentHighlight, DocumentSymbol, Hover, Position, Range, TextEdit, WorkspaceEdit } from "vscode-languageserver";
55
import { parse as acornParse, Options } from 'acorn';
66
import { parse as looseParse } from 'acorn-loose';
@@ -68,6 +68,7 @@ export class AST {
6868
this.processDeclarations(child, parent);
6969
this.processDiagnostics(child, parent);
7070
queue.push(child);
71+
7172
})
7273
}
7374

@@ -471,7 +472,8 @@ export class AST {
471472
TextEdit.insert({ line: 0, character: 0 }, `import { ${item.label} } from "${item.data.module_name}";\n`)
472473
]
473474
};
474-
}));
475+
}))
476+
.concat(keyword_autocomplete[this.context.chapter-1]);
475477
}
476478

477479
public getDiagnostics(): Diagnostic[] {
@@ -480,4 +482,4 @@ export class AST {
480482
source: `Source ${this.context.chapter}`
481483
}))
482484
}
483-
}
485+
}

server/src/docs/keywords.json

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"source": [
3+
["true", "false", "undefined", "const", "ternary", "function", "if_source1", "else", "lambda"],
4+
["true", "false", "undefined", "const", "ternary", "function", "if_source1", "else", "lambda", "null"],
5+
["true", "false", "undefined", "const", "ternary", "function", "if_source3", "else", "lambda", "null", "let", "while", "for", "break", "continue"],
6+
["true", "false", "undefined", "const", "ternary", "function", "if_source3", "else", "lambda", "null", "let", "while", "for", "break", "continue"]
7+
],
8+
"keywords": {
9+
"true": {
10+
"label": "true",
11+
"documentation": "Boolean true"
12+
},
13+
"false": {
14+
"label": "false",
15+
"documentation": "Boolean false"
16+
},
17+
"undefined": {
18+
"label": "undefined",
19+
"documentation": "Primitive value"
20+
},
21+
"return": {
22+
"label": "return",
23+
"documentation": "Return statement"
24+
},
25+
"const": {
26+
"label": "const",
27+
"documentation": "```js\nconst name = value\n```",
28+
"insertText": "const ${1:name} = ${2:value};"
29+
},
30+
"ternary": {
31+
"label": "ternary",
32+
"documentation": "```js\npredicate ? consequent : alternate\n```",
33+
"insertText": "${1:predicate} ? ${2:consequent} : ${3:alternate};"
34+
},
35+
"function": {
36+
"label": "function",
37+
"documentation": "```js\nfunction name(args) {\n\t// Function body\n}\n```",
38+
"insertText": "function ${1:name}(${2:args}) {\n\t${3:}\n}"
39+
},
40+
"if_source1": {
41+
"label": "if",
42+
"documentation": "```js\nif (predicate) {\n\t// If body\n}\nelse {\n\t\n}\n```",
43+
"insertText": "if (${1:predicate}) {\n\t${2:}\n}\nelse {\n\t${3:}\n}"
44+
},
45+
"else": {
46+
"label": "else",
47+
"documentation": "```js\nelse {\n\t// Else body\n}\n```",
48+
"insertText": "else {\n\t${1:}\n}"
49+
},
50+
"lambda": {
51+
"label": "lambda",
52+
"documentation": "```js\n(args) => // Lambda body\n```",
53+
"insertText": "(${1:args}) => ${2:}"
54+
},
55+
"null": {
56+
"label": "null",
57+
"documentation": "Primitive value, the empty list."
58+
},
59+
"if_source3": {
60+
"label": "if",
61+
"documentation": "```js\nif (predicate) {\n\t// If body\n}\n```",
62+
"insertText": "if (${1:predicate}) {\n\t${2:}\n}"
63+
},
64+
"let": {
65+
"label": "let",
66+
"documentation": "```js\nlet name = value\n```",
67+
"insertText": "let ${1:name} = ${2:value};"
68+
},
69+
"while": {
70+
"label": "while",
71+
"documentation": "```js\nwhile(predicate) {\n\t// While body\n}\n```",
72+
"insertText": "while(${1:predicate}) {\n\t${2:}\n}"
73+
},
74+
"for": {
75+
"label": "for",
76+
"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}"
78+
},
79+
"break": {
80+
"label": "break",
81+
"documentation": "Break statement"
82+
},
83+
"continue": {
84+
"label": "continue",
85+
"documentation": "Conintue statement"
86+
}
87+
}
88+
}

server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ connection.onCompletion(
148148
// This handler resolves additional information for the item selected in
149149
// the completion list.
150150
connection.onCompletionResolve((item: CompletionItem): CompletionItem => {
151-
if (item.data.parameters) {
151+
if (item.data?.parameters) {
152152
item.insertText = `${item.label}(${item.data.parameters.filter((x: string) => item.data.optional_params ? !item.data.optional_params.includes(x) : true).map((param: string, idx: number) => `\${${idx + 1}:${param}}`)})`;
153153
item.insertTextFormat = InsertTextFormat.Snippet;
154154
};

server/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as es from 'estree';
44
// Note that the order the enum fields appear in determine the order they are displayed in the autocomplete list
55
export enum AUTOCOMPLETE_TYPES {
66
BUILTIN,
7+
KEYWORD,
78
SYMBOL,
89
MODULE
910
}

server/src/utils.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import * as es from "estree";
2-
import { CompletionItem, CompletionItemKind, DocumentSymbol, MarkupKind, Position, Range, SymbolKind } from "vscode-languageserver";
2+
import { CompletionItem, CompletionItemKind, DocumentSymbol, InsertTextFormat, MarkupKind, Position, Range, SymbolKind } from "vscode-languageserver";
33
import { AUTOCOMPLETE_TYPES, Chapter, CompletionItemData, Context, DeclarationKind, DeclarationSymbol, Documentation } from "./types";
44

55
import source from './docs/source.json'
66
import modules from "./docs/modules.json";
7+
import keywords from "./docs/keywords.json"
78

89
export const builtin_functions: Array<{ [key: string]: Documentation }> = source.map(version => version.filter(doc => doc.meta === "func").reduce((a, v) => ({ ...a, [v.label]: v }), {}));
910
export const builtin_constants: Array<{ [key: string]: Documentation }> = source.map(version => version.filter(doc => doc.meta === "const").reduce((a, v) => ({ ...a, [v.label]: v }), {}));
@@ -45,6 +46,26 @@ for (const key in modules) {
4546
});
4647
}
4748

49+
export const keyword_autocomplete: CompletionItem[][] = [];
50+
for (let i = 0; i < 4; i++) {
51+
keyword_autocomplete[i] = [];
52+
keywords.source[i].forEach(s => {
53+
const keyword: {label: string, documentation: string, insertText?:string} = keywords.keywords[s as keyof typeof keywords.keywords];
54+
55+
keyword_autocomplete[i].push({
56+
...keyword,
57+
kind: CompletionItemKind.Keyword,
58+
labelDetails: { detail: " (keyword)" },
59+
documentation: {
60+
kind: MarkupKind.Markdown,
61+
value: keyword.documentation
62+
},
63+
...keyword.insertText && { insertTextFormat: InsertTextFormat.Snippet },
64+
sortText: '' + AUTOCOMPLETE_TYPES.KEYWORD
65+
})
66+
});
67+
}
68+
4869
export function moduleExists(module_name: string): boolean {
4970
return module_name in modules;
5071
}
@@ -95,7 +116,7 @@ export function getNodeChildren(node: es.Node, allChildren = false): es.Node[] {
95116
case 'VariableDeclaration':
96117
return node.declarations.flatMap(x => getNodeChildren(x, allChildren))
97118
case 'VariableDeclarator':
98-
const var_id: es.Node[] = (allChildren && node.id ) ? [node.id] : []
119+
const var_id: es.Node[] = (allChildren && node.id) ? [node.id] : []
99120
const init = node.init ? [node.init] : []
100121
return var_id.concat(init);
101122
case 'ImportDeclaration':

0 commit comments

Comments
 (0)