Skip to content

Commit 6852923

Browse files
committed
Semi working completions
1 parent f69b8e2 commit 6852923

24 files changed

+25804
-4760
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
"lint": "eslint ./client/src ./server/src --ext .ts,.tsx",
100100
"postinstall": "cd client && npm install && cd ../server && npm install && cd ..",
101101
"test": "vscode-test",
102-
"antlr4ng": "antlr4ng -Dlanguage=TypeScript -visitor ./server/src/utils/antlr/MapIni.g4 -o ./server/src/utils/antlr4ng"
102+
"antlr4ng": "antlr4ng -Dlanguage=TypeScript -visitor ./server/src/utils/antlr4ng/MapIni.g4 -o ./server/src/utils/antlr4ng"
103103
},
104104
"devDependencies": {
105105
"@types/mocha": "^9.1.0",
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { CandidatesCollection } from "antlr4-c3";
2+
import { ParserRuleContext, Token } from "antlr4ng";
3+
import { MapIniParser } from "../utils/antlr4ng/MapIniParser";
4+
import { CompletionItem, CompletionItemKind } from "vscode-languageserver";
5+
import * as list from '../utils/lists'
6+
import { RBTree } from "bintrees";
7+
8+
9+
export function findTokenIndex(tokens: Token[], offset: number): number {
10+
for (let i = 0; i < tokens.length; i++) {
11+
const token = tokens[i];
12+
if (token.start <= offset && token.stop + 1 >= offset) {
13+
return i;
14+
}
15+
}
16+
// If no exact match, return the last token
17+
return tokens.length - 1;
18+
}
19+
20+
export function generateCompletionItems(candidates: CandidatesCollection, parser: MapIniParser): CompletionItem[] {
21+
const completionItems: CompletionItem[] = [];
22+
23+
// Process token candidates (keywords, symbols)
24+
for (const [tokenType, candidate] of candidates.tokens) {
25+
const tokenName = parser.vocabulary.getDisplayName(tokenType);
26+
27+
if (!tokenName) {
28+
continue
29+
}
30+
31+
// Clean up the token name
32+
const label = tokenName.startsWith("'") && tokenName.endsWith("'")
33+
? tokenName.substring(1, tokenName.length - 1)
34+
: tokenName;
35+
36+
completionItems.push({
37+
label,
38+
kind: CompletionItemKind.Property,
39+
data: tokenType,
40+
documentation: `Keyword: ${label}`,
41+
});
42+
}
43+
44+
// Process rule candidates (snippets, templates)
45+
for (const [ruleIndex, candidate] of candidates.rules) {
46+
const ruleName = parser.ruleNames[ruleIndex];
47+
48+
completionItems.push({
49+
label: ruleName,
50+
kind: CompletionItemKind.Snippet,
51+
data: ruleIndex,
52+
documentation: `Rule: ${ruleName}`,
53+
});
54+
}
55+
56+
return completionItems;
57+
}
58+
59+
export function findContextAtPosition(tree: ParserRuleContext, position: number): ParserRuleContext | null {
60+
if (!tree || tree.start === null || tree.stop === null) {
61+
return null;
62+
}
63+
64+
const start = tree.start.start;
65+
const stop = tree.stop.stop;
66+
67+
console.log(`CurrentRule: ${tree.getText()}, Start: ${start}, stop: ${stop}`)
68+
69+
if (position < start || position > stop) {
70+
return null;
71+
}
72+
73+
// If the node covers the position, check its children
74+
for (let i = 0; i < tree.getChildCount(); i++) {
75+
const child = tree.getChild(i);
76+
77+
if (child instanceof ParserRuleContext) {
78+
const result = findContextAtPosition(child, position);
79+
if (result !== null) {
80+
return result;
81+
}
82+
}
83+
}
84+
85+
// If no children cover the position, return this node
86+
return tree;
87+
}
88+
89+
export function getContextSpecificCompletions(ruleName: string): CompletionItem[] {
90+
const completionItems: CompletionItem[] = [];
91+
92+
// console.log(`Rules: ${ruleName}`)
93+
94+
//TODO: Add retrievels here
95+
switch (ruleName) {
96+
case 'upgrade_property':
97+
// Add completion items relevant to function declarations
98+
99+
completionItems.push(...getCompletionItemsFromRBTree(list.upgrades));
100+
completionItems.push(...getCompletionItemsFromRBTree(list.customUpgrades));
101+
break;
102+
// Add more cases for different contexts
103+
default:
104+
break;
105+
}
106+
107+
return completionItems;
108+
}
109+
110+
function getCompletionItemsFromRBTree(tree: RBTree<string>): CompletionItem[] {
111+
const completionItems: CompletionItem[] = [];
112+
113+
if (tree.size === 0) {
114+
return completionItems; // Return empty array if tree is empty
115+
}
116+
117+
// Proceed with traversal
118+
tree.each((value: string) => {
119+
completionItems.push({
120+
label: value,
121+
kind: CompletionItemKind.Text,
122+
data: value,
123+
documentation: `Value: ${value}`,
124+
});
125+
});
126+
127+
return completionItems;
128+
}

server/src/completion/helpers.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { CandidatesCollection } from "antlr4-c3";
2+
import { ParserRuleContext, TerminalNode, Token } from "antlr4ng";
3+
import { MapIniParser } from "../utils/antlr4ng/MapIniParser";
4+
import { CompletionItem, CompletionItemKind } from "vscode-languageserver";
5+
import * as list from '../utils/lists'
6+
import { RBTree } from "bintrees";
7+
8+
9+
export function findTokenIndex(tokens: Token[], offset: number): number {
10+
for (let i = 0; i < tokens.length; i++) {
11+
const token = tokens[i];
12+
if (token.start <= offset && token.stop + 1 >= offset) {
13+
return i;
14+
}
15+
}
16+
// If no exact match, return the last token
17+
return tokens.length - 1;
18+
}
19+
20+
export function generateCompletionItems(candidates: CandidatesCollection, parser: MapIniParser): CompletionItem[] {
21+
const completionItems: CompletionItem[] = [];
22+
23+
// Process token candidates (keywords, symbols)
24+
for (const [tokenType, candidate] of candidates.tokens) {
25+
const tokenName = parser.vocabulary.getDisplayName(tokenType);
26+
27+
if (!tokenName) {
28+
continue
29+
}
30+
31+
// Clean up the token name
32+
const label = tokenName.startsWith("'") && tokenName.endsWith("'")
33+
? tokenName.substring(1, tokenName.length - 1)
34+
: tokenName;
35+
36+
completionItems.push({
37+
label,
38+
kind: CompletionItemKind.Field,
39+
data: tokenType,
40+
documentation: `Keyword: ${label}`,
41+
});
42+
}
43+
44+
// Process rule candidates (snippets, templates)
45+
for (const [ruleIndex, candidate] of candidates.rules) {
46+
const ruleName = parser.ruleNames[ruleIndex];
47+
48+
completionItems.push({
49+
label: ruleName,
50+
kind: CompletionItemKind.Snippet,
51+
data: ruleIndex,
52+
documentation: `Rule: ${ruleName}`,
53+
});
54+
}
55+
56+
return completionItems;
57+
}
58+
59+
export function findContextAtPosition(tree: ParserRuleContext, position: number): ParserRuleContext | null {
60+
if (!tree || tree.start === null || tree.stop === null) {
61+
return null;
62+
}
63+
64+
console.log(`Start: ${tree.start.start}, End: ${tree.stop.stop}, Position: ${position}`)
65+
66+
position -= 1;
67+
68+
const start = tree.start.start;
69+
const stop = tree.stop.stop + 1;
70+
71+
if (position < start || position > stop) {
72+
console.log('out of range')
73+
return null;
74+
}
75+
76+
// If the node covers the position, check its children
77+
for (let i = 0; i < tree.getChildCount(); i++) {
78+
const child = tree.getChild(i);
79+
80+
if (child instanceof ParserRuleContext) {
81+
const result = findContextAtPosition(child, position);
82+
if (result !== null) {
83+
return result;
84+
}
85+
}
86+
}
87+
88+
// If no children cover the position, return this node
89+
return tree;
90+
}
91+
92+
export function getContextSpecificCompletions(ruleName: string): CompletionItem[] {
93+
const completionItems: CompletionItem[] = [];
94+
95+
console.log(`Rules: ${ruleName}`)
96+
97+
//TODO: Add retrievels here
98+
switch (ruleName) {
99+
case 'upgrade_property':
100+
// Add completion items relevant to function declarations
101+
102+
completionItems.push(...getCompletionItemsFromRBTree(list.upgrades));
103+
completionItems.push(...getCompletionItemsFromRBTree(list.customUpgrades));
104+
break;
105+
// Add more cases for different contexts
106+
default:
107+
break;
108+
}
109+
110+
return completionItems;
111+
}
112+
113+
function getCompletionItemsFromRBTree(tree: RBTree<string>): CompletionItem[] {
114+
const completionItems: CompletionItem[] = [];
115+
116+
if (tree.size === 0) {
117+
return completionItems; // Return empty array if tree is empty
118+
}
119+
120+
// Proceed with traversal
121+
tree.each((value: string) => {
122+
completionItems.push({
123+
label: value,
124+
kind: CompletionItemKind.Text,
125+
data: value,
126+
documentation: `Value: ${value}`,
127+
});
128+
});
129+
130+
return completionItems;
131+
}

0 commit comments

Comments
 (0)