From 4ecde9cc719024bdf4696726aa5a70c08bcd3e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Mon, 6 Mar 2023 13:54:30 +0100 Subject: [PATCH 1/9] Show SQL cursors in outline view --- server/src/language/models/cache.js | 4 ++++ server/src/language/models/declaration.js | 2 +- server/src/language/parser.js | 28 +++++++++++++++++++++++ server/src/providers/documentSymbols.ts | 12 +++++++++- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/server/src/language/models/cache.js b/server/src/language/models/cache.js index 60822e16..0b3ea7d4 100644 --- a/server/src/language/models/cache.js +++ b/server/src/language/models/cache.js @@ -42,6 +42,10 @@ export default class Cache { /** @type {IncludeStatement[]} */ this.includes = cache.includes || []; + + /** @type {Declaration[]} */ + this.cursor = cache.cursor || []; + } /** diff --git a/server/src/language/models/declaration.js b/server/src/language/models/declaration.js index 2fe2230b..40e44420 100644 --- a/server/src/language/models/declaration.js +++ b/server/src/language/models/declaration.js @@ -4,7 +4,7 @@ import Cache from "./cache"; export default class Declaration { /** * - * @param {"procedure"|"subroutine"|"file"|"struct"|"subitem"|"variable"|"constant"} type + * @param {"procedure"|"subroutine"|"file"|"struct"|"subitem"|"variable"|"constant"|"cursor"} type */ constructor(type) { this.type = type; diff --git a/server/src/language/parser.js b/server/src/language/parser.js index 85b67ff7..5061da46 100644 --- a/server/src/language/parser.js +++ b/server/src/language/parser.js @@ -165,6 +165,8 @@ export default class Parser { /** @type {Declaration} */ let currentSub; let currentProcName; + /** @type {Declaration} */ + let currentCursor; let resetDefinition = false; //Set to true when you're done defining a new item let docs = false; // If section is for ILEDocs @@ -778,6 +780,32 @@ export default class Parser { } break; + + case `EXEC`: + if (parts[1] === `SQL`) { + const indexDeclare = parts.findIndex(el => el === `DECLARE`); + const indexCursor = parts.findIndex(el => el === `CURSOR`); + let indexCursorName = 0; + if (indexCursor-1 == indexDeclare+1) { + indexCursorName = indexCursor-1; + } + if (currentCursor === undefined + && indexDeclare >=0 + && indexCursor >= 0) { + currentCursor = new Declaration(`cursor`); + currentCursor.name = parts[indexCursorName]; + currentCursor.position = { + path: file, + line: statementStartingLine + }; + currentCursor.keywords.push(`CURSOR`); + + scope.cursor.push(currentCursor); + currentCursor = undefined; + } + break; + } + case `///`: docs = !docs; diff --git a/server/src/providers/documentSymbols.ts b/server/src/providers/documentSymbols.ts index 619131c9..6ec583c3 100644 --- a/server/src/providers/documentSymbols.ts +++ b/server/src/providers/documentSymbols.ts @@ -74,7 +74,17 @@ export default async function documentSymbolProvider(handler: DocumentSymbolPara SymbolKind.Constant, Range.create(def.position.line, 0, def.position.line, 0), Range.create(def.position.line, 0, def.position.line, 0) - )) + )), + + ...scope.cursor + .filter(cursor => cursor.position && cursor.position.path === currentPath) + .map(def => DocumentSymbol.create( + def.name, + def.keywords.join(` `).trim(), + SymbolKind.Constant, + Range.create(def.position.line, 0, def.position.line, 0), + Range.create(def.position.line, 0, def.position.line, 0) + )) ); scope.files From 6e59d06e4d5005f35fabc29acb77af3244cd6277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Mon, 6 Mar 2023 14:03:15 +0100 Subject: [PATCH 2/9] Add fixed format --- server/src/language/parser.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/server/src/language/parser.js b/server/src/language/parser.js index 5061da46..1c94ed86 100644 --- a/server/src/language/parser.js +++ b/server/src/language/parser.js @@ -780,7 +780,6 @@ export default class Parser { } break; - case `EXEC`: if (parts[1] === `SQL`) { const indexDeclare = parts.findIndex(el => el === `DECLARE`); @@ -996,6 +995,7 @@ export default class Parser { } break; + case `P`: const pSpec = parsePLine(line); @@ -1212,6 +1212,31 @@ export default class Parser { potentialName = undefined; } break; + + case `EXEC`: + if (parts[1] === `SQL`) { + const indexDeclare = parts.findIndex(el => el === `DECLARE`); + const indexCursor = parts.findIndex(el => el === `CURSOR`); + let indexCursorName = 0; + if (indexCursor-1 == indexDeclare+1) { + indexCursorName = indexCursor-1; + } + if (currentCursor === undefined + && indexDeclare >=0 + && indexCursor >= 0) { + currentCursor = new Declaration(`cursor`); + currentCursor.name = parts[indexCursorName]; + currentCursor.position = { + path: file, + line: statementStartingLine + }; + currentCursor.keywords.push(`CURSOR`); + + scope.cursor.push(currentCursor); + currentCursor = undefined; + } + break; + } } } From 4f6ef7a4dd24bf603fd75ef33f01f67b04ce322f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Tue, 7 Mar 2023 13:40:37 +0100 Subject: [PATCH 3/9] fix fixed format --- server/src/language/parser.js | 105 +++++++++++++++++----------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/server/src/language/parser.js b/server/src/language/parser.js index 1c94ed86..0a3b0687 100644 --- a/server/src/language/parser.js +++ b/server/src/language/parser.js @@ -382,7 +382,7 @@ export default class Parser { // We don't want to waste precious time parsing all C specs, so we make sure it's got // BEGSR or ENDSR in it first. const upperLine = line.toUpperCase(); - if ([`BEGSR`, `ENDSR`].some(v => upperLine.includes(v)) === false) { + if ([`BEGSR`, `ENDSR`, `CURSOR`].some(v => upperLine.includes(v)) === false) { continue; } } @@ -802,8 +802,8 @@ export default class Parser { scope.cursor.push(currentCursor); currentCursor = undefined; } - break; } + break; case `///`: docs = !docs; @@ -961,37 +961,62 @@ export default class Parser { break; case `C`: - const cSpec = parseCLine(line); - - potentialName = cSpec.factor1; - - switch (cSpec.opcode) { - case `BEGSR`: - if (!scope.subroutines.find(sub => sub.name && sub.name.toUpperCase() === potentialName)) { - currentItem = new Declaration(`subroutine`); - currentItem.name = potentialName; - currentItem.keywords = [`Subroutine`]; - - currentItem.position = { + if (line.substring(5, 7) === `C+`) { + pieces = line.split(`;`); + parts = pieces[0].toUpperCase().split(` `).filter(piece => piece !== ``); + const indexDeclare = parts.findIndex(el => el === `DECLARE`); + const indexCursor = parts.findIndex(el => el === `CURSOR`); + let indexCursorName = 0; + if (indexCursor-1 == indexDeclare+1) { + indexCursorName = indexCursor-1; + } + if (currentCursor === undefined + && indexDeclare >=0 + && indexCursor >= 0) { + currentCursor = new Declaration(`cursor`); + currentCursor.name = parts[indexCursorName]; + currentCursor.position = { path: file, line: lineNumber }; - - currentItem.range = { - start: lineNumber, - end: lineNumber - }; - - currentDescription = []; + currentCursor.keywords.push(`CURSOR`); + + scope.cursor.push(currentCursor); + currentCursor = undefined; } - break; - case `ENDSR`: - if (currentItem && currentItem.type === `subroutine`) { - currentItem.range.end = lineNumber; - scope.subroutines.push(currentItem); - resetDefinition = true; + } else { + const cSpec = parseCLine(line); + + potentialName = cSpec.factor1; + + switch (cSpec.opcode) { + case `BEGSR`: + if (!scope.subroutines.find(sub => sub.name && sub.name.toUpperCase() === potentialName)) { + currentItem = new Declaration(`subroutine`); + currentItem.name = potentialName; + currentItem.keywords = [`Subroutine`]; + + currentItem.position = { + path: file, + line: lineNumber + }; + + currentItem.range = { + start: lineNumber, + end: lineNumber + }; + + currentDescription = []; + } + break; + case `ENDSR`: + if (currentItem && currentItem.type === `subroutine`) { + currentItem.range.end = lineNumber; + scope.subroutines.push(currentItem); + resetDefinition = true; + } + break; } - break; } break; @@ -1213,30 +1238,6 @@ export default class Parser { } break; - case `EXEC`: - if (parts[1] === `SQL`) { - const indexDeclare = parts.findIndex(el => el === `DECLARE`); - const indexCursor = parts.findIndex(el => el === `CURSOR`); - let indexCursorName = 0; - if (indexCursor-1 == indexDeclare+1) { - indexCursorName = indexCursor-1; - } - if (currentCursor === undefined - && indexDeclare >=0 - && indexCursor >= 0) { - currentCursor = new Declaration(`cursor`); - currentCursor.name = parts[indexCursorName]; - currentCursor.position = { - path: file, - line: statementStartingLine - }; - currentCursor.keywords.push(`CURSOR`); - - scope.cursor.push(currentCursor); - currentCursor = undefined; - } - break; - } } } From e1a28fd9f3dc9494531983def9686b4f79872ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Wed, 8 Mar 2023 08:46:27 +0100 Subject: [PATCH 4/9] test cases --- tests/suite/directives.js | 28 ++++++++++++++++++++++++++++ tests/suite/fixed.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tests/suite/directives.js b/tests/suite/directives.js index 657d4faa..e7571a3c 100644 --- a/tests/suite/directives.js +++ b/tests/suite/directives.js @@ -506,5 +506,33 @@ module.exports = { const someDs = cache.find(`someDs`); assert.strictEqual(someDs.keyword[`BASED`], undefined); + }, + + exec_sql: async () => { + const lines = [ + `**FREE`, + ``, + `EXEC SQL`, + ` select count(*) into :NB_LIG`, + ` from DEPARTMENT`, + ` where DEPTNAME = 'A'`, + `EXEC SQL`, + ` DECLARE C1 CURSOR FOR`, + ` select *`, + ` from DEPARTMENT`, + ` where DEPTNAME = 'A';`, + `exec sql`, + ` OPEN C1`, + `exec sql`, + ` Fetch C1 INTO :DEPARTMENT`, + `exec sql`, + ` CLOSE C1`, + ].join(`\n`); + + const parser = parserSetup(); + const cache = await parser.getDocs(uri, lines); + + assert.strictEqual(cache.cursor.length, 1); + assert.strictEqual(cache.cursor[0].name, `C1`); } } \ No newline at end of file diff --git a/tests/suite/fixed.js b/tests/suite/fixed.js index 36266937..deb0bc94 100644 --- a/tests/suite/fixed.js +++ b/tests/suite/fixed.js @@ -1040,4 +1040,35 @@ exports.ctl_opt_fixed = async () => { assert.strictEqual(cache.keyword[`FTRANS`], `*SRC`); assert.strictEqual(cache.keyword[`DATFMT`], `*MDY/`); assert.strictEqual(cache.keyword[`COPYRIGHT`], `'(C) Copyright ABC Programming - 1995'`); +}; + +exports.fixedfree2 = async () => { + const lines = [ + ` C/exec sql`, + ` C+ select count(*) into :NB_LIG`, + ` C+ from DEPARTMENT`, + ` C+ where DEPTNAME = 'A'`, + ` C/end-exec`, + ` C/exec sql`, + ` C+ DECLARE C1 CURSOR FOR`, + ` C+ select *`, + ` C+ from DEPARTMENT`, + ` C+ where DEPTNAME = 'A'`, + ` C/end-exec`, + ` C/exec sql`, + ` C+ OPEN C1`, + ` C/end-exec`, + ` C/exec sql`, + ` C+ Fetch C1 INTO :DEPARTMENT`, + ` C/end-exec`, + ` C/exec sql`, + ` C+ CLOSE C1`, + ` C/end-exec` + ].join(`\n`); + + const parser = parserSetup(); + const cache = await parser.getDocs(uri, lines); + + assert.strictEqual(cache.cursor.length, 1); + assert.strictEqual(cache.cursor[0].name, `C1`); }; \ No newline at end of file From f6402c377fd46d5d1dc55029636828174ff4332e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Tue, 14 Mar 2023 15:10:53 +0100 Subject: [PATCH 5/9] Fix(cache) --- server/src/language/models/cache.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/src/language/models/cache.js b/server/src/language/models/cache.js index 0b3ea7d4..72fdac59 100644 --- a/server/src/language/models/cache.js +++ b/server/src/language/models/cache.js @@ -62,7 +62,8 @@ export default class Cache { files: [...this.files, ...second.files], structs: [...this.structs, ...second.structs], constants: [...this.constants, ...second.constants], - indicators: [...this.indicators, ...second.indicators] + indicators: [...this.indicators, ...second.indicators], + cursor: [...this.cursor, ...second.cursor] }); } else { return this; @@ -83,6 +84,7 @@ export default class Cache { ...this.subroutines.map(def => def.name), ...this.variables.map(def => def.name), ...this.structs.map(def => def.name), + ...this.cursor.map(def => def.name), ].filter(name => name); } @@ -97,7 +99,8 @@ export default class Cache { this.structs.filter(d => d.position.path === fsPath).pop(), this.variables.filter(d => d.position.path === fsPath).pop(), this.constants.filter(d => d.position.path === fsPath).pop(), - this.files.filter(d => d.position.path === fsPath).pop() + this.files.filter(d => d.position.path === fsPath).pop(), + this.cursor.filter(d => d.position.path === fsPath).pop() ].filter(d => d !== undefined); const lines = lasts.map(d => d.range && d.range.end ? d.range.end : d.position.line).sort((a, b) => b - a); @@ -124,6 +127,7 @@ export default class Cache { ...this.subroutines.filter(def => def.name.toUpperCase() === name), ...this.variables.filter(def => def.name.toUpperCase() === name), ...this.indicators.filter(def => def.name.toUpperCase() === name), + ...this.cursor.filter(def => def.name.toUpperCase() === name), ]; if (allStructs.length > 0 && possibles.length === 0) { @@ -140,7 +144,7 @@ export default class Cache { } clearReferences() { - [...this.parameters, ...this.constants, ...this.files, ...this.procedures, ...this.subroutines, ...this.variables, ...this.structs].forEach(def => { + [...this.parameters, ...this.constants, ...this.files, ...this.procedures, ...this.subroutines, ...this.variables, ...this.structs, ...this.cursor].forEach(def => { def.references = []; }); From e0191733c39069608f45d59c28b1545ff146424a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Mon, 20 Mar 2023 13:16:01 +0100 Subject: [PATCH 6/9] Fix: Linter test --- tests/suite/linter.js | 50 ++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/tests/suite/linter.js b/tests/suite/linter.js index f0dec497..f65547f0 100644 --- a/tests/suite/linter.js +++ b/tests/suite/linter.js @@ -2426,20 +2426,46 @@ exports.linter34 = async () => { SQLHostVarCheck: true }, cache); - assert.strictEqual(errors.length, 1); + assert.strictEqual(errors.length, 3); - assert.deepStrictEqual(errors[0], { - type: `SQLHostVarCheck`, - range: new Range( - new Position(7, 0), - new Position(10, 28), - ), - offset: { - position: 117, - end: 124 + assert.deepStrictEqual(errors, [ + { + range: new Range( + new Position(7, 0), + new Position(10, 28), + ), + offset: { + position: 17, + end: 24 + }, + type: `SQLHostVarCheck`, + newValue: `:empCurA` }, - newValue: `:Deptnum` - }); + { + type: `SQLHostVarCheck`, + newValue: `:Deptnum`, + range: new Range( + new Position(7, 0), + new Position(10, 28), + ), + offset: { + position: 117, + end: 124 + }, + }, + { + range: new Range( + new Position(12, 0), + new Position(16, 29), + ), + offset: { + position: 17, + end: 24 + }, + type: `SQLHostVarCheck`, + newValue: `:empCurB` + } + ]); } exports.linter35 = async () => { From af0b9a02249f279878e4724c8cc6915a229f3247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Thu, 18 May 2023 15:27:28 +0200 Subject: [PATCH 7/9] Fix: linter + SymbolKind --- server/src/language/models/cache.js | 2 +- server/src/language/parser.js | 134 ++++++++++++------------ server/src/providers/documentSymbols.ts | 4 +- 3 files changed, 69 insertions(+), 71 deletions(-) diff --git a/server/src/language/models/cache.js b/server/src/language/models/cache.js index 72fdac59..a7470b35 100644 --- a/server/src/language/models/cache.js +++ b/server/src/language/models/cache.js @@ -44,7 +44,7 @@ export default class Cache { this.includes = cache.includes || []; /** @type {Declaration[]} */ - this.cursor = cache.cursor || []; + this.cursors = cache.cursors || []; } diff --git a/server/src/language/parser.js b/server/src/language/parser.js index 0a3b0687..9df9bc84 100644 --- a/server/src/language/parser.js +++ b/server/src/language/parser.js @@ -47,8 +47,8 @@ export default class Parser { } /** - * @param {includeFilePromise} promise - */ + * @param {includeFilePromise} promise + */ setIncludeFileFetch(promise) { this.includeFileFetch = promise; } @@ -121,9 +121,9 @@ export default class Parser { } /** - * @param {string} line - * @returns {string|undefined} - */ + * @param {string} line + * @returns {string|undefined} + */ static getIncludeFromDirective(line) { const linePieces = line.trim().split(` `); const copyIndex = linePieces.findIndex(piece => { @@ -132,8 +132,8 @@ export default class Parser { return (pieceUpper.includes(`/COPY`) || pieceUpper.includes(`/INCLUDE`)); }); - if (copyIndex >= 0 && linePieces[copyIndex+1]) { - return linePieces[copyIndex+1]; + if (copyIndex >= 0 && linePieces[copyIndex + 1]) { + return linePieces[copyIndex + 1]; } } @@ -143,7 +143,7 @@ export default class Parser { * @param {{withIncludes?: boolean, ignoreCache?: boolean}} options * @returns {Promise} */ - async getDocs(workingUri, content, options = {withIncludes: true}) { + async getDocs(workingUri, content, options = { withIncludes: true }) { const existingCache = this.getParsedCache(workingUri); if (options.ignoreCache !== true && existingCache) { return existingCache; @@ -194,12 +194,12 @@ export default class Parser { let objectName = defaultName; const extObjKeywords = [`EXTFILE`]; const extObjKeywordsDesc = [`EXTDESC`]; - + // Check for external object extObjKeywords.forEach(keyword => { const keywordValue = keywords.find(part => part.startsWith(`${keyword}(`) && part.endsWith(`)`)); if (keywordValue) { - objectName = keywordValue.substring(keyword.length+1, keywordValue.length - 1).toUpperCase(); + objectName = keywordValue.substring(keyword.length + 1, keywordValue.length - 1).toUpperCase(); if (objectName.startsWith(`'`) && objectName.endsWith(`'`)) { objectName = objectName.substring(1, objectName.length - 1); @@ -207,12 +207,12 @@ export default class Parser { } }); - if(objectName === `*EXTDESC`){ + if (objectName === `*EXTDESC`) { // Check for external object extObjKeywordsDesc.forEach(keyword => { const keywordValue = keywords.find(part => part.startsWith(`${keyword}(`) && part.endsWith(`)`)); if (keywordValue) { - objectName = keywordValue.substring(keyword.length+1, keywordValue.length - 1).toUpperCase(); + objectName = keywordValue.substring(keyword.length + 1, keywordValue.length - 1).toUpperCase(); if (objectName.startsWith(`'`) && objectName.endsWith(`'`)) { objectName = objectName.substring(1, objectName.length - 1); @@ -234,7 +234,7 @@ export default class Parser { for (const tag of tags) { const keyword = ds.keywords.find(keyword => keyword.startsWith(`${tag}(`) && keyword.endsWith(`)`)); if (keyword) { - let keywordValue = keyword.substring(tag.length+1, keyword.length - 1).toUpperCase(); + let keywordValue = keyword.substring(tag.length + 1, keyword.length - 1).toUpperCase(); if (keywordValue.includes(`:`)) { const parms = keywordValue.split(`:`).filter(part => part.trim().startsWith(`*`) === false); @@ -278,7 +278,7 @@ export default class Parser { const valuePointer = scopes[i].structs.find(struct => struct.name.toUpperCase() === keywordValue); if (valuePointer) { ds.subItems = valuePointer.subItems; - + // We need to add qualified as it is qualified by default. if (!ds.keywords.includes(`QUALIFIED`)) ds.keywords.push(`QUALIFIED`); @@ -291,7 +291,7 @@ export default class Parser { }; if (options.withIncludes && this.includeFileFetch) { - //First loop is for copy/include statements + //First loop is for copy/include statements for (let i = baseLines.length - 1; i >= 0; i--) { let line = baseLines[i].trim(); //Paths are case insensitive so it's okay if (line === ``) continue; @@ -306,8 +306,8 @@ export default class Parser { return (pieceUpper.includes(`/COPY`) || pieceUpper.includes(`/INCLUDE`)); }); - if (copyIndex >= 0 && pieces[copyIndex+1]) { - const include = await this.includeFileFetch(workingUri, pieces[copyIndex+1]); + if (copyIndex >= 0 && pieces[copyIndex + 1]) { + const include = await this.includeFileFetch(workingUri, pieces[copyIndex + 1]); if (include.found) { files[include.uri] = include.lines; scopes[0].includes.push({ @@ -371,7 +371,7 @@ export default class Parser { lineIsFree = true; } else { if (spec === ` `) { - //Clear out stupid comments + //Clear out stupid comments line = line.substring(7); lineIsFree = true; @@ -397,7 +397,7 @@ export default class Parser { pieces = []; parts = []; - + if (isFullyFree || lineIsFree) { // Free format! line = line.trim(); @@ -453,7 +453,7 @@ export default class Parser { } else if (!line.endsWith(`;`)) { currentStatement = (currentStatement || ``) + line.trim(); - if (currentStatement.endsWith(`-`)) + if (currentStatement.endsWith(`-`)) currentStatement = currentStatement.substring(0, currentStatement.length - 1); else currentStatement += ` `; @@ -488,8 +488,8 @@ export default class Parser { prefix = element.trim().substring(7, element.indexOf(`)`)) return true; } - }); - + }); + const recordFormats = await this.fetchTable(objectName, parts.length.toString(), parts.includes(`ALIAS`)); if (recordFormats.length > 0) { @@ -504,7 +504,7 @@ export default class Parser { recordFormat.subItems.forEach(subItem => { // We put the prefix here because in 'fetchTable' we use cached version. So if the user change the prefix, it will not refresh the variable name - if(prefix) { + if (prefix) { subItem.name = prefix + subItem.name; } subItem.position = currentItem.position; @@ -612,7 +612,7 @@ export default class Parser { dsScopes[dsScopes.length - 2].subItems.push(dsScopes.pop()); } break; - + case `DCL-PR`: if (currentItem === undefined) { if (parts.length > 1) { @@ -663,11 +663,11 @@ export default class Parser { resetDefinition = true; } break; - + case `DCL-PROC`: if (parts.length > 1) { - //We can overwrite it.. it might have been a PR before. - // eslint-disable-next-line no-case-declarations + //We can overwrite it.. it might have been a PR before. + // eslint-disable-next-line no-case-declarations const existingProc = scope.procedures.findIndex(proc => proc.name && proc.name.toUpperCase() === parts[1]); // We found the PR... so we can overwrite it @@ -712,7 +712,7 @@ export default class Parser { if (currentItem) { // Indicates that the PI starts and ends on the same line - if (endInline >= 0) { + if (endInline >= 0) { parts.splice(endInline, 1); currentItem.readParms = false; resetDefinition = true; @@ -771,7 +771,7 @@ export default class Parser { } } break; - + case `ENDSR`: if (currentItem && currentItem.type === `subroutine`) { currentItem.range.end = statementStartingLine; @@ -785,21 +785,20 @@ export default class Parser { const indexDeclare = parts.findIndex(el => el === `DECLARE`); const indexCursor = parts.findIndex(el => el === `CURSOR`); let indexCursorName = 0; - if (indexCursor-1 == indexDeclare+1) { - indexCursorName = indexCursor-1; + if (indexCursor - 1 == indexDeclare + 1) { + indexCursorName = indexCursor - 1; } - if (currentCursor === undefined - && indexDeclare >=0 - && indexCursor >= 0) { + if (currentCursor === undefined + && indexDeclare >= 0 + && indexCursor >= 0) { currentCursor = new Declaration(`cursor`); currentCursor.name = parts[indexCursorName]; currentCursor.position = { path: file, line: statementStartingLine }; - currentCursor.keywords.push(`CURSOR`); - scope.cursor.push(currentCursor); + scope.cursors.push(currentCursor); currentCursor = undefined; } } @@ -807,7 +806,7 @@ export default class Parser { case `///`: docs = !docs; - + // When enabled if (docs === true) { currentTitle = undefined; @@ -842,7 +841,7 @@ export default class Parser { } } else { - //Do nothing because it's a regular comment + //Do nothing because it's a regular comment } } else { @@ -861,7 +860,7 @@ export default class Parser { } currentSub = new Declaration(`subitem`); - currentSub.name = (parts[0] === `*N` ? `parm${currentItem.subItems.length+1}` : partsLower[0]) ; + currentSub.name = (parts[0] === `*N` ? `parm${currentItem.subItems.length + 1}` : partsLower[0]); currentSub.keywords = parts.slice(1); currentSub.position = { @@ -919,15 +918,15 @@ export default class Parser { path: file, line: lineNumber }; - - let prefix = ``; + + let prefix = ``; fSpec.keywords.find(element => { if (element.toUpperCase().includes(`PREFIX`)) { prefix = element.substring(7, element.indexOf(`)`)) return true; } - }); + }); const recordFormats = await this.fetchTable(potentialName, line.length.toString(), fSpec.keywords.includes(`ALIAS`)); @@ -942,10 +941,10 @@ export default class Parser { recordFormat.position = currentItem.position; recordFormat.subItems.forEach(subItem => { - // We put the prefix here because in 'fetchTable' we use cached version. So if the user change the prefix, it will not refresh the variable name - if(prefix) { + // We put the prefix here because in 'fetchTable' we use cached version. So if the user change the prefix, it will not refresh the variable name + if (prefix) { subItem.name = prefix.toUpperCase() + subItem.name; - } + } subItem.position = currentItem.position; }); }); @@ -956,7 +955,7 @@ export default class Parser { scope.files.push(currentItem); } - + resetDefinition = true; break; @@ -967,21 +966,20 @@ export default class Parser { const indexDeclare = parts.findIndex(el => el === `DECLARE`); const indexCursor = parts.findIndex(el => el === `CURSOR`); let indexCursorName = 0; - if (indexCursor-1 == indexDeclare+1) { - indexCursorName = indexCursor-1; + if (indexCursor - 1 == indexDeclare + 1) { + indexCursorName = indexCursor - 1; } - if (currentCursor === undefined - && indexDeclare >=0 - && indexCursor >= 0) { + if (currentCursor === undefined + && indexDeclare >= 0 + && indexCursor >= 0) { currentCursor = new Declaration(`cursor`); currentCursor.name = parts[indexCursorName]; currentCursor.position = { path: file, line: lineNumber }; - currentCursor.keywords.push(`CURSOR`); - scope.cursor.push(currentCursor); + scope.cursors.push(currentCursor); currentCursor = undefined; } } else { @@ -995,17 +993,17 @@ export default class Parser { currentItem = new Declaration(`subroutine`); currentItem.name = potentialName; currentItem.keywords = [`Subroutine`]; - + currentItem.position = { path: file, line: lineNumber }; - + currentItem.range = { start: lineNumber, end: lineNumber }; - + currentDescription = []; } break; @@ -1020,7 +1018,7 @@ export default class Parser { } break; - + case `P`: const pSpec = parsePLine(line); @@ -1093,13 +1091,13 @@ export default class Parser { currentItem = new Declaration(`constant`); currentItem.name = potentialName || `*N`; currentItem.keywords = [...dSpec.keywords]; - + // TODO: line number might be different with ...? currentItem.position = { path: file, line: lineNumber - (potentialNameUsed ? 1 : 0) // Account that name is on line before }; - + scope.constants.push(currentItem); resetDefinition = true; break; @@ -1146,7 +1144,7 @@ export default class Parser { currentItem = new Declaration(`procedure`); currentItem.name = potentialName || `*N`; currentItem.keywords = [getPrettyType(dSpec), ...dSpec.keywords]; - + currentItem.position = { path: file, line: lineNumber - (potentialNameUsed ? 1 : 0) // Account that name is on line before @@ -1156,7 +1154,7 @@ export default class Parser { start: currentItem.position.line, end: currentItem.position.line }; - + currentGroup = `procedures`; scope.procedures.push(currentItem); currentDescription = []; @@ -1190,7 +1188,7 @@ export default class Parser { validScope = scopes[i]; if (validScope[currentGroup].length > 0) break; } - + currentItem = validScope[currentGroup][validScope[currentGroup].length - 1]; break; } @@ -1200,7 +1198,7 @@ export default class Parser { // This happens when it's a blank parm. if (potentialName === `` && (dSpec.type || dSpec.len)) - potentialName = (potentialName === `` ? `parm${currentItem.subItems.length+1}` : potentialName); + potentialName = (potentialName === `` ? `parm${currentItem.subItems.length + 1}` : potentialName); if (potentialName) { currentSub = new Declaration(`subitem`); @@ -1233,7 +1231,7 @@ export default class Parser { } break; } - + potentialName = undefined; } break; @@ -1249,7 +1247,7 @@ export default class Parser { potentialName = undefined; potentialNameUsed = false; - + currentItem = undefined; currentTitle = undefined; currentDescription = []; @@ -1263,7 +1261,7 @@ export default class Parser { scopes[0].keyword = Parser.expandKeywords(keywords); } - scopes[0].fixProcedures(); + scopes[0].fixProcedures(); const parsedData = scopes[0]; @@ -1284,8 +1282,8 @@ export default class Parser { for (let i = 0; i < keywordParts.length; i++) { if (keywordParts[i].value) { - if (keywordParts[i+1] && keywordParts[i+1].type === `block`) { - keyvalues[keywordParts[i].value.toUpperCase()] = keywordParts[i+1].block.map(part => part.value).join(``); + if (keywordParts[i + 1] && keywordParts[i + 1].type === `block`) { + keyvalues[keywordParts[i].value.toUpperCase()] = keywordParts[i + 1].block.map(part => part.value).join(``); i++; // Skip one for the block. } else { keyvalues[keywordParts[i].value.toUpperCase()] = true; diff --git a/server/src/providers/documentSymbols.ts b/server/src/providers/documentSymbols.ts index 6ec583c3..32a3ab48 100644 --- a/server/src/providers/documentSymbols.ts +++ b/server/src/providers/documentSymbols.ts @@ -76,12 +76,12 @@ export default async function documentSymbolProvider(handler: DocumentSymbolPara Range.create(def.position.line, 0, def.position.line, 0) )), - ...scope.cursor + ...scope.cursors .filter(cursor => cursor.position && cursor.position.path === currentPath) .map(def => DocumentSymbol.create( def.name, def.keywords.join(` `).trim(), - SymbolKind.Constant, + SymbolKind.Interface, Range.create(def.position.line, 0, def.position.line, 0), Range.create(def.position.line, 0, def.position.line, 0) )) From 6729ff0590dee68cd73187b3fbe8eff9171e57b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Sun, 21 May 2023 10:48:56 +0200 Subject: [PATCH 8/9] Fix: unit tests --- server/src/language/models/cache.js | 10 +++++----- tests/suite/directives.js | 4 ++-- tests/suite/fixed.js | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/server/src/language/models/cache.js b/server/src/language/models/cache.js index a7470b35..a9498bdd 100644 --- a/server/src/language/models/cache.js +++ b/server/src/language/models/cache.js @@ -63,7 +63,7 @@ export default class Cache { structs: [...this.structs, ...second.structs], constants: [...this.constants, ...second.constants], indicators: [...this.indicators, ...second.indicators], - cursor: [...this.cursor, ...second.cursor] + cursors: [...this.cursors, ...second.cursors] }); } else { return this; @@ -84,7 +84,7 @@ export default class Cache { ...this.subroutines.map(def => def.name), ...this.variables.map(def => def.name), ...this.structs.map(def => def.name), - ...this.cursor.map(def => def.name), + ...this.cursors.map(def => def.name), ].filter(name => name); } @@ -100,7 +100,7 @@ export default class Cache { this.variables.filter(d => d.position.path === fsPath).pop(), this.constants.filter(d => d.position.path === fsPath).pop(), this.files.filter(d => d.position.path === fsPath).pop(), - this.cursor.filter(d => d.position.path === fsPath).pop() + this.cursors.filter(d => d.position.path === fsPath).pop() ].filter(d => d !== undefined); const lines = lasts.map(d => d.range && d.range.end ? d.range.end : d.position.line).sort((a, b) => b - a); @@ -127,7 +127,7 @@ export default class Cache { ...this.subroutines.filter(def => def.name.toUpperCase() === name), ...this.variables.filter(def => def.name.toUpperCase() === name), ...this.indicators.filter(def => def.name.toUpperCase() === name), - ...this.cursor.filter(def => def.name.toUpperCase() === name), + ...this.cursors.filter(def => def.name.toUpperCase() === name), ]; if (allStructs.length > 0 && possibles.length === 0) { @@ -144,7 +144,7 @@ export default class Cache { } clearReferences() { - [...this.parameters, ...this.constants, ...this.files, ...this.procedures, ...this.subroutines, ...this.variables, ...this.structs, ...this.cursor].forEach(def => { + [...this.parameters, ...this.constants, ...this.files, ...this.procedures, ...this.subroutines, ...this.variables, ...this.structs, ...this.cursors].forEach(def => { def.references = []; }); diff --git a/tests/suite/directives.js b/tests/suite/directives.js index e7571a3c..a3d30266 100644 --- a/tests/suite/directives.js +++ b/tests/suite/directives.js @@ -532,7 +532,7 @@ module.exports = { const parser = parserSetup(); const cache = await parser.getDocs(uri, lines); - assert.strictEqual(cache.cursor.length, 1); - assert.strictEqual(cache.cursor[0].name, `C1`); + assert.strictEqual(cache.cursors.length, 1); + assert.strictEqual(cache.cursors[0].name, `C1`); } } \ No newline at end of file diff --git a/tests/suite/fixed.js b/tests/suite/fixed.js index deb0bc94..8258d274 100644 --- a/tests/suite/fixed.js +++ b/tests/suite/fixed.js @@ -1069,6 +1069,6 @@ exports.fixedfree2 = async () => { const parser = parserSetup(); const cache = await parser.getDocs(uri, lines); - assert.strictEqual(cache.cursor.length, 1); - assert.strictEqual(cache.cursor[0].name, `C1`); + assert.strictEqual(cache.cursors.length, 1); + assert.strictEqual(cache.cursors[0].name, `C1`); }; \ No newline at end of file From 638ed80d68fed56b57a800b539388605f89210f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BOURREAU?= Date: Tue, 17 Oct 2023 13:55:29 +0200 Subject: [PATCH 9/9] add type cursors --- language/parserTypes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/language/parserTypes.ts b/language/parserTypes.ts index 0b86b1dc..2e9e21e1 100644 --- a/language/parserTypes.ts +++ b/language/parserTypes.ts @@ -22,6 +22,7 @@ export interface CacheProps { sqlReferences?: Declaration[]; indicators?: Declaration[]; includes?: IncludeStatement[]; + cursors?: Declaration[]; } export interface Rules {