From 6e3d5a26885a162dacbf4888f0ccebb45d6f085c Mon Sep 17 00:00:00 2001 From: Liam Barry Allan Date: Fri, 19 Aug 2022 11:37:54 -0400 Subject: [PATCH 1/4] Ensure ranges are attached to definitions Signed-off-by: Liam Barry Allan --- src/language/linter.js | 2 +- src/language/parser.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/language/linter.js b/src/language/linter.js index aa8ae205..28b8f539 100644 --- a/src/language/linter.js +++ b/src/language/linter.js @@ -868,7 +868,7 @@ module.exports = class Linter { if (rules.CollectReferences) { let defRef; - if (currentProcedure) { + if (currentProcedure && currentProcedure.scope) { defRef = currentProcedure.scope.find(upperName); if (!defRef) { diff --git a/src/language/parser.js b/src/language/parser.js index 859e8841..00800d4b 100644 --- a/src/language/parser.js +++ b/src/language/parser.js @@ -566,6 +566,11 @@ module.exports = class Parser { line: lineNumber } + currentItem.range = { + start: lineNumber, + end: null + }; + currentGroup = `structs`; // Expand the LIKEDS value if there is one. @@ -588,6 +593,11 @@ module.exports = class Parser { break; case `END-DS`: + if (dsScopes.length > 0) { + const currentDs = dsScopes[dsScopes.length - 1]; + currentDs.range.end = lineNumber; + } + if (dsScopes.length === 1) { scope.structs.push(dsScopes.pop()); } else @@ -613,8 +623,14 @@ module.exports = class Parser { currentItem.readParms = true; + currentItem.range = { + start: lineNumber, + end: null + }; + // Does the keywords include a keyword that makes end-ds useless? if (currentItem.keywords.some(keyword => oneLineTriggers[`DCL-PR`].some(trigger => keyword.startsWith(trigger)))) { + currentItem.range.end = lineNumber; scope.procedures.push(currentItem); resetDefinition = true; } @@ -626,6 +642,7 @@ module.exports = class Parser { case `END-PR`: if (currentItem && currentItem.type === `procedure`) { + currentItem.range.end = lineNumber; scope.procedures.push(currentItem); resetDefinition = true; } @@ -996,6 +1013,11 @@ module.exports = class Parser { line: lineNumber } + currentItem.range = { + start: lineNumber, + end: null + }; + expandDs(file, currentItem); currentGroup = `structs`; @@ -1014,6 +1036,11 @@ module.exports = class Parser { path: file, line: lineNumber } + + currentItem.range = { + start: lineNumber, + end: null + }; currentGroup = `procedures`; scope.procedures.push(currentItem); @@ -1034,6 +1061,7 @@ module.exports = class Parser { break; default: + // No type, must be either a struct subfield OR a parameter if (!currentItem) { switch (currentGroup) { case `structs`: @@ -1082,6 +1110,8 @@ module.exports = class Parser { currentItem.subItems[currentItem.subItems.length - 1].keywords.push(Fixed.getPrettyType(dSpec), ...dSpec.keywords); } } + + currentItem.range.end = lineNumber; } break; } From 519240ef3fbb75b28f40187ef686132f1bfdbbd9 Mon Sep 17 00:00:00 2001 From: Liam Barry Allan Date: Fri, 19 Aug 2022 11:38:09 -0400 Subject: [PATCH 2/4] Update unit tests to check ranges Signed-off-by: Liam Barry Allan --- tests/suite/basics.js | 64 +++++++++++++++++++++++++++++++++++++++++++ tests/suite/fixed.js | 54 ++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/tests/suite/basics.js b/tests/suite/basics.js index 90fde1e9..ee239c19 100644 --- a/tests/suite/basics.js +++ b/tests/suite/basics.js @@ -65,6 +65,11 @@ exports.test3 = async () => { assert.strictEqual(cache.variables[0].position.line, 1, `Index of 1 expected`); assert.strictEqual(cache.variables[1].position.line, 6, `Index of 6 expected`); assert.strictEqual(cache.structs[0].position.line, 2, `Index of 2 expected`); + + assert.deepStrictEqual(cache.structs[0].range, { + start: 2, + end: 5 + }); } /** @@ -89,6 +94,7 @@ exports.test4 = async () => { assert.strictEqual(cache.variables[0].position.line, 1, `Index of 1 expected`); assert.strictEqual(cache.subroutines[0].range.start, 4, `Index of 4 expected`); + assert.strictEqual(cache.subroutines[0].range.end, 6); } /** @@ -363,6 +369,11 @@ exports.test12 = async () => { const theLocalProc = cache.find(`theLocalProc`); + assert.deepStrictEqual(theLocalProc.range, { + start: 16, + end: 23 + }); + // Has a parameter assert.strictEqual(theLocalProc.subItems.length, 1, `Expect length of 1`); @@ -437,4 +448,57 @@ exports.subds1 = async () => { assert.strictEqual(DsChangingNodeRole.subItems.length, 13); assert.strictEqual(DsChangingNodeRole.subItems[12].name, `Role`); + + assert.deepStrictEqual(DsChangingNodeRole.range, { + start: 2, + end: 19 + }); +}; + +exports.dothingy = async () => { + const lines = [ + `**FREE`, + `///`, + `// Get delimiters`, + `//`, + `// Returns a pointer to the used delimiters for parsing the JSON and XML data.`, + `// The pointer points to a data structure in the format jx_DelimiterDS.`, + `//`, + `// @return Pointer to the delimiters data structure (jx_DelimiterDS)`, + `//`, + `// @warning Do not deallocate the returned pointer!`, + `///`, + `Dcl-PR json_getDelims Pointer extproc(*CWIDEN : 'jx_GetDelimiters') End-PR;`, + ``, + `///`, + `// Set delimiters`, + `//`, + `// Sets the delimiters used for parsing the JSON and XML data.`, + `// For the default delimiters see constant json_DELIMITERS.`, + `//`, + `// @param Delimiters`, + `///`, + `Dcl-PR json_setDelimiters extproc(*CWIDEN : 'jx_SetDelimiters');`, + ` delimiters pointer value options(*string);`, + `End-PR;`, + ``, + ].join(`\n`); + + const parser = new Parser(); + const cache = await parser.getDocs(uri, lines); + + assert.strictEqual(cache.procedures.length, 2); + + const json_getDelims = cache.find(`json_getDelims`); + assert.deepStrictEqual(json_getDelims.range, { + start: 11, + end: 11 + }); + + const json_getDelimiters = cache.find(`json_setDelimiters`); + assert.strictEqual(json_getDelimiters.subItems.length, 1); + assert.deepStrictEqual(json_getDelimiters.range, { + start: 21, + end: 23 + }); }; \ No newline at end of file diff --git a/tests/suite/fixed.js b/tests/suite/fixed.js index 5caeaab9..e4061f8d 100644 --- a/tests/suite/fixed.js +++ b/tests/suite/fixed.js @@ -406,6 +406,9 @@ exports.fixed10 = async () => { assert.strictEqual(dataDs.name, `data`); assert.strictEqual(dataDs.subItems.length, 4); + assert.strictEqual(dataDs.range.start, 0); + assert.strictEqual(dataDs.range.end, 4); + const rrn02 = cache.find(`rrn02`); assert.strictEqual(rrn02.name, `rrn02`); assert.strictEqual(rrn02.keywords.includes(`PACKED(7:2)`), true); @@ -485,6 +488,8 @@ exports.fixedfree1 = async () => { const Obj_List = cache.find(`Obj_List`); assert.strictEqual(Obj_List.name, `Obj_List`); + assert.strictEqual(Obj_List.range.start, 6); + assert.strictEqual(Obj_List.range.end, 50); assert.strictEqual(Obj_List.position.line, 6); assert.strictEqual(Obj_List.keywords.includes(`EXPORT`), true); assert.strictEqual(Obj_List.keyword[`EXPORT`], true); @@ -518,6 +523,8 @@ exports.fixed11 = async () => { const F4DATE = cache.find(`F4DATE`); assert.strictEqual(F4DATE.subItems.length, 4); + assert.strictEqual(F4DATE.range.start, 0); + assert.strictEqual(F4DATE.range.end, 4); const parm1 = F4DATE.subItems[0]; assert.strictEqual(parm1.keywords[0], `CHAR(1)`); @@ -532,6 +539,11 @@ exports.fixed11 = async () => { const parm4 = F4DATE.subItems[3]; assert.strictEqual(parm4.keywords[0], `CHAR(1)`); assert.strictEqual(parm4.keywords[1], `OPTIONS(*NOPASS)`); + + const F4DATEDS = cache.find(`F4DATEDS`); + assert.strictEqual(F4DATEDS.subItems.length, 4); + assert.strictEqual(F4DATEDS.range.start, 6); + assert.strictEqual(F4DATEDS.range.end, 10); }; exports.columnFix = async () => { @@ -556,6 +568,24 @@ exports.columnFix = async () => { const cache = await parser.getDocs(uri, lines); assert.strictEqual(cache.procedures.length, 3); + + const abcd1 = cache.find(`abcd1`); + assert.deepStrictEqual(abcd1.range, { + start: 0, + end: 4 + }); + + const abcd2 = cache.find(`abcd2`); + assert.deepStrictEqual(abcd2.range, { + start: 5, + end: 10 + }); + + const abcd3 = cache.find(`abcd3`); + assert.deepStrictEqual(abcd3.range, { + start: 11, + end: 13 + }); }; exports.comments1 = async () => { @@ -602,19 +632,43 @@ exports.comments1 = async () => { const SndMsgPgmQ = cache.find(`SndMsgPgmQ`); assert.strictEqual(SndMsgPgmQ.subItems.length, 4); + assert.deepStrictEqual(SndMsgPgmQ.range, { + start: 2, + end: 7 + }); const ClrMsgPgmQ = cache.find(`ClrMsgPgmQ`); assert.strictEqual(ClrMsgPgmQ.subItems.length, 1); + assert.deepStrictEqual(ClrMsgPgmQ.range, { + start: 9, + end: 10 + }); const SndEscMsg = cache.find(`SndEscMsg`); assert.strictEqual(SndEscMsg.subItems.length, 1); + assert.deepStrictEqual(SndEscMsg.range, { + start: 13, + end: 14 + }); const SndInfMsg = cache.find(`SndInfMsg`); assert.strictEqual(SndInfMsg.subItems.length, 1); + assert.deepStrictEqual(SndInfMsg.range, { + start: 17, + end: 18 + }); const JobLogMsg = cache.find(`JobLogMsg`); assert.strictEqual(JobLogMsg.subItems.length, 1); + assert.deepStrictEqual(JobLogMsg.range, { + start: 21, + end: 22 + }); const Show = cache.find(`Show`); assert.strictEqual(Show.subItems.length, 3); + assert.deepStrictEqual(Show.range, { + start: 25, + end: 28 + }); }; \ No newline at end of file From 649bf3f782e7d7a98d299ada71f906296333faa8 Mon Sep 17 00:00:00 2001 From: Liam Barry Allan Date: Fri, 19 Aug 2022 11:38:22 -0400 Subject: [PATCH 3/4] Update symbol provider to support ranges Signed-off-by: Liam Barry Allan --- src/vscode/LanguageWorker.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vscode/LanguageWorker.js b/src/vscode/LanguageWorker.js index 1e0de7c2..3cdef350 100644 --- a/src/vscode/LanguageWorker.js +++ b/src/vscode/LanguageWorker.js @@ -224,11 +224,11 @@ module.exports = class LanguageWorker { currentScopeDefs.push( ...scope.subroutines.filter(sub => sub.position && sub.position.path === currentPath) .map(def => new vscode.DocumentSymbol( - def.name, + def.name, def.keywords.join(` `).trim(), vscode.SymbolKind.Function, - new vscode.Range(def.position.line, 0, def.position.line, 0), - new vscode.Range(def.position.line, 0, def.position.line, 0) + new vscode.Range(def.range.start, 0, def.range.end, 0), + new vscode.Range(def.range.start, 0, def.range.start, 0), )), ...scope.variables @@ -299,8 +299,8 @@ module.exports = class LanguageWorker { struct.name, struct.keywords.join(` `).trim(), vscode.SymbolKind.Struct, - new vscode.Range(struct.position.line, 0, struct.position.line, 0), - new vscode.Range(struct.position.line, 0, struct.position.line, 0) + new vscode.Range(struct.range.start, 0, struct.range.end, 0), + new vscode.Range(struct.range.start, 0, struct.range.start, 0), ); structDef.children.push( @@ -332,8 +332,8 @@ module.exports = class LanguageWorker { proc.name, proc.keywords.join(` `).trim(), vscode.SymbolKind.Function, - new vscode.Range(proc.position.line, 0, proc.position.line, 0), - new vscode.Range(proc.position.line, 0, proc.position.line, 0) + new vscode.Range(proc.range.start, 0, proc.range.end, 0), + new vscode.Range(proc.range.start, 0, proc.range.start, 0), ); procDef.children.push( From 524e60cd5c8fc727e6d4a22b38796a5aa2d32f6a Mon Sep 17 00:00:00 2001 From: Liam Barry Allan Date: Sun, 21 Aug 2022 14:22:01 -0400 Subject: [PATCH 4/4] Fix broken range for oneline struct Signed-off-by: Liam Barry Allan --- src/language/parser.js | 3 ++- tests/suite/basics.js | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/language/parser.js b/src/language/parser.js index 00800d4b..b08c5d1a 100644 --- a/src/language/parser.js +++ b/src/language/parser.js @@ -578,6 +578,7 @@ module.exports = class Parser { // Does the keywords include a keyword that makes end-ds useless? if (currentItem.keywords.some(keyword => oneLineTriggers[`DCL-DS`].some(trigger => keyword.startsWith(trigger)))) { + currentItem.range.end = lineNumber; scope.structs.push(currentItem); } else { currentItem.readParms = true; @@ -1039,7 +1040,7 @@ module.exports = class Parser { currentItem.range = { start: lineNumber, - end: null + end: lineNumber }; currentGroup = `procedures`; diff --git a/tests/suite/basics.js b/tests/suite/basics.js index ee239c19..8e1108fd 100644 --- a/tests/suite/basics.js +++ b/tests/suite/basics.js @@ -455,7 +455,7 @@ exports.subds1 = async () => { }); }; -exports.dothingy = async () => { +exports.range1 = async () => { const lines = [ `**FREE`, `///`, @@ -501,4 +501,28 @@ exports.dothingy = async () => { start: 21, end: 23 }); -}; \ No newline at end of file +}; + +exports.range2 = async () => { + const lines = [ + `**free`, + `Dcl-S FullCmd Char(32);`, + `Dcl-DS ABCD LikeDS(BBOOP);`, + ``, + ``, + `Dcl-S Eod Ind;`, + ].join(`\n`); + + const parser = new Parser(); + const cache = await parser.getDocs(uri, lines); + + assert.strictEqual(cache.variables.length, 2); + assert.strictEqual(cache.structs.length, 1); + + const ABCD = cache.find(`ABCD`); + assert.strictEqual(ABCD.keyword[`LIKEDS`], `BBOOP`); + assert.deepStrictEqual(ABCD.range, { + start: 2, + end: 2 + }); +} \ No newline at end of file