From 1e26562df9a53ae3935f656865e691b05c9be222 Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Thu, 28 Dec 2023 23:35:05 -0700 Subject: [PATCH 1/8] awakening latest Spin2 compiler support (v43) - add override coloring for byte(), word(), and long() - add version recognition at top of file {Spin2_v43} - add lstring() support if file says ver 43 - add lstring override coloring if ver 43 --- spin2/CHANGELOG.md | 11 ++++ .../TEST_LANG_SERVER/spin2/231112-fixes.spin2 | 2 + .../TEST_LANG_SERVER/spin2/231116-fixes.spin2 | 6 +- .../spin2/debug_coloring_examples.spin2 | 4 +- spin2/server/src/parser/spin.common.ts | 26 ++++++++ .../parser/spin2.documentSemanticParser.ts | 66 +++++++++++++++---- spin2/server/src/parser/spin2.utils.ts | 59 +++++++++++++++-- 7 files changed, 152 insertions(+), 22 deletions(-) diff --git a/spin2/CHANGELOG.md b/spin2/CHANGELOG.md index 7c2cafe..3f7867d 100644 --- a/spin2/CHANGELOG.md +++ b/spin2/CHANGELOG.md @@ -18,6 +18,17 @@ Possible next additions: - Add new-file templates as Snippets - Add additional Snippets as the community identifies them +## [2.2.11] 2023-12-?? + +Update P2 Only + +- Add recognition of "auto" on debug scope display declarations +- Add Semantic highlight color change for byte(), word(), and long() method overrides +- Add recognition of byte(), word(), and long() method names to provide method vs. storage type hover text +- Add recognition of {Spin2_v4XX} format Spin Language Requirement directive +- Emit any languge directive in use to generated interface documentation +- Add support for lstring() when {Spin2_v43} is specified + ## [2.2.10] 2023-12-24 Update P1 and P2 diff --git a/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 index 3fa6359..ac0aff7 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 @@ -1,5 +1,7 @@ ' -------------------------------------------------------------------------------------------------- ' bitfield highlight issues Ln#32-34 +' {Spin2_v43} + VAR byte header_buf[$100] diff --git a/spin2/TEST_LANG_SERVER/spin2/231116-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231116-fixes.spin2 index d089a2e..340b218 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231116-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231116-fixes.spin2 @@ -25,9 +25,9 @@ VAR stack[30], clocks, sample, states PUB go() | i, x, y, m, n ' lines 172-... debug(`plot p title 'Five Buttons on a Single Pin' size 430 600 backcolor white update) - debug(`p set `(sch_x,sch_y-60) blue text 'clocks: `(clocks)) ' <--- untermnated string not highlighted - debug(`p set `(sch_x,sch_y-80) text 'sample: `(sample)) - debug(`p set `(sch_x,sch_y-100) text 'states: `ubin_byte_(n)) + debug(`p set `(sch_x,sch_y-60) blue text 'clocks: `(clocks)') ' <--- untermnated string not highlighted + debug(`p set `(sch_x,sch_y-80) text 'sample: `(sample)') + debug(`p set `(sch_x,sch_y-100) text 'states: `ubin_byte_(n)') debug(`p update `dly(20)) diff --git a/spin2/TEST_LANG_SERVER/spin2/debug_coloring_examples.spin2 b/spin2/TEST_LANG_SERVER/spin2/debug_coloring_examples.spin2 index 06d4c3f..fda09eb 100644 --- a/spin2/TEST_LANG_SERVER/spin2/debug_coloring_examples.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/debug_coloring_examples.spin2 @@ -5,8 +5,8 @@ PUB oscilloscope() | chanA, chanB, chanC '' TASK: display oscilloscope waveforms debug(`scope Scope1 pos 100 0 size 800 600 linesize 6) ' end of line comments - debug(`Scope1 'Analog Input' 0 3300 250 0 %1111 red) ' WARNING (if no comment here semantic highlight is BAD!!!) - debug(`Scope1 'Digital Output' 0 3300 250 300 %1111 green) ' end of line comments + debug(`Scope1 'Analog Input' 0 3300 250 0 %1111 red auto) ' WARNING (if no comment here semantic highlight is BAD!!!) + debug(`Scope1 'Digital Output' 0 3300 250 300 %1111 green auto) ' end of line comments debug(`Scope1 samples 16 trigger -1) debug(`term Term1 pos 914 71 size 30 1 textsize 20 color red) ' end of line comments debug(`term Term2 pos 914 0 size 30 1 textsize 20 color green) diff --git a/spin2/server/src/parser/spin.common.ts b/spin2/server/src/parser/spin.common.ts index 1e15fc9..5ac50ab 100644 --- a/spin2/server/src/parser/spin.common.ts +++ b/spin2/server/src/parser/spin.common.ts @@ -84,6 +84,32 @@ export function haveDebugLine(line: string, startsWith: boolean = false): boolea return startsWith ? debugStatementOpenStartRegEx.test(line) : debugStatementOpenRegEx.test(line); } +export function isMethodCall(line: string): boolean { + const methodOpenRegEx = /\s*\(/; // match zero or more whitespace before '(' + return methodOpenRegEx.test(line); +} + +export function containsSpinLanguageSpec(line: string): boolean { + // return T/F where T means {Spin2_v##} was found in given string + const languageVersionRegEx = /\{Spin2\_v[0-9][0-9]\}/i; // our version specification + return languageVersionRegEx.test(line); +} + +export function versionFromSpinLanguageSpec(line: string): number { + // return T/F where T means {Spin2_v##} was found in given string + let decodedVersion: number = 0; // return no version by default + if (containsSpinLanguageSpec(line)) { + const matchText: string = "{Spin2_v".toLowerCase(); + const verOffset: number = line.toLowerCase().indexOf(matchText); + if (verOffset != -1) { + const tens: number = parseInt(line.charAt(verOffset + matchText.length)); + const ones: number = parseInt(line.charAt(verOffset + matchText.length + 1)); + decodedVersion = tens * 10 + ones; + } + } + return decodedVersion; +} + export class SpinControlFlowTracker { private flowStatementStack: ICurrControlStatement[] = []; // nested statement tracking private flowLogEnabled: boolean = false; diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index 0c30dcf..db612b4 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -8,7 +8,17 @@ import { Context, ServerBehaviorConfiguration, EditorConfiguration } from "../co import { DocumentFindings, RememberedComment, eCommentType, RememberedToken, eBLockType, IParsedToken, eSeverity, eDefinitionType } from "./spin.semantic.findings"; import { Spin2ParseUtils } from "./spin2.utils"; import { isSpin1File } from "./lang.utils"; -import { eParseState, eDebugDisplayType, ContinuedLines, haveDebugLine, SpinControlFlowTracker, ICurrControlSpan } from "./spin.common"; +import { + eParseState, + eDebugDisplayType, + ContinuedLines, + haveDebugLine, + SpinControlFlowTracker, + ICurrControlSpan, + isMethodCall, + containsSpinLanguageSpec, + versionFromSpinLanguageSpec, +} from "./spin.common"; import { fileInDirExists } from "../files"; //import { PublishDiagnosticsNotification } from "vscode-languageserver"; import { ExtensionUtils } from "../parser/spin.extension.utils"; @@ -43,7 +53,7 @@ export class Spin2DocumentSemanticParser { private bLogStarted: boolean = false; // adjust following true/false to show specific parsing debug - private spin2DebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private spin2DebugLogEnabled: boolean = true; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showSpinCode: boolean = true; private showPreProc: boolean = true; private showCON: boolean = true; @@ -70,6 +80,7 @@ export class Spin2DocumentSemanticParser { private directory: string = ""; private bRecordTrailingComments: boolean = false; // initially, we don't generate tokens for trailing comments on lines + private bHuntingForVersion: boolean = true; // initially we re hunting for a {Spin2_v##} spec in file-top comments public constructor(protected readonly ctx: Context) { this.extensionUtils = new ExtensionUtils(ctx, this.spin2DebugLogEnabled); @@ -161,6 +172,12 @@ export class Spin2DocumentSemanticParser { const lineNbr: number = i + 1; const line: string = lines[i]; const trimmedLine: string = line.trim(); + if (this.bHuntingForVersion && containsSpinLanguageSpec(trimmedLine)) { + this.bHuntingForVersion = false; // done we found it + const newLangVersion: number = versionFromSpinLanguageSpec(trimmedLine); + this.parseUtils.setSpinVersion(newLangVersion); + this._logMessage(` -- found Spin2 version (${newLangVersion}), stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); + } const lineWOutInlineComments: string = this.parseUtils.getLineWithoutInlineComments(line); const bHaveLineToProcess: boolean = lineWOutInlineComments.length > 0; //this._logMessage(` -- Ln#${lineNbr} bHaveLineToProcess=(${bHaveLineToProcess}), lineWOutInlineComments=[${lineWOutInlineComments}](${lineWOutInlineComments.length})`); @@ -399,6 +416,11 @@ export class Spin2DocumentSemanticParser { } } + if (this.bHuntingForVersion) { + this.bHuntingForVersion = false; // done, we passed the file-top comments. we can no longer search + this._logMessage(` -- stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); + } + currState = sectionStatus.inProgressStatus; // record start of next block in code // NOTE: this causes end of prior block to be recorded @@ -2604,7 +2626,7 @@ export class Spin2DocumentSemanticParser { if (possibleName.charAt(0).match(/[a-zA-Z_]/) || (isDatPAsm && possibleName.charAt(0).match(/[a-zA-Z_\.]/))) { nameOffset = line.indexOf(possibleName, currentOffset); if (showDebug) { - this._logMessage(` -- possibleName=[${possibleName}]`); + this._logMessage(` -- DATval possibleName=[${possibleName}]`); } // does name contain a namespace reference? let possibleNameSet: string[] = [possibleName]; @@ -3929,7 +3951,7 @@ export class Spin2DocumentSemanticParser { } } if (referenceDetails != undefined) { - this._logSPIN(` -- SPIN RHS name=[${namePart}], ofs=(${nameOffset})`); + this._logSPIN(` -- SPIN Am RHS name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { line: symbolPosition.line, startCharacter: symbolPosition.character, @@ -4131,7 +4153,7 @@ export class Spin2DocumentSemanticParser { let nameOffset: number = 0; currNameLength = possibleName.length; if (possibleName.charAt(0).match(/[a-zA-Z_]/)) { - this._logSPIN(` -- possibleName=[${possibleName}]`); + this._logSPIN(` -- Spinm possibleName=[${possibleName}]`); offsetInNonStringRHS = nonStringAssignmentRHSStr.indexOf(possibleName, offsetInNonStringRHS); //nameOffset = offsetInNonStringRHS + assignmentStringOffset; symbolPosition = multiLineSet.locateSymbol(possibleName, currSingleLineOffset); @@ -4181,7 +4203,7 @@ export class Spin2DocumentSemanticParser { } } if (referenceDetails != undefined) { - this._logSPIN(` -- SPIN RHS name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); + this._logSPIN(` -- SPIN Bm RHS name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { line: symbolPosition.line, startCharacter: symbolPosition.character, @@ -4190,7 +4212,18 @@ export class Spin2DocumentSemanticParser { ptTokenModifiers: referenceDetails.modifiers, }); } else { - if (this.parseUtils.isFloatConversion(namePart) && (nonStringAssignmentRHSStr.indexOf(namePart + "(") == -1 || nonStringAssignmentRHSStr.indexOf(namePart + "()") != -1)) { + const methodFollowString: string = multiLineSet.lineAt(symbolPosition.line).substring(nameOffset + namePart.length); + this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + if (this.parseUtils.isMethodOverride(namePart, this.parseUtils.selectedSpinVersion()) && isMethodCall(methodFollowString)) { + this._logSPIN(` -- override with method coloring name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); + this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { + line: symbolPosition.line, + startCharacter: symbolPosition.character, + length: namePart.length, + ptTokenType: "function", + ptTokenModifiers: ["support"], + }); + } else if (this.parseUtils.isFloatConversion(namePart) && (nonStringAssignmentRHSStr.indexOf(namePart + "(") == -1 || nonStringAssignmentRHSStr.indexOf(namePart + "()") != -1)) { this._logSPIN(` -- SPIN MISSING PARENS name=[${namePart}]`); this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { line: symbolPosition.line, @@ -4507,7 +4540,7 @@ export class Spin2DocumentSemanticParser { } } if (referenceDetails != undefined) { - this._logSPIN(" -- SPIN RHS name=[" + namePart + "], ofs=(" + nameOffset + ")"); + this._logSPIN(` -- SPIN A RHS name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: nameOffset, @@ -4693,7 +4726,7 @@ export class Spin2DocumentSemanticParser { let nameOffset: number = 0; currNameLength = possibleName.length; if (possibleName.charAt(0).match(/[a-zA-Z_]/)) { - this._logSPIN(` -- possibleName=[${possibleName}]`); + this._logSPIN(` -- Spin possibleName=[${possibleName}]`); offsetInNonStringRHS = nonStringAssignmentRHSStr.indexOf(possibleName, offsetInNonStringRHS); nameOffset = offsetInNonStringRHS + assignmentStringOffset; let bHaveObjReference: boolean = this._isPossibleObjectReference(possibleName) ? this._reportObjectReference(possibleName, lineIdx, offsetInNonStringRHS, line, tokenSet) : false; @@ -4754,7 +4787,7 @@ export class Spin2DocumentSemanticParser { } } if (referenceDetails != undefined) { - this._logSPIN(` -- SPIN RHS name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); + this._logSPIN(` -- SPIN B RHS name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: nameOffset, @@ -4763,7 +4796,18 @@ export class Spin2DocumentSemanticParser { ptTokenModifiers: referenceDetails.modifiers, }); } else { - if (this.parseUtils.isFloatConversion(namePart) && (nonStringAssignmentRHSStr.indexOf(namePart + "(") == -1 || nonStringAssignmentRHSStr.indexOf(namePart + "()") != -1)) { + const methodFollowString: string = line.substring(nameOffset + namePart.length); + this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + if (this.parseUtils.isMethodOverride(namePart, this.parseUtils.selectedSpinVersion()) && isMethodCall(methodFollowString)) { + this._logSPIN(` -- override with method coloring name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); + this._recordToken(tokenSet, line, { + line: lineIdx, + startCharacter: nameOffset, + length: namePart.length, + ptTokenType: "function", + ptTokenModifiers: ["support"], + }); + } else if (this.parseUtils.isFloatConversion(namePart) && (nonStringAssignmentRHSStr.indexOf(namePart + "(") == -1 || nonStringAssignmentRHSStr.indexOf(namePart + "()") != -1)) { this._logSPIN(" -- SPIN MISSING PARENS name=[" + namePart + "]"); this._recordToken(tokenSet, line, { line: lineIdx, diff --git a/spin2/server/src/parser/spin2.utils.ts b/spin2/server/src/parser/spin2.utils.ts index 7eace8c..f3fc872 100644 --- a/spin2/server/src/parser/spin2.utils.ts +++ b/spin2/server/src/parser/spin2.utils.ts @@ -22,6 +22,7 @@ type TMethodTuple = readonly [signature: string, description: string, parameters export class Spin2ParseUtils { private utilsLogEnabled: boolean = true; private ctx: Context | undefined = undefined; + private languageVersion: number = 0; public enableLogging(ctx: Context, doEnable: boolean = true): void { this.utilsLogEnabled = doEnable; @@ -37,6 +38,22 @@ export class Spin2ParseUtils { } } + public setSpinVersion(requiredVersion: number): void { + if (requiredVersion >= 41 && requiredVersion != 42) { + this.languageVersion = requiredVersion; + } + } + + public selectedSpinVersion(): number { + const selectedVersion: number = this.languageVersion == 0 ? 41 : this.languageVersion; + return selectedVersion; + } + + public requestedSpinVersion(requiredVersion: number): boolean { + const supportedStatus: boolean = this.languageVersion >= requiredVersion ? true : false; + return supportedStatus; + } + public charsInsetCount(line: string, tabWidth: number): number { // count and return the number of columns of white space at start of line // NOTE: expands tabs appropriate to editor settings! @@ -1996,10 +2013,15 @@ export class Spin2ParseUtils { 'STRING("Text",13) : StringAddress', "Compose a zero-terminated string (quoted characters and values 1..255 allowed), return address of string

