Skip to content

Commit 0b06c73

Browse files
committed
Improved completion provider for multi-depth structs
Signed-off-by: worksofliam <[email protected]>
1 parent 84dac38 commit 0b06c73

File tree

3 files changed

+63
-38
lines changed

3 files changed

+63
-38
lines changed

extension/server/src/data.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export function dspffdToRecordFormats(data: any, aliases = false): Declaration[]
5858
field: ``,
5959
pos: ``
6060
});
61-
currentSubfield.description = text.trim();
61+
62+
currentSubfield.tags.push({tag: `description`, content: text.trim()})
6263

6364
recordFormat.subItems.push(currentSubfield);
6465
});

extension/server/src/providers/completionItem.ts

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import * as ileExports from './apis';
77
import skipRules from './linter/skipRules';
88
import * as Project from "./project";
99
import { getInterfaces } from './project/exportInterfaces';
10+
import Parser from '../../../../language/parser';
1011

1112
const completionKind = {
12-
function: CompletionItemKind.Function,
13-
struct: CompletionItemKind.Struct
13+
function: CompletionItemKind.Function,
14+
struct: CompletionItemKind.Struct
1415
};
1516

1617
const eol = `\n`;
@@ -29,10 +30,10 @@ export default async function completionItemProvider(handler: CompletionParams):
2930
const isFree = (document.getText(Range.create(0, 0, 0, 6)).toUpperCase() === `**FREE`);
3031

3132
// If they're typing inside of a procedure, let's get the stuff from there too
32-
const currentProcedure = doc.procedures.find((proc, index) =>
33+
const currentProcedure = doc.procedures.find((proc, index) =>
3334
proc.range.start && proc.range.end &&
34-
lineNumber >= proc.range.start &&
35-
(lineNumber <= proc.range.end+1 || index === doc.procedures.length-1) &&
35+
lineNumber >= proc.range.start &&
36+
(lineNumber <= proc.range.end + 1 || index === doc.procedures.length - 1) &&
3637
currentPath === proc.position.path
3738
);
3839

@@ -45,42 +46,61 @@ export default async function completionItemProvider(handler: CompletionParams):
4546

4647
// This means we're just looking for subfields in the struct
4748
if (trigger === `.`) {
48-
let currentPosition = Position.create(handler.position.line, handler.position.character - 2);
49-
let preWord = getWordRangeAtPosition(document, currentPosition);
49+
const tokens = Parser.lineTokens(isFree ? currentLine : currentLine.length >= 7 ? currentLine.substring(7) : ``, 0, 0, true);
5050

51-
// Uh oh! Maybe we found dim struct?
52-
if (preWord && preWord.includes(`)`)) {
53-
const startBracket = currentLine.lastIndexOf(`(`, currentPosition.character);
51+
if (tokens.length > 0) {
52+
const cursorIndex = handler.position.character;
53+
let tokenIndex = tokens.findIndex(token => cursorIndex > token.range.start && cursorIndex <= token.range.end);
54+
console.log(tokens);
55+
console.log({ cPos: handler.position.character, tokenIndex });
5456

55-
if (startBracket > -1) {
56-
currentPosition = Position.create(handler.position.line, startBracket - 1);
57-
preWord = getWordRangeAtPosition(document, currentPosition);
57+
while (tokens[tokenIndex] && [`block`, `word`, `dot`].includes(tokens[tokenIndex].type) && tokenIndex > 0) {
58+
tokenIndex--;
5859
}
59-
}
6060

61-
// Ok, we have a 'preWord' (the name of the struct?)
62-
if (preWord) {
63-
preWord = preWord.toUpperCase();
64-
65-
// Look at the parms or existing structs to find a possible reference
66-
const possibleStruct: Declaration | undefined = [
67-
// First we search the local procedure
68-
currentProcedure && currentProcedure.scope ? currentProcedure.scope.parameters.find(parm => parm.name.toUpperCase() === preWord && parm.subItems.length > 0) : undefined,
69-
currentProcedure && currentProcedure.scope ? currentProcedure.scope.structs.find(struct => struct.name.toUpperCase() === preWord) : undefined,
70-
currentProcedure && currentProcedure.scope ? currentProcedure.scope.constants.find(struct => struct.subItems.length > 0 && struct.name.toUpperCase() === preWord) : undefined,
71-
72-
// Then we search the globals
73-
doc.structs.find(struct => struct.name.toUpperCase() === preWord),
74-
doc.constants.find(struct => struct.subItems.length > 0 && struct.name.toUpperCase() === preWord)
75-
].find(x => x); // find the first non-undefined item
76-
77-
if (possibleStruct && possibleStruct.keyword[`QUALIFIED`]) {
78-
items.push(...possibleStruct.subItems.map(subItem => {
61+
let currentDef: Declaration | undefined;
62+
63+
for (tokenIndex; tokenIndex < tokens.length; tokenIndex++) {
64+
if ([`block`, `dot`, `newline`].includes(tokens[tokenIndex].type)) {
65+
continue;
66+
}
67+
68+
const word = tokens[tokenIndex].value?.toUpperCase();
69+
70+
if (!word) break;
71+
72+
if (currentDef) {
73+
if (currentDef.subItems && currentDef.subItems.length > 0) {
74+
currentDef = currentDef.subItems.find(subItem => subItem.name.toUpperCase() === word);
75+
}
76+
77+
} else {
78+
currentDef = [
79+
// First we search the local procedure
80+
currentProcedure && currentProcedure.scope ? currentProcedure.scope.parameters.find(parm => parm.name.toUpperCase() === word && parm.subItems.length > 0) : undefined,
81+
currentProcedure && currentProcedure.scope ? currentProcedure.scope.structs.find(struct => struct.name.toUpperCase() === word && struct.keyword[`QUALIFIED`]) : undefined,
82+
currentProcedure && currentProcedure.scope ? currentProcedure.scope.constants.find(struct => struct.subItems.length > 0 && struct.name.toUpperCase() === word) : undefined,
83+
84+
// Then we search the globals
85+
doc.structs.find(struct => struct.name.toUpperCase() === word && struct.keyword[`QUALIFIED`]),
86+
doc.constants.find(constants => constants.subItems.length > 0 && constants.name.toUpperCase() === word)
87+
].find(x => x); // find the first non-undefined item
88+
89+
if (currentDef && currentDef.subItems.length > 0) {
90+
// All good!
91+
} else {
92+
currentDef = undefined;
93+
}
94+
}
95+
}
96+
97+
if (currentDef && currentDef.subItems.length > 0) {
98+
items.push(...currentDef.subItems.map(subItem => {
7999
const item = CompletionItem.create(subItem.name);
80100
item.kind = CompletionItemKind.Property;
81101
item.insertText = subItem.name;
82102
item.detail = prettyKeywords(subItem.keyword);
83-
item.documentation = subItem.description + `${possibleStruct ? ` (${possibleStruct.name})` : ``}`;
103+
item.documentation = subItem.description + ` (${currentDef.name})`;
84104
return item;
85105
}));
86106
}
@@ -100,10 +120,10 @@ export default async function completionItemProvider(handler: CompletionParams):
100120
item.detail = file.relative;
101121
return item;
102122
}));
103-
123+
104124
} else if (currentLine.trimStart().startsWith(`//`)) {
105125
items.push(...skipRules);
106-
126+
107127
} else {
108128
const expandScope = (localCache: Cache) => {
109129
for (const subItem of localCache.parameters) {
@@ -266,7 +286,7 @@ export default async function completionItemProvider(handler: CompletionParams):
266286
kind: `markdown`,
267287
value: [
268288
currentExport.description,
269-
(currentExport.example ?
289+
(currentExport.example ?
270290
[`---`, '', '```rpgle', currentExport.example.join(eol), '```'].join(eol)
271291
: undefined)
272292
].filter(v => v).join(eol + eol)

language/parser.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,16 @@ export default class Parser {
145145
}
146146
}
147147

148-
static lineTokens(input: string, lineNumber: number, lineIndex: number): Token[] {
148+
static lineTokens(input: string, lineNumber: number, lineIndex: number, withBlocks?: boolean): Token[] {
149149
let tokens = tokenise(input, {
150150
baseIndex: lineIndex,
151151
lineNumber,
152152
ignoreTypes: [`tab`]
153153
});
154+
155+
if (withBlocks) {
156+
tokens = createBlocks(tokens);
157+
}
154158

155159
return tokens;
156160
}

0 commit comments

Comments
 (0)