@param `listOfElements` - a comma separated list of elements to be built into a string (quoted characters and values 1..255 allowed)
@returns `StringAddress` - address of where string was placed in ram", ], - //lstring: ['LSTRING("Hello",0,"Terve",0) : StringAddress', "Compose a length-headed string (quoted characters and values 0..255), return address of string."], - //byte: ["BYTE($80,$09,$77,WORD $1234,LONG -1) : BytesAddress", "Compose a string of bytes, return address of string. WORD/LONG size overrides allowed."], - //word: ["WORD(1_000,10_000,50_000,LONG $12345678) : WordsAddress", "Compose a string of words, return address of string. BYTE/LONG size overrides allowed."], - //long: ["LONG(1e-6,1e-3,1.0,1e3,1e6,-50,BYTE $FF) : LongsAddress", "Compose a string of longs, return address of string. BYTE/WORD size overrides allowed."], + // new additions at v42 but cleaned up at v43 + byte: ["BYTE($80,$09,$77,WORD $1234,LONG -1) : BytesAddress", "Compose a string of bytes, return address of string. WORD/LONG size overrides allowed."], + word: ["WORD(1_000,10_000,50_000,LONG $12345678) : WordsAddress", "Compose a string of words, return address of string. BYTE/LONG size overrides allowed."], + long: ["LONG(1e-6,1e-3,1.0,1e3,1e6,-50,BYTE $FF) : LongsAddress", "Compose a string of longs, return address of string. BYTE/WORD size overrides allowed."], + }; + + private _tableSpinEnhancements_v43: { [Identifier: string]: string[] } = { + // NOTE: this does NOT support signature help! (paramaters are not highlighted for signature help due to variant forms for string() being allowed) + lstring: ['LSTRING("Hello",0,"Terve",0) : StringAddress', "Compose a length-headed string (quoted characters and values 0..255), return address of string."], }; private _tableSpinIndexValueMethods: { [Identifier: string]: string[] } = { @@ -2063,6 +2085,10 @@ export class Spin2ParseUtils { reservedStatus = nameKey in this._tableSpinIndexValueMethods; if (!reservedStatus) { reservedStatus = nameKey in this._tableSpinControlFlowMethods; + if (!reservedStatus && this.requestedSpinVersion(43)) { + // if {Spin2_v43} then also search this table + reservedStatus = nameKey in this._tableSpinEnhancements_v43; + } } } } @@ -2106,6 +2132,10 @@ export class Spin2ParseUtils { } else if (nameKey in this._tableSpinStringBuilder) { desiredDocText.category = "String Method"; protoWDescr = this._tableSpinStringBuilder[nameKey]; + } else if (this.requestedSpinVersion(43) && nameKey in this._tableSpinEnhancements_v43) { + // if {Spin2_v43} then also search this table + desiredDocText.category = "String Method"; + protoWDescr = this._tableSpinEnhancements_v43[nameKey]; } else if (nameKey in this._tableSpinIndexValueMethods) { desiredDocText.category = "Hub Method"; protoWDescr = this._tableSpinIndexValueMethods[nameKey]; @@ -2854,6 +2884,23 @@ export class Spin2ParseUtils { return returnStatus; } + public isMethodOverride(name: string, languageVersion: number): boolean { + // storage type override with method name: BYTE, WORD, LONG + let returnStatus: boolean = false; + if (name.length > 3) { + const checkType: string = name.toUpperCase(); + if (checkType == "BYTE" || checkType == "WORD" || checkType == "LONG") { + returnStatus = true; + } + if (!returnStatus && languageVersion == 43) { + if (checkType == "LSTRING") { + returnStatus = true; + } + } + } + return returnStatus; + } + public isStorageType(name: string): boolean { // storage type : (BYTE|WORD)FIT, BYTE, WORD, LONG let returnStatus: boolean = false; @@ -3067,14 +3114,14 @@ export class Spin2ParseUtils { // Debug Display: SCOPE declaration public isDebugScopeDeclarationParam(name: string): boolean { - const debugScopeDeclTypes: string[] = ["title", "pos", "size", "samples", "rate", "dotsize", "linesize", "textsize", "color", "hidexy"]; + const debugScopeDeclTypes: string[] = ["title", "pos", "size", "samples", "rate", "dotsize", "linesize", "textsize", "color", "hidexy", "auto"]; const bScopeDeclParamStatus: boolean = debugScopeDeclTypes.indexOf(name.toLowerCase()) != -1; return bScopeDeclParamStatus; } // Debug Display: SCOPE feed public isDebugScopeFeedParam(name: string): boolean { - const debugScopeFeedTypes: string[] = ["trigger", "holdoff", "samples", "clear", "save", "window", "close"]; + const debugScopeFeedTypes: string[] = ["trigger", "holdoff", "samples", "clear", "save", "window", "close", "auto"]; const bScopeFeedParamStatus: boolean = debugScopeFeedTypes.indexOf(name.toLowerCase()) != -1; return bScopeFeedParamStatus; } From 495d22b174f5be0d01bcfb2a1af4e215634e1380 Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Thu, 28 Dec 2023 23:41:55 -0700 Subject: [PATCH 2/8] Update spin2.utils.ts - adjust so 43 or greater works! --- spin2/server/src/parser/spin2.utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spin2/server/src/parser/spin2.utils.ts b/spin2/server/src/parser/spin2.utils.ts index f3fc872..2d8af50 100644 --- a/spin2/server/src/parser/spin2.utils.ts +++ b/spin2/server/src/parser/spin2.utils.ts @@ -2086,7 +2086,7 @@ export class Spin2ParseUtils { if (!reservedStatus) { reservedStatus = nameKey in this._tableSpinControlFlowMethods; if (!reservedStatus && this.requestedSpinVersion(43)) { - // if {Spin2_v43} then also search this table + // if {Spin2_v43} or greater then also search this table reservedStatus = nameKey in this._tableSpinEnhancements_v43; } } @@ -2133,7 +2133,7 @@ export class Spin2ParseUtils { desiredDocText.category = "String Method"; protoWDescr = this._tableSpinStringBuilder[nameKey]; } else if (this.requestedSpinVersion(43) && nameKey in this._tableSpinEnhancements_v43) { - // if {Spin2_v43} then also search this table + // if {Spin2_v43} or greater then also search this table desiredDocText.category = "String Method"; protoWDescr = this._tableSpinEnhancements_v43[nameKey]; } else if (nameKey in this._tableSpinIndexValueMethods) { @@ -2892,7 +2892,7 @@ export class Spin2ParseUtils { if (checkType == "BYTE" || checkType == "WORD" || checkType == "LONG") { returnStatus = true; } - if (!returnStatus && languageVersion == 43) { + if (!returnStatus && languageVersion >= 43) { if (checkType == "LSTRING") { returnStatus = true; } From 8355eedf8779c0e382686d53aa62bbefbcb19e93 Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Fri, 29 Dec 2023 00:08:12 -0700 Subject: [PATCH 3/8] cleanup method recognition, ensure lang ver parse happens for each file reload --- spin2/server/src/parser/spin.common.ts | 2 +- .../parser/spin2.documentSemanticParser.ts | 29 ++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/spin2/server/src/parser/spin.common.ts b/spin2/server/src/parser/spin.common.ts index 5ac50ab..47d40ee 100644 --- a/spin2/server/src/parser/spin.common.ts +++ b/spin2/server/src/parser/spin.common.ts @@ -85,7 +85,7 @@ export function haveDebugLine(line: string, startsWith: boolean = false): boolea } export function isMethodCall(line: string): boolean { - const methodOpenRegEx = /\s*\(/; // match zero or more whitespace before '(' + const methodOpenRegEx = /^\s*\(/; // match zero or more whitespace before '(' from left edge of string return methodOpenRegEx.test(line); } diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index db612b4..d4022f7 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -160,6 +160,8 @@ export class Spin2DocumentSemanticParser { // also track and record block comments (both braces and tic's!) // let's also track prior single line and trailing comment on same line this._logMessage(`---> Pre SCAN -- `); + this.parseUtils.setSpinVersion(0); // PRESET no override language version until we find one! + this.bHuntingForVersion = true; // we start hunting from top of file let bBuildingSingleLineCmtBlock: boolean = false; let bBuildingSingleLineDocCmtBlock: boolean = false; this.spinControlFlowTracker.reset(); @@ -168,6 +170,7 @@ export class Spin2DocumentSemanticParser { const NONDOC_COMMENT = false; const BLOCK_COMMENT = true; const LINE_COMMENT = false; + for (let i = 0; i < lines.length; i++) { const lineNbr: number = i + 1; const line: string = lines[i]; @@ -3938,13 +3941,13 @@ export class Spin2DocumentSemanticParser { referenceDetails = this.semanticFindings.getGlobalToken(namePart); this._logSPIN(` -- FOUND global name=[${namePart}], referenceDetails=(${referenceDetails})`); if (referenceDetails != undefined && referenceDetails?.type == "method") { - const methodCallNoSpace = `${namePart}(`; - const methodCallSpace = `${namePart} (`; const addressOf = `@${namePart}`; // FIXME: TODO: need better whitespace detection before parens! // if it's not a legit method call, kill the reference const searchSpace: string = multiLineSet.line.substring(nameOffset); - if (!searchSpace.includes(methodCallNoSpace) && !searchSpace.includes(methodCallSpace) && !searchString.includes(addressOf)) { + const methodFollowString: string = multiLineSet.line.substring(nameOffset + namePart.length); + this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + if (!isMethodCall(methodFollowString) && !searchString.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; } @@ -4192,11 +4195,11 @@ export class Spin2DocumentSemanticParser { referenceDetails = this.semanticFindings.getGlobalToken(namePart); this._logSPIN(` -- FOUND Global name=[${namePart}], referenceDetails=(${referenceDetails})`); if (referenceDetails != undefined && referenceDetails?.type == "method") { - const methodCallNoSpace = `${namePart}(`; - const methodCallSpace = `${namePart} (`; // FIXME: TODO: need better whitespace detection before parens! const addressOf = `@${namePart}`; - if (!nonStringAssignmentRHSStr.includes(methodCallNoSpace) && !nonStringAssignmentRHSStr.includes(methodCallSpace) && !nonStringAssignmentRHSStr.includes(addressOf)) { + const methodFollowString: string = multiLineSet.line.substring(nameOffset + namePart.length); + this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + if (!isMethodCall(methodFollowString) && !nonStringAssignmentRHSStr.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; } @@ -4528,12 +4531,12 @@ export class Spin2DocumentSemanticParser { referenceDetails = this.semanticFindings.getGlobalToken(namePart); this._logSPIN(" -- FOUND global name=[" + namePart + "]"); if (referenceDetails != undefined && referenceDetails?.type == "method") { - const methodCallNoSpace = `${namePart}(`; - const methodCallSpace = `${namePart} (`; const addressOf = `@${namePart}`; // FIXME: TODO: need better whitespace detection before parens! // if it's not a legit method call, kill the reference - if (!line.includes(methodCallNoSpace) && !line.includes(methodCallSpace) && !searchString.includes(addressOf)) { + const methodFollowString: string = line.substring(nameOffset + namePart.length); + this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + if (!isMethodCall(methodFollowString) && !searchString.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; } @@ -4776,12 +4779,12 @@ export class Spin2DocumentSemanticParser { this._logSPIN(` -- EXISTS Global name=[${namePart}], BUT referenceDetails=(${referenceDetails})`); } if (referenceDetails != undefined && referenceDetails?.type == "method") { - const methodCallNoSpace = `${namePart}(`; - const methodCallSpace = `${namePart} (`; // FIXME: TODO: need better whitespace detection before parens! const addressOf = `@${namePart}`; - if (!nonStringAssignmentRHSStr.includes(methodCallNoSpace) && !nonStringAssignmentRHSStr.includes(methodCallSpace) && !nonStringAssignmentRHSStr.includes(addressOf)) { - this._logSPIN(" -- MISSING parens on method=[" + namePart + "]"); + const methodFollowString: string = line.substring(nameOffset + namePart.length); + this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + if (!isMethodCall(methodFollowString) && !nonStringAssignmentRHSStr.includes(addressOf)) { + this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; } } From cb4836c5f956dbbe0a5e1a8455aab708342254dd Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Fri, 29 Dec 2023 00:22:34 -0700 Subject: [PATCH 4/8] update tests forcasting needs for dupe checks! --- spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 | 13 +++++++++++-- spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 | 14 ++++++++++++++ .../src/parser/spin2.documentSemanticParser.ts | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 index ac0aff7..3aa5aef 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 @@ -1,14 +1,18 @@ ' -------------------------------------------------------------------------------------------------- ' bitfield highlight issues Ln#32-34 -' {Spin2_v43} +DAT + +header_buf byte 0[$100] VAR -byte header_buf[$100] +byte header_buf[$100] ' <-- BUG should generate dupe of DAT variable declaration error +byte header_buf[$100] ' <-- BUG should generate P2 Spin dupe VAR varaible declaration error CON SEGA_FOURCC = ("S"+"E"<<8+"G"<<16+"A"<<24) +SEGA_FOURCC = ("S"+"E"<<8+"G"<<16+"A"<<24) ' <-- BUG should generate P2 Spin dupe constant declaration REGION_OVERSEAS_60HZ = %10 '' Americas REGION_DOMESTIC_60HZ = %00 '' Japan @@ -62,6 +66,9 @@ repeat (256*8) PUB LEDon() + file_open(@"filename1", %"r+") + file_open(@"filename1", %"A+") + DoSETUP(string("Text",13)) 'turn on LEDs DoSETUP(lstring("Hello",0,"Terve",0)) 'turn on LEDs DoSETUP(byte($21,$09,$00,$02,$00,$00,$01,$00)) 'turn on LEDs @@ -72,3 +79,5 @@ PUB LEDon() DoSETUP(long(1e-6,1e-3,1.0,1e3,1e6,-50,BYTE $FF)) 'turn on LEDs PRI DoSETUP(pBytes) + +PRI file_open(pFilename, pOpenMode) diff --git a/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 index ee1950a..21b7e1e 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 @@ -1,5 +1,6 @@ ' -------------------- ' test parsing of storagetype variable subpart access +' {Spin2_v43} PUB main(param1) : retValue | local2 @@ -13,3 +14,16 @@ PUB main(param1) : retValue | local2 PRI test(b) + +PUB LEDon() + + DoSETUP(string("Text",13)) 'turn on LEDs + DoSETUP(lstring("Hello",0,"Terve",0)) 'turn on LEDs + DoSETUP(byte($21,$09,$00,$02,$00,$00,$01,$00)) 'turn on LEDs + DoSETUP(byte($80,$09,$77,WORD $1234,LONG -1)) 'turn on LEDs + DoSETUP(word($21,$09,$00,$02,$00,$00,$01,$00)) 'turn on LEDs + DoSETUP(word(1_000,10_000,50_000,LONG $12345678)) 'turn on LEDs + DoSETUP(long($21,$09,$00,$02,$00,$00,$01,$00)) 'turn on LEDs + DoSETUP(long(1e-6,1e-3,1.0,1e3,1e6,-50,BYTE $FF)) 'turn on LEDs + +PRI DoSETUP(pBytes) diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index d4022f7..cf404b0 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -161,7 +161,7 @@ export class Spin2DocumentSemanticParser { // let's also track prior single line and trailing comment on same line this._logMessage(`---> Pre SCAN -- `); this.parseUtils.setSpinVersion(0); // PRESET no override language version until we find one! - this.bHuntingForVersion = true; // we start hunting from top of file + this.bHuntingForVersion = true; // PRESET we start hunting from top of file let bBuildingSingleLineCmtBlock: boolean = false; let bBuildingSingleLineDocCmtBlock: boolean = false; this.spinControlFlowTracker.reset(); From 0e761cc560f62c01d09d53267d34f2570f1c7218 Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Fri, 29 Dec 2023 14:37:06 -0700 Subject: [PATCH 5/8] add ability to filter hover text repsonses to method() or not - add support in hover text for byte() method vs. byte storage type --- .../TEST_LANG_SERVER/spin2/231123-fixes.spin2 | 4 +-- .../server/src/parser/spin.extension.utils.ts | 6 ++--- .../parser/spin2.documentSemanticParser.ts | 3 ++- spin2/server/src/parser/spin2.utils.ts | 15 ++++++++--- spin2/server/src/providers/HoverProvider.ts | 25 +++++++++++++------ .../src/providers/SignatureHelpProvider.ts | 4 +-- 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 index 21b7e1e..3f6d30c 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 @@ -1,8 +1,8 @@ ' -------------------- ' test parsing of storagetype variable subpart access -' {Spin2_v43} +' {Spin2_v42} -PUB main(param1) : retValue | local2 +PUB main(param1) : retValue | LONG local2 test(param1.word[0]) diff --git a/spin2/server/src/parser/spin.extension.utils.ts b/spin2/server/src/parser/spin.extension.utils.ts index e9eef5d..149cbd7 100644 --- a/spin2/server/src/parser/spin.extension.utils.ts +++ b/spin2/server/src/parser/spin.extension.utils.ts @@ -92,7 +92,7 @@ export class ExtensionUtils { cursorPosition: lsp.Position, isInBlockComment: boolean, inPasmCodeStatus: boolean - ): [boolean, string, string, lsp.Position] { + ): [boolean, string, string, lsp.Position, lsp.Position] { const lineText = DocumentLineAt(document, wordPosition).trimEnd(); const P2_LOCAL_LABEL_PREFIX: string = "."; const P1_LOCAL_LABEL_PREFIX: string = ":"; @@ -180,14 +180,14 @@ export class ExtensionUtils { } if (!wordRange || this.isPositionInString(lineText, wordPosition, stringsFound, ticVarsFound) || bPositionInComment || word.match(/^\d+.?\d+$/) || spinControlFlowKeywords.indexOf(word) > 0) { this._logMessage(`+ sp2Utils: adjustWordPosition() EXIT false`); - return [false, null!, null!, null!]; + return [false, null!, null!, null!, null!]; } if (PositionIsEqual(wordPosition, wordRange.end) && PositionIsAfter(wordPosition, wordRange.start)) { wordPosition = PositionTranslate(wordPosition, 0, -1); } this._logMessage(`+ sp2Utils: adjustWordPosition() EXIT true`); - return [true, objectRef, word, wordPosition]; + return [true, objectRef, word, wordPosition, wordRange.start]; } public isPositionInString(lineText: string, position: lsp.Position, stringsInLine: IPairs[], ticVarsInLine: IPairs[]): boolean { diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index cf404b0..7a501a6 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -421,7 +421,8 @@ export class Spin2DocumentSemanticParser { if (this.bHuntingForVersion) { this.bHuntingForVersion = false; // done, we passed the file-top comments. we can no longer search - this._logMessage(` -- stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); + const newLangVersion: number = this.parseUtils.selectedSpinVersion(); + this._logMessage(` -- stopping HUNT w/lang=(${newLangVersion}), Ln#${lineNbr}=[${trimmedLine}]`); } currState = sectionStatus.inProgressStatus; diff --git a/spin2/server/src/parser/spin2.utils.ts b/spin2/server/src/parser/spin2.utils.ts index 2d8af50..56042b3 100644 --- a/spin2/server/src/parser/spin2.utils.ts +++ b/spin2/server/src/parser/spin2.utils.ts @@ -19,6 +19,13 @@ export const displayEnumByTypeName = new Map([ // this is how we decribe our methods with parameters in our tables... type TMethodTuple = readonly [signature: string, description: string, parameters: string[], returns?: string[] | undefined]; +export enum eSearchFilterType { + Unknown = 0, + FT_NO_PREFERENCE, + FT_METHOD, + FT_NOT_METHOD, +} + export class Spin2ParseUtils { private utilsLogEnabled: boolean = true; private ctx: Context | undefined = undefined; @@ -841,20 +848,20 @@ export class Spin2ParseUtils { desiredDocText.description = description; } else { // not a debug specific symbol then check other built-in's - desiredDocText = this.docTextForBuiltIn(name); + desiredDocText = this.docTextForBuiltIn(name, eSearchFilterType.FT_NO_PREFERENCE); } } return desiredDocText; } - public docTextForBuiltIn(name: string): IBuiltinDescription { + public docTextForBuiltIn(name: string, typeFilter: eSearchFilterType): IBuiltinDescription { let desiredDocText: IBuiltinDescription = this._docTextForSpinBuiltInVariable(name); if (desiredDocText.found) { desiredDocText.type = eBuiltInType.BIT_VARIABLE; } else { desiredDocText = this._docTextForSpinBuiltInMethod(name); - if (desiredDocText.found) { + if (desiredDocText.found && typeFilter != eSearchFilterType.FT_NOT_METHOD) { desiredDocText.type = eBuiltInType.BIT_METHOD; } else { desiredDocText = this._docTextForCogAndNumericSymbols(name); @@ -878,7 +885,7 @@ export class Spin2ParseUtils { // desiredDocText.type = --> nope, this one sets the type for us! } else { desiredDocText = this._docTextForSpinStorageTypesAlignment(name); - if (desiredDocText.found) { + if (desiredDocText.found && typeFilter != eSearchFilterType.FT_METHOD) { desiredDocText.type = eBuiltInType.BIT_TYPE; } else { desiredDocText = this._docTextForSpinDebugBuiltInInvoke(name); diff --git a/spin2/server/src/providers/HoverProvider.ts b/spin2/server/src/providers/HoverProvider.ts index b72c903..6350359 100644 --- a/spin2/server/src/providers/HoverProvider.ts +++ b/spin2/server/src/providers/HoverProvider.ts @@ -13,13 +13,13 @@ import { Context } from "../context"; import { DocumentFindings, ITokenDescription } from "../parser/spin.semantic.findings"; import { IDefinitionInfo, ExtensionUtils } from "../parser/spin.extension.utils"; import { DocumentLineAt } from "../parser/lsp.textDocument.utils"; -import { Spin2ParseUtils } from "../parser/spin2.utils"; +import { Spin2ParseUtils, eSearchFilterType } from "../parser/spin2.utils"; import { Spin1ParseUtils } from "../parser/spin1.utils"; -import { eBuiltInType } from "../parser/spin.common"; +import { eBuiltInType, isMethodCall } from "../parser/spin.common"; import { isSpin1File, fileSpecFromURI } from "../parser/lang.utils"; export default class HoverProvider implements Provider { - private hoverLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private hoverLogEnabled: boolean = true; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private symbolsFound: DocumentFindings = new DocumentFindings(); // this gets replaced @@ -135,6 +135,10 @@ export default class HoverProvider implements Provider { this._logMessage(`+ Hvr: definitionLocation() EXIT fail`); return Promise.resolve(null); } + this._logMessage( + `+ Hvr: adjustWordPosition -> [0]bool=(${adjustedPos[0]}), [1]string=[${adjustedPos[1]}], [2]string=[${adjustedPos[2]}], [3]adjposn=[${adjustedPos[3].line}:${adjustedPos[3].character}], [3]wordSrt=[${adjustedPos[4].line}:${adjustedPos[4].character}]` + ); + const wordStart: Position = adjustedPos[4]; const declarationLine: string = DocumentLineAt(document, position).trimEnd(); let objectRef = inObjDeclarationStatus ? this._objectNameFromDeclaration(declarationLine) : adjustedPos[1]; @@ -144,10 +148,16 @@ export default class HoverProvider implements Provider { } const sourcePosition: Position = adjustedPos[3]; let fileBasename = path.basename(document.uri); - this._logMessage(`+ Hvr: hoverSource=[${hoverSource}], inObjDecl=(${inObjDeclarationStatus}), adjPos=(${position.line},${position.character}), file=[${fileBasename}], line=[${declarationLine}]`); + const methodFollowString: string = declarationLine.substring(wordStart.character + hoverSource.length); + const bMethodCall: boolean = isMethodCall(methodFollowString); + this._logMessage(`+ Hvr: methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + + this._logMessage( + `+ Hvr: hoverSource=[${hoverSource}], isMethod=(${bMethodCall}), inObjDecl=(${inObjDeclarationStatus}), adjPos=(${position.line},${position.character}), file=[${fileBasename}], line=[${declarationLine}]` + ); this._logMessage(`+ Hvr: definitionLocation() EXIT after getting symbol details`); - return this.getSymbolDetails(document, sourcePosition, objectRef, hoverSource); + return this.getSymbolDetails(document, sourcePosition, objectRef, hoverSource, bMethodCall); } private _objectNameFromDeclaration(line: string): string { @@ -193,7 +203,7 @@ export default class HoverProvider implements Provider { return desiredLinePortion; } - private getSymbolDetails(document: TextDocument, position: Position, objRef: string, searchWord: string): Promise { + private getSymbolDetails(document: TextDocument, position: Position, objRef: string, searchWord: string, isMethodCall: boolean): Promise { return new Promise((resolve, reject) => { const defInfo: IDefinitionInfo = { file: document.uri, @@ -238,7 +248,8 @@ export default class HoverProvider implements Provider { const isDebugLine: boolean = this.spin1File ? false : sourceLine.toLowerCase().startsWith("debug("); let bFoundSomething: boolean = false; // we've no answer - let builtInFindings = isDebugLine ? this.parseUtils.docTextForDebugBuiltIn(searchWord) : this.parseUtils.docTextForBuiltIn(searchWord); + const filterType: eSearchFilterType = isMethodCall ? eSearchFilterType.FT_METHOD : eSearchFilterType.FT_NOT_METHOD; + let builtInFindings = isDebugLine ? this.parseUtils.docTextForDebugBuiltIn(searchWord) : this.parseUtils.docTextForBuiltIn(searchWord, filterType); if (!builtInFindings.found) { this._logMessage(`+ Hvr: built-in=[${searchWord}], NOT found!`); } else { diff --git a/spin2/server/src/providers/SignatureHelpProvider.ts b/spin2/server/src/providers/SignatureHelpProvider.ts index 98dffcd..80f26a3 100644 --- a/spin2/server/src/providers/SignatureHelpProvider.ts +++ b/spin2/server/src/providers/SignatureHelpProvider.ts @@ -12,7 +12,7 @@ import { Context } from "../context"; import { DocumentFindings, ITokenDescription } from "../parser/spin.semantic.findings"; import { IDefinitionInfo, ExtensionUtils, IPairs } from "../parser/spin.extension.utils"; import { GetWordRangeAtPosition, DocumentLineAt, PositionTranslate } from "../parser/lsp.textDocument.utils"; -import { Spin2ParseUtils } from "../parser/spin2.utils"; +import { Spin2ParseUtils, eSearchFilterType } from "../parser/spin2.utils"; import { Spin1ParseUtils } from "../parser/spin1.utils"; import { IBuiltinDescription, eBuiltInType } from "../parser/spin.common"; import { isSpin1File, fileSpecFromURI } from "../parser/lang.utils"; @@ -265,7 +265,7 @@ export default class SignatureHelpProvider implements Provider { this._logMessage(`+ Sig: getSymbolDetails() isSignatureLine=(${isSignatureLine}), isDebugLine=(${isDebugLine}), isObjectReference=(${isObjectReference})`); let bFoundSomething: boolean = false; // we've no answer - let builtInFindings: IBuiltinDescription = isDebugLine ? this.parseUtils.docTextForDebugBuiltIn(searchWord) : this.parseUtils.docTextForBuiltIn(searchWord); + let builtInFindings: IBuiltinDescription = isDebugLine ? this.parseUtils.docTextForDebugBuiltIn(searchWord) : this.parseUtils.docTextForBuiltIn(searchWord, eSearchFilterType.FT_NO_PREFERENCE); if (!builtInFindings.found) { this._logMessage(`+ Sig: built-in=[${searchWord}], NOT found!`); } else { From 765813eef1aebcdfd3d552016c629a6215099b6f Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Sat, 30 Dec 2023 18:05:32 -0700 Subject: [PATCH 6/8] fix version detection --- .../TEST_LANG_SERVER/spin2/231123-fixes.spin2 | 2 +- spin2/server/src/parser/spin.common.ts | 36 ++++++++++++++----- .../parser/spin2.documentSemanticParser.ts | 19 ++++++++-- spin2/server/src/parser/spin2.utils.ts | 4 +-- spin2/server/src/providers/HoverProvider.ts | 2 +- .../src/providers/TextDocumentSyncProvider.ts | 9 +++-- spin2/server/src/server.ts | 4 +-- 7 files changed, 54 insertions(+), 22 deletions(-) diff --git a/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 index 3f6d30c..807a5a9 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231123-fixes.spin2 @@ -1,6 +1,6 @@ ' -------------------- ' test parsing of storagetype variable subpart access -' {Spin2_v42} +' {Spin2_v41} PUB main(param1) : retValue | LONG local2 diff --git a/spin2/server/src/parser/spin.common.ts b/spin2/server/src/parser/spin.common.ts index 47d40ee..348ef12 100644 --- a/spin2/server/src/parser/spin.common.ts +++ b/spin2/server/src/parser/spin.common.ts @@ -91,20 +91,40 @@ export function isMethodCall(line: string): boolean { export function containsSpinLanguageSpec(line: string): boolean { // return T/F where T means {Spin2_v##} was found in given string - const languageVersionRegEx = /\{Spin2\_v[0-9][0-9]\}/i; // our version specification + const languageVersionRegEx = /\{Spin2\_v/i; // our version specification (just look for left edge) return languageVersionRegEx.test(line); } export function versionFromSpinLanguageSpec(line: string): number { // return T/F where T means {Spin2_v##} was found in given string let decodedVersion: number = 0; // return no version by default - if (containsSpinLanguageSpec(line)) { - const matchText: string = "{Spin2_v".toLowerCase(); - const verOffset: number = line.toLowerCase().indexOf(matchText); - if (verOffset != -1) { - const tens: number = parseInt(line.charAt(verOffset + matchText.length)); - const ones: number = parseInt(line.charAt(verOffset + matchText.length + 1)); - decodedVersion = tens * 10 + ones; + const languageVersionRegEx = /\{Spin2\_v[0-9][0-9]\}/i; // our version specification - well formatted 0-99 + const languageVersionThousandsRegEx = /\{Spin2\_v[0-9][0-9][0-9]\}/i; // our version specification - well formatted 0-999 + const is3digit: boolean = languageVersionThousandsRegEx.test(line); + // if have fully formatted version + if (languageVersionRegEx.test(line) || is3digit) { + if (containsSpinLanguageSpec(line)) { + const matchText: string = "{Spin2_v".toLowerCase(); + const verOffset: number = line.toLowerCase().indexOf(matchText); + if (verOffset != -1) { + if (is3digit) { + const hundreds: number = parseInt(line.charAt(verOffset + matchText.length)); + const tens: number = parseInt(line.charAt(verOffset + matchText.length + 1)); + const ones: number = parseInt(line.charAt(verOffset + matchText.length + 2)); + decodedVersion = hundreds * 100 + tens * 10 + ones; + } else { + const tens: number = parseInt(line.charAt(verOffset + matchText.length)); + const ones: number = parseInt(line.charAt(verOffset + matchText.length + 1)); + decodedVersion = tens * 10 + ones; + } + } + // special: disallow unreleased versions: + // - 41 is base version so say 0 + // - 42 was not released so say zero + // - 40 or less is also 0 + if (decodedVersion < 43) { + decodedVersion = 0; + } } } return decodedVersion; diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index 7a501a6..735382e 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -104,6 +104,7 @@ export class Spin2DocumentSemanticParser { public reportDocumentSemanticTokens(document: TextDocument, findings: DocumentFindings, dirSpec: string): void { this.semanticFindings = findings; this.directory = dirSpec; + const startingLangVersion: number = this.parseUtils.selectedSpinVersion(); if (this.spin2DebugLogEnabled) { this.semanticFindings.enableLogging(this.ctx); this.parseUtils.enableLogging(this.ctx); @@ -118,6 +119,13 @@ export class Spin2DocumentSemanticParser { // retrieve tokens to highlight, post to DocumentFindings const allTokens = this._parseText(document.getText()); + const endingLangVersion: number = this.parseUtils.selectedSpinVersion(); + if (startingLangVersion != endingLangVersion) { + this._logMessage(`* Spin2 LANG VERSION HUNT [${startingLangVersion} -> ${endingLangVersion}]`); + } else { + this._logMessage(`* Spin2 LANG VERSION HUNT [${startingLangVersion}]`); + } + this._checkTokenSet(allTokens); allTokens.forEach((token) => { this.semanticFindings.pushSemanticToken(token); }); @@ -160,6 +168,7 @@ export class Spin2DocumentSemanticParser { // also track and record block comments (both braces and tic's!) // let's also track prior single line and trailing comment on same line this._logMessage(`---> Pre SCAN -- `); + const startingLangVersion: number = this.parseUtils.selectedSpinVersion(); this.parseUtils.setSpinVersion(0); // PRESET no override language version until we find one! this.bHuntingForVersion = true; // PRESET we start hunting from top of file let bBuildingSingleLineCmtBlock: boolean = false; @@ -176,10 +185,15 @@ export class Spin2DocumentSemanticParser { const line: string = lines[i]; const trimmedLine: string = line.trim(); if (this.bHuntingForVersion && containsSpinLanguageSpec(trimmedLine)) { + this._logMessage(` -- POSSIBLE spec: stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); this.bHuntingForVersion = false; // done we found it const newLangVersion: number = versionFromSpinLanguageSpec(trimmedLine); - this.parseUtils.setSpinVersion(newLangVersion); - this._logMessage(` -- found Spin2 version (${newLangVersion}), stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); + if (newLangVersion != startingLangVersion) { + this.parseUtils.setSpinVersion(newLangVersion); + this._logMessage(` -- found Spin2 NEW version (${newLangVersion}), stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); + } else { + this._logMessage(` -- found Spin2 SAME version (${newLangVersion}), stopping HUNT Ln#${lineNbr}=[${trimmedLine}]`); + } } const lineWOutInlineComments: string = this.parseUtils.getLineWithoutInlineComments(line); const bHaveLineToProcess: boolean = lineWOutInlineComments.length > 0; @@ -1091,7 +1105,6 @@ export class Spin2DocumentSemanticParser { pendingState = eParseState.Unknown; } } - this._checkTokenSet(tokenSet); return tokenSet; } diff --git a/spin2/server/src/parser/spin2.utils.ts b/spin2/server/src/parser/spin2.utils.ts index 56042b3..3e293b6 100644 --- a/spin2/server/src/parser/spin2.utils.ts +++ b/spin2/server/src/parser/spin2.utils.ts @@ -46,8 +46,8 @@ export class Spin2ParseUtils { } public setSpinVersion(requiredVersion: number): void { - if (requiredVersion >= 41 && requiredVersion != 42) { - this.languageVersion = requiredVersion; + if (requiredVersion >= 0 && requiredVersion <= 999) { + this.languageVersion = requiredVersion < 43 ? 0 : requiredVersion; } } diff --git a/spin2/server/src/providers/HoverProvider.ts b/spin2/server/src/providers/HoverProvider.ts index 6350359..97e31d8 100644 --- a/spin2/server/src/providers/HoverProvider.ts +++ b/spin2/server/src/providers/HoverProvider.ts @@ -19,7 +19,7 @@ import { eBuiltInType, isMethodCall } from "../parser/spin.common"; import { isSpin1File, fileSpecFromURI } from "../parser/lang.utils"; export default class HoverProvider implements Provider { - private hoverLogEnabled: boolean = true; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private hoverLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private symbolsFound: DocumentFindings = new DocumentFindings(); // this gets replaced diff --git a/spin2/server/src/providers/TextDocumentSyncProvider.ts b/spin2/server/src/providers/TextDocumentSyncProvider.ts index a5b0a2c..4fd23eb 100644 --- a/spin2/server/src/providers/TextDocumentSyncProvider.ts +++ b/spin2/server/src/providers/TextDocumentSyncProvider.ts @@ -61,7 +61,7 @@ export default class TextDocumentSyncProvider implements Provider { const updatedDoc = TextDocument.update(document, contentChanges, version); await this.processor.process(updatedDoc).then(({ parseResult }) => { - // Send just local parser diagnostics - can't get vasm errors until save + // Send just local parser diagnostics // FIXME: not quite correct but works for now... this.fileDiagnostics(uri); }); @@ -106,7 +106,7 @@ export default class TextDocumentSyncProvider implements Provider { ` DBG -- ctx.parserConfig.maxNumberOfReportedIssues=(${this.ctx.parserConfig.maxNumberOfReportedIssues}), highlightFlexspinDirectives=[${this.ctx.parserConfig.highlightFlexspinDirectives}], changes=(${configChanged})` ); }); - await this.ctx.connection.workspace.getConfiguration("Editor").then((editorConfiguration: EditorConfiguration) => { + await this.ctx.connection.workspace.getConfiguration("editor").then((editorConfiguration: EditorConfiguration) => { if (editorConfiguration == null) { this.ctx.logger.log(`TRC: DP.process() ERROR! get editor settings received no info: config left at defaults`); } else { @@ -176,7 +176,7 @@ export default class TextDocumentSyncProvider implements Provider { const change = params.changes[index]; switch (change.type) { case lsp.FileChangeType.Changed: - // dont care + // dont care (doc change will handle this need to reparse) break; case lsp.FileChangeType.Created: // cause reparse open files @@ -215,7 +215,7 @@ export default class TextDocumentSyncProvider implements Provider { } /** - * Send diagnostics from both local parser and vasm + * Send diagnostics from both local parser */ async fileDiagnostics(uri: string) { const docFSpec: string = fileSpecFromURI(uri); @@ -223,7 +223,6 @@ export default class TextDocumentSyncProvider implements Provider { if (!processedDocument) { return; } - //const vasmDiagnostics = await this.diagnostics.vasmDiagnostics(uri); this.ctx.logger.log(`TRC: CHK CFG maxNumberOfReportedIssues=(${this.ctx.parserConfig.maxNumberOfReportedIssues})`); let captureDiagnostics: lsp.Diagnostic[] = processedDocument.parseResult.allDiagnosticMessages(this.ctx.parserConfig.maxNumberOfReportedIssues); //this.ctx.logger.log(`CL-TRC: captureDiagnostics=[${JSON.stringify(captureDiagnostics)}]`); diff --git a/spin2/server/src/server.ts b/spin2/server/src/server.ts index f03bd8a..18211f8 100644 --- a/spin2/server/src/server.ts +++ b/spin2/server/src/server.ts @@ -77,10 +77,10 @@ connection.onDidChangeWatchedFiles((_change) => { const change = _change.changes[index]; switch (change.type) { case FileChangeType.Changed: - // dont care + // dont care (doc change will handle this need to reparse) break; case FileChangeType.Created: - // casue eparse open files + // cause reparse open files break; case FileChangeType.Deleted: // cause reparse open files From 4aaf0418c43c30e02d585f932e8920a9e100161b Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Sat, 30 Dec 2023 19:10:45 -0700 Subject: [PATCH 7/8] Final updates - release prep - emit language version when present to Object Interface Documentation - report on duplicate CON, VAR, DAT declarations - update test files --- spin2/CHANGELOG.md | 9 ++-- .../TEST_LANG_SERVER/spin2/231112-fixes.spin2 | 6 +-- .../TEST_LANG_SERVER/spin2/231230-fixes.spin2 | 17 +++++++ .../src/providers/spin.document.generate.ts | 25 ++++++++++- spin2/client/src/spin.code.utils.ts | 44 +++++++++++++++++++ .../parser/spin2.documentSemanticParser.ts | 41 ++++++++++++++--- 6 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 spin2/TEST_LANG_SERVER/spin2/231230-fixes.spin2 diff --git a/spin2/CHANGELOG.md b/spin2/CHANGELOG.md index 3f7867d..e8cd899 100644 --- a/spin2/CHANGELOG.md +++ b/spin2/CHANGELOG.md @@ -18,16 +18,17 @@ Possible next additions: - Add new-file templates as Snippets - Add additional Snippets as the community identifies them -## [2.2.11] 2023-12-?? +## [2.2.11] 2023-12-30 Update P2 Only - Add recognition of "auto" on debug scope display declarations -- Add Semantic highlight color change for byte(), word(), and long() method overrides +- Add semantic highlight color change for byte(), word(), and long() method overrides - Add recognition of byte(), word(), and long() method names to provide method vs. storage type hover text -- Add recognition of {Spin2_v4XX} format Spin Language Requirement directive -- Emit any languge directive in use to generated interface documentation +- Add recognition of {Spin2_v##} format Spin Language Requirement directive +- Emit any languge directive when used to generated interface documentation - Add support for lstring() when {Spin2_v43} is specified +- Add detection of/error generation for duplicate declarations within CON, VAR and DAT sections ## [2.2.10] 2023-12-24 diff --git a/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 index 3aa5aef..b518877 100644 --- a/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/231112-fixes.spin2 @@ -1,17 +1,13 @@ ' -------------------------------------------------------------------------------------------------- ' bitfield highlight issues Ln#32-34 -DAT - -header_buf byte 0[$100] +' {Spin2_v43} VAR byte header_buf[$100] ' <-- BUG should generate dupe of DAT variable declaration error -byte header_buf[$100] ' <-- BUG should generate P2 Spin dupe VAR varaible declaration error CON -SEGA_FOURCC = ("S"+"E"<<8+"G"<<16+"A"<<24) SEGA_FOURCC = ("S"+"E"<<8+"G"<<16+"A"<<24) ' <-- BUG should generate P2 Spin dupe constant declaration REGION_OVERSEAS_60HZ = %10 '' Americas diff --git a/spin2/TEST_LANG_SERVER/spin2/231230-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/231230-fixes.spin2 new file mode 100644 index 0000000..9f38cbf --- /dev/null +++ b/spin2/TEST_LANG_SERVER/spin2/231230-fixes.spin2 @@ -0,0 +1,17 @@ + +' -------------------- +' test duplicate global detection + +DAT + +header_buf byte 0[$100] + +VAR + +byte header_buf[$100] ' <-- BUG should generate dupe of DAT variable declaration error +byte header_buf[$100] ' <-- BUG should generate P2 Spin dupe VAR varaible declaration error + +CON + +SEGA_FOURCC = ("S"+"E"<<8+"G"<<16+"A"<<24) +SEGA_FOURCC = ("S"+"E"<<8+"G"<<16+"A"<<24) ' <-- BUG should generate P2 Spin dupe constant declaration diff --git a/spin2/client/src/providers/spin.document.generate.ts b/spin2/client/src/providers/spin.document.generate.ts index c62d7ac..1584a9e 100644 --- a/spin2/client/src/providers/spin.document.generate.ts +++ b/spin2/client/src/providers/spin.document.generate.ts @@ -204,6 +204,7 @@ export class DocGenerator { let textEditor = vscode.window.activeTextEditor; if (textEditor) { let endOfLineStr: string = textEditor.document.eol == EndOfLine.CRLF ? "\r\n" : "\n"; + let bHuntingForVersion: boolean = true; // initially we re hunting for a {Spin2_v##} spec in file-top comments var currentlyOpenTabfilePath = textEditor.document.uri.fsPath; var currentlyOpenTabfolderName = path.dirname(currentlyOpenTabfilePath); @@ -236,13 +237,22 @@ export class DocGenerator { let priorState: eParseState = currState; let pubsFound: number = 0; + + let requiredLanguageVersion: number = 0; // // 1st pass: emit topfile doc comments then list of pub methods // for (let i = 0; i < textEditor.document.lineCount; i++) { let line = textEditor.document.lineAt(i); + const lineNbr = i + 1; const trimmedLine = line.text.trim(); + if (bHuntingForVersion && this.spinCodeUtils.containsSpinLanguageSpec(trimmedLine)) { + bHuntingForVersion = false; // done we found it + requiredLanguageVersion = this.spinCodeUtils.versionFromSpinLanguageSpec(trimmedLine); + this.logMessage(`+ (DBG) generateDocument() requiredLanguageVersion=(${requiredLanguageVersion}) at Ln#${lineNbr} [${trimmedLine}]`); + } + if (currState == eParseState.inMultiLineDocComment) { // skip all {{ --- }} multi-line doc comments // in multi-line doc-comment, hunt for end '}}' to exit @@ -315,13 +325,26 @@ export class DocGenerator { // emit comment without leading '' fs.appendFileSync(outFile, trimmedLine.substring(2) + endOfLineStr); } - } else if (sectionStatus.isSectionStart && currState == eParseState.inPub) { + continue; + } + + if (sectionStatus.isSectionStart && bHuntingForVersion) { + this.logMessage(`+ (DBG) generateDocument() STOP HUNT at Ln#${lineNbr} [${trimmedLine}]`); + bHuntingForVersion = false; // done, we passed the file-top comments. we can no longer search + } + + if (sectionStatus.isSectionStart && currState == eParseState.inPub) { // have public method report it pubsFound++; if (shouldEmitTopDocComments) { + this.logMessage(`+ (DBG) generateDocument() EMIT object header`); fs.appendFileSync(outFile, "" + endOfLineStr); // blank line const introText: string = 'Object "' + objectName + '" Interface:'; fs.appendFileSync(outFile, introText + endOfLineStr); + if (requiredLanguageVersion > 0) { + const lanVersionText: string = ` (Requires Spin2 Language v${requiredLanguageVersion})`; + fs.appendFileSync(outFile, lanVersionText + endOfLineStr); + } fs.appendFileSync(outFile, "" + endOfLineStr); // blank line } shouldEmitTopDocComments = false; // no more of these! diff --git a/spin2/client/src/spin.code.utils.ts b/spin2/client/src/spin.code.utils.ts index f27a083..d578b84 100644 --- a/spin2/client/src/spin.code.utils.ts +++ b/spin2/client/src/spin.code.utils.ts @@ -202,4 +202,48 @@ export class SpinCodeUtils { return trimmedLine; } + + // borrowed from: + // dupe code at server/src/parser/spin.common.ts + // + public containsSpinLanguageSpec(line: string): boolean { + // return T/F where T means {Spin2_v##} was found in given string + const languageVersionRegEx = /\{Spin2\_v/i; // our version specification (just look for left edge) + return languageVersionRegEx.test(line); + } + + public versionFromSpinLanguageSpec(line: string): number { + // return T/F where T means {Spin2_v##} was found in given string + let decodedVersion: number = 0; // return no version by default + const languageVersionRegEx = /\{Spin2\_v[0-9][0-9]\}/i; // our version specification - well formatted 0-99 + const languageVersionThousandsRegEx = /\{Spin2\_v[0-9][0-9][0-9]\}/i; // our version specification - well formatted 0-999 + const is3digit: boolean = languageVersionThousandsRegEx.test(line); + // if have fully formatted version + if (languageVersionRegEx.test(line) || is3digit) { + if (this.containsSpinLanguageSpec(line)) { + const matchText: string = "{Spin2_v".toLowerCase(); + const verOffset: number = line.toLowerCase().indexOf(matchText); + if (verOffset != -1) { + if (is3digit) { + const hundreds: number = parseInt(line.charAt(verOffset + matchText.length)); + const tens: number = parseInt(line.charAt(verOffset + matchText.length + 1)); + const ones: number = parseInt(line.charAt(verOffset + matchText.length + 2)); + decodedVersion = hundreds * 100 + tens * 10 + ones; + } else { + const tens: number = parseInt(line.charAt(verOffset + matchText.length)); + const ones: number = parseInt(line.charAt(verOffset + matchText.length + 1)); + decodedVersion = tens * 10 + ones; + } + } + // special: disallow unreleased versions: + // - 41 is base version so say 0 + // - 42 was not released so say zero + // - 40 or less is also 0 + if (decodedVersion < 43) { + decodedVersion = 0; + } + } + } + return decodedVersion; + } } diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index 735382e..e3abbdf 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -53,7 +53,7 @@ export class Spin2DocumentSemanticParser { private bLogStarted: boolean = false; // adjust following true/false to show specific parsing debug - private spin2DebugLogEnabled: boolean = true; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private spin2DebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showSpinCode: boolean = true; private showPreProc: boolean = true; private showCON: boolean = true; @@ -1419,8 +1419,13 @@ export class Spin2DocumentSemanticParser { this._logCON(" -- GLBL GetCONDecl newName=[" + newName + "]"); // remember this object name so we can annotate a call to it const nameOffset = line.indexOf(newName, currentOffset); // FIXME: UNDONE, do we have to dial this in? - this.semanticFindings.recordDeclarationLine(line, lineNbr); - this.semanticFindings.setGlobalToken(newName, new RememberedToken("variable", lineNbr - 1, nameOffset, ["readonly"]), this._declarationComment()); + const referenceDetails: RememberedToken | undefined = this.semanticFindings.getGlobalToken(newName); + if (referenceDetails) { + this.semanticFindings.pushDiagnosticMessage(lineNbr - 1, nameOffset, nameOffset + newName.length, eSeverity.Error, `P2 Spin Duplicate constant name [${newName}], already declared`); + } else { + this.semanticFindings.recordDeclarationLine(line, lineNbr); + this.semanticFindings.setGlobalToken(newName, new RememberedToken("variable", lineNbr - 1, nameOffset, ["readonly"]), this._declarationComment()); + } } } } @@ -1808,8 +1813,19 @@ export class Spin2DocumentSemanticParser { if (newName.charAt(0).match(/[a-zA-Z_]/)) { this._logVAR(" -- GLBL GetVarDecl newName=[" + newName + "]"); const nameOffset = line.indexOf(newName, currentOffset); // FIXME: UNDONE, do we have to dial this in? - this.semanticFindings.recordDeclarationLine(line, lineNbr); - this.semanticFindings.setGlobalToken(newName, new RememberedToken("variable", lineNbr - 1, nameOffset, ["instance"]), this._declarationComment()); + const referenceDetails: RememberedToken | undefined = this.semanticFindings.getGlobalToken(newName); + if (referenceDetails) { + this.semanticFindings.pushDiagnosticMessage( + lineNbr - 1, + nameOffset, + nameOffset + newName.length, + eSeverity.Error, + `P2 Spin Duplicate global variable name [${newName}], already declared` + ); + } else { + this.semanticFindings.recordDeclarationLine(line, lineNbr); + this.semanticFindings.setGlobalToken(newName, new RememberedToken("variable", lineNbr - 1, nameOffset, ["instance"]), this._declarationComment()); + } } } } else if (!hasGoodType && lineParts.length > 0) { @@ -1818,8 +1834,19 @@ export class Spin2DocumentSemanticParser { if (longVarName.charAt(0).match(/[a-zA-Z_]/)) { this._logVAR(" -- GLBL GetVarDecl newName=[" + longVarName + "]"); const nameOffset = line.indexOf(longVarName, currentOffset); // FIXME: UNDONE, do we have to dial this in? - this.semanticFindings.recordDeclarationLine(line, lineNbr); - this.semanticFindings.setGlobalToken(longVarName, new RememberedToken("variable", lineNbr - 1, nameOffset, ["instance"]), this._declarationComment()); + const referenceDetails: RememberedToken | undefined = this.semanticFindings.getGlobalToken(longVarName); + if (referenceDetails) { + this.semanticFindings.pushDiagnosticMessage( + lineNbr - 1, + nameOffset, + nameOffset + longVarName.length, + eSeverity.Error, + `P2 Spin Duplicate global variable name [${longVarName}], already declared` + ); + } else { + this.semanticFindings.recordDeclarationLine(line, lineNbr); + this.semanticFindings.setGlobalToken(longVarName, new RememberedToken("variable", lineNbr - 1, nameOffset, ["instance"]), this._declarationComment()); + } } } } From db52b20cfdcfaed85fe17094ffa115b6e7db7386 Mon Sep 17 00:00:00 2001 From: Stephen M Moraco Date: Sat, 30 Dec 2023 19:13:01 -0700 Subject: [PATCH 8/8] prep for release --- spin2/package.json | 2 +- spin2/scripts/LIVE-package.json | 2 +- spin2/scripts/TEST-package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spin2/package.json b/spin2/package.json index b1f7726..e7a2403 100644 --- a/spin2/package.json +++ b/spin2/package.json @@ -5,7 +5,7 @@ "icon": "images/Propeller.ico", "author": "IronSheep", "license": "MIT", - "version": "2.2.10", + "version": "2.2.11", "repository": { "type": "git", "url": "https://github.com/ironsheep/P2-vscode-langserv-extension" diff --git a/spin2/scripts/LIVE-package.json b/spin2/scripts/LIVE-package.json index b1f7726..e7a2403 100644 --- a/spin2/scripts/LIVE-package.json +++ b/spin2/scripts/LIVE-package.json @@ -5,7 +5,7 @@ "icon": "images/Propeller.ico", "author": "IronSheep", "license": "MIT", - "version": "2.2.10", + "version": "2.2.11", "repository": { "type": "git", "url": "https://github.com/ironsheep/P2-vscode-langserv-extension" diff --git a/spin2/scripts/TEST-package.json b/spin2/scripts/TEST-package.json index 2912fd5..cb38897 100644 --- a/spin2/scripts/TEST-package.json +++ b/spin2/scripts/TEST-package.json @@ -4,7 +4,7 @@ "description": "P1 and P2 Spin/Pasm Syntax/Semantic Highlighting w/Code Outline, Object Outline and Custom tabbing support", "author": "IronSheep", "license": "MIT", - "version": "2.2.10", + "version": "2.2.11", "repository": { "type": "git", "url": "https://github.com/ironsheep/P2-vscode-langserv-extension"