diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index a17c200..ff46964 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -29,7 +29,11 @@ jobs: - run: npm ci # For some reason, antlr4ng writes to a different location on the VM # than it does locally, preventing compile. Command added to move the generated files. + - run: npm run textMate - name: npm run antlr4ng - run: npm run antlr4ng && mv ./server/src/antlr/out/server/src/antlr/* ./server/src/antlr/out + run: npm run antlr4ng && npm run antlr4ngPre && mv ./server/src/antlr/out/server/src/antlr/* ./server/src/antlr/out - run: npm run compile - # - run: npm test + - run: xvfb-run -a npm test + if: runner.os == 'Linux' + - run: npm test + if: runner.os != 'Linux' diff --git a/.gitignore b/.gitignore index 4baa129..14d4ba0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ node_modules client/server .vscode-test sample*/** -*.vsix \ No newline at end of file +*.vsix +*.tsbuildinfo \ No newline at end of file diff --git a/.vscode-test.mjs b/.vscode-test.mjs new file mode 100644 index 0000000..d30c53a --- /dev/null +++ b/.vscode-test.mjs @@ -0,0 +1,10 @@ +// .vscode-test.mjs +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'client/out/test/**/*.test.js', + mocha: { + ui: 'tdd', + timeout: 4000 + } +}); diff --git a/.vscode/launch.json b/.vscode/launch.json index b2ca3b6..6bbcd08 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -46,6 +46,27 @@ "startRule": "startRule", "printParseTree": true, "visualParseTree": true + }, + { + "name": "Debug ANTLR4 Pre grammar", + "type": "antlr-debug", + "request": "launch", + "input": "${file}", + "grammar": "server/src/antlr/vbapre.g4", + "startRule": "startRule", + "printParseTree": true, + "visualParseTree": true + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/client/out/test/index" + ], + "outFiles": ["${workspaceFolder}/client/out/test/**/*.test.js"] } ], "compounds": [ diff --git a/.vscodeignore b/.vscodeignore index 6528078..5af0b1c 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -23,6 +23,7 @@ contributing.md *.interp *.tokens *.vsix +*.tsbuildinfo # Samples and testing sample*/** diff --git a/client/package-lock.json b/client/package-lock.json index f2a7edd..090de57 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,7 +12,7 @@ "vscode-languageclient": "^9.0.1" }, "devDependencies": { - "@types/vscode": "^1.94.0", + "@types/vscode": "^1.96.0", "@vscode/test-electron": "^2.4.1" }, "engines": { @@ -20,9 +20,9 @@ } }, "node_modules/@types/vscode": { - "version": "1.94.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.94.0.tgz", - "integrity": "sha512-UyQOIUT0pb14XSqJskYnRwD2aG0QrPVefIfrW1djR+/J4KeFQ0i1+hjZoaAmeNf3Z2jleK+R2hv+EboG/m8ruw==", + "version": "1.96.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz", + "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==", "dev": true, "license": "MIT" }, @@ -44,14 +44,11 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -158,9 +155,9 @@ } }, "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", "engines": { @@ -207,9 +204,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -253,13 +250,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { diff --git a/client/package.json b/client/package.json index db42369..0fe610f 100644 --- a/client/package.json +++ b/client/package.json @@ -16,7 +16,7 @@ "vscode-languageclient": "^9.0.1" }, "devDependencies": { - "@types/vscode": "^1.94.0", + "@types/vscode": "^1.96.0", "@vscode/test-electron": "^2.4.1" } } diff --git a/client/src/syntaxes/vba.tmLanguage.yaml b/client/src/syntaxes/vba.tmLanguage.yaml index 092734f..a2e8a88 100644 --- a/client/src/syntaxes/vba.tmLanguage.yaml +++ b/client/src/syntaxes/vba.tmLanguage.yaml @@ -377,7 +377,7 @@ repository: repository: argsVariable: name: meta.arguments.argsVariable.vba - match: (?i),?\s*((?:Optional\s+)?(?:(?:ByVal|ByRef)\s+)?)?([a-z][a-z0-9_]*)(?:\s+(as\s+[a-z][a-z0-9_]*))?(\s*=\s*[^,)]+)? + match: (?i),?\s*((?:Optional\s+)?(?:(?:ByVal|ByRef)\s+)?)?([a-z][a-z0-9_]*)(\(\))?(?:\s+(as\s+[a-z][a-z0-9_]*))?(\s*=\s*[^,)]+)? # Attempted replacing \s with (?:\s+|\s*_\s*\n) to consume a space or a line ending but it refuses to play the game. # match: ~~ doesn't work (?i),?(?:\s+|\s*_\s*\n)*((?:Optional(?:\s+|\s*_\s*\n)+)?(?:(?:ByVal|ByRef)(?:\s+|\s*_\s*\n)+)?)?([a-z][a-z0-9_]*)(?:(?:\s+|\s*_\s*\n)+(as(?:\s+|\s*_\s*\n)+[a-z][a-z0-9_]*))?((?:\s+|\s*_\s*\n)*=(?:\s+|\s*_\s*\n)*[^,\n)]+)? # match: ~~ all broken (?i),?(?:\s*_\s*\n)*((?:Optional(?:\s+(?:\s*_\s*\n)*))?(?:(?:ByVal|ByRef)(?:\s+(?:\s*_\s*\n)*))?)?([a-z][a-z0-9_]*)(?:(?:\s+(?:\s*_\s*\n)*)(as\(?:\s+(?:\s*_\s*\n)*)[a-z][a-z0-9_]*))?((?:\s*_\s*\n)*=(?:\s*_\s*\n)*[^,)]+)? @@ -428,7 +428,7 @@ repository: patterns: - include: "#lineContinuation" apostropheComments: - name: comment.line.apostropheXX.vba + name: comment.line.apostrophe.vba match: (?i)\s*'.* remarkComments: name: comment.line.remark.vba @@ -460,6 +460,7 @@ repository: patterns: - include: "#comments" - include: "#literals" + - include: "#operators" moduleOption: name: keyword.control.vba @@ -673,7 +674,7 @@ repository: repository: variableDeclaration: name: storage.var-declaration.vba - match: (?i)^\s*(Dim|Public|Private)\s+([a-z][a-z0-9_]*)(\(.*\))?(\s+As(?:\s+New)?\s+[A-Z][A-Z0-9_]*)? + match: (?i)^\s*(Dim|Public|Private|Global)\s+([a-z][a-z0-9_]*)([&%#!@$^])?(\(.*\))?(\s+As(?:\s+New)?\s+[A-Z][A-Z0-9_]*)? captures: 1: # Dim|Private @@ -682,28 +683,34 @@ repository: # varName name: variable.other.readwrite.vba 3: + # Type hint? + name: support.type.primitive.vba + 4: # Array bounds? patterns: - include: "#language" - 4: + 5: # As Type patterns: - include: "#types" constDeclaration: name: storage.const-declaration.vba - match: "^\\s*((?i)(?:(?:Public|Private)\\s+)?Const)\\s+([A-Z][A-Z0-9_]*)((?i)\\s+As\\s+[A-Z][A-Z0-9_]*)?(.*)" + match: ^\s*((?i)(?:(?:Public|Private)\s+)?Const)\s+([A-Z][A-Z0-9_]*)([&%#!@$^])?((?i)\s+As\s+[A-Z][A-Z0-9_]*)?(.*) captures: 1: # Public|Private Const - name: constant.language.vba + name: storage.type.vba 2: # CONSTNAME - name: variable.other.constant.property + name: variable.other.constant.vba 3: + # Type hint? + name: support.type.primitive.vba + 4: # As Type patterns: - include: "#types" - 4: + 5: # = "some value" patterns: - include: "#language" diff --git a/client/src/test/completion.test.ts b/client/src/test/completion.test.ts index f355078..4d7470b 100644 --- a/client/src/test/completion.test.ts +++ b/client/src/test/completion.test.ts @@ -1,43 +1,43 @@ -/* -------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - * ------------------------------------------------------------------------------------------ */ +// /* -------------------------------------------------------------------------------------------- +// * Copyright (c) Microsoft Corporation. All rights reserved. +// * Licensed under the MIT License. See License.txt in the project root for license information. +// * ------------------------------------------------------------------------------------------ */ -import * as vscode from 'vscode'; -import * as assert from 'assert'; -import { getDocUri, activate } from './helper'; +// import * as vscode from 'vscode'; +// import * as assert from 'assert'; +// import { getDocUri, activate } from './helper'; -suite('Should do completion', () => { - const docUri = getDocUri('completion.txt'); +// suite('Should do completion', () => { +// const docUri = getDocUri('completion.txt'); - test('Completes JS/TS in txt file', async () => { - await testCompletion(docUri, new vscode.Position(0, 0), { - items: [ - { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, - { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } - ] - }); - }); -}); +// test('Completes JS/TS in txt file', async () => { +// await testCompletion(docUri, new vscode.Position(0, 0), { +// items: [ +// { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, +// { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } +// ] +// }); +// }); +// }); -async function testCompletion( - docUri: vscode.Uri, - position: vscode.Position, - expectedCompletionList: vscode.CompletionList -) { - await activate(docUri); +// async function testCompletion( +// docUri: vscode.Uri, +// position: vscode.Position, +// expectedCompletionList: vscode.CompletionList +// ) { +// await activate(docUri); - // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion - const actualCompletionList = (await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - docUri, - position - )) as vscode.CompletionList; +// // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion +// const actualCompletionList = (await vscode.commands.executeCommand( +// 'vscode.executeCompletionItemProvider', +// docUri, +// position +// )) as vscode.CompletionList; - assert.ok(actualCompletionList.items.length >= 2); - expectedCompletionList.items.forEach((expectedItem, i) => { - const actualItem = actualCompletionList.items[i]; - assert.equal(actualItem.label, expectedItem.label); - assert.equal(actualItem.kind, expectedItem.kind); - }); -} +// assert.ok(actualCompletionList.items.length >= 2); +// expectedCompletionList.items.forEach((expectedItem, i) => { +// const actualItem = actualCompletionList.items[i]; +// assert.equal(actualItem.label, expectedItem.label); +// assert.equal(actualItem.kind, expectedItem.kind); +// }); +// } diff --git a/client/src/test/diagnostics.test.ts b/client/src/test/diagnostics.test.ts index 1aa8a36..41469f9 100644 --- a/client/src/test/diagnostics.test.ts +++ b/client/src/test/diagnostics.test.ts @@ -8,20 +8,144 @@ import * as assert from 'assert'; import { getDocUri, activate } from './helper'; suite('Should get diagnostics', () => { - const docUri = getDocUri('diagnostics.txt'); + test('diagnostics.class.missingNameAttributeError', async () => { + await testDiagnostics(getDocUri('MissingAttributeClass.cls'), [ + { + message: 'Module missing attribute VB_NAME.', + range: toRange(1, 0, 1, 0), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + } + ]); + }); + + test('diagnostics.module.missingNameAttributeError', async () => { + await testDiagnostics(getDocUri('MissingAttributeModule.bas'), [ + { + message: 'Module missing attribute VB_NAME.', + range: toRange(1, 0, 1, 0), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + } + ]); + }); + + test('diagnostics.class.noOptionExplicitWarning', async () => { + await testDiagnostics(getDocUri('EmptyClass.cls'), [ + { + message: 'Option Explicit is missing from module header.', + range: toRange(11, 1, 11, 1), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + } + ]); + }); + + test('diagnostics.module.noOptionExplicitWarning', async () => { + await testDiagnostics(getDocUri('EmptyModule.bas'), [ + { + message: 'Option Explicit is missing from module header.', + range: toRange(2, 1, 2, 1), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + } + ]); + }); + + test('diagnostics.module.generalDiagnostics', async () => { + // Don't seem to be able to sort these. Good luck! + await testDiagnostics(getDocUri('Diagnostics.bas'), [ + { + message: 'Module duplicate attribute VB_Name.', + range: toRange(2, 0, 2, 33), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + }, + { + message: 'Unknown attribute \'VB_Creatable\' will be ignored.', + range: toRange(3, 0, 3, 30), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + }, + { + message: 'Unknown attribute \'VB_Foo\' will be ignored.', + range: toRange(4, 0, 4, 24), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + }, + { + message: 'The Do...Loop statement provides a more structured and flexible way to perform looping.', + range: toRange(10, 4, 10, 8), + severity: vscode.DiagnosticSeverity.Information, + source: 'ex' + }, + { + message: 'Unexpected duplicate operator.', + range: toRange(11, 15, 11, 16), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + }, + { + message: 'Enum declarations cannot appear below a Sub, Function, or Property declaration.', + range: toRange(28, 7, 32, 8), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + } + ]); + }); - test('Diagnoses uppercase texts', async () => { - await testDiagnostics(docUri, [ - { message: 'ANY is all uppercase.', range: toRange(0, 0, 0, 3), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }, - { message: 'ANY is all uppercase.', range: toRange(0, 14, 0, 17), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }, - { message: 'OS is all uppercase.', range: toRange(0, 18, 0, 20), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' } + test('diagnostics.class.generalDiagnostics', async () => { + // Don't seem to be able to sort these. Good luck! + await testDiagnostics(getDocUri('Diagnostics.cls'), [ + { + message: 'Module duplicate attribute VB_GlobalNameSpace.', + range: toRange(8, 0, 8, 36), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + }, + { + message: 'Module duplicate attribute VB_Exxposed.', + range: toRange(13, 0, 13, 29), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + }, + { + message: 'Unknown attribute \'VB_Exxposed\' will be ignored.', + range: toRange(12, 0, 12, 29), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + }, + { + message: 'Unknown attribute \'VB_Exxposed\' will be ignored.', + range: toRange(13, 0, 13, 29), + severity: vscode.DiagnosticSeverity.Warning, + source: 'ex' + }, + { + message: 'The Do...Loop statement provides a more structured and flexible way to perform looping.', + range: toRange(19, 4, 19, 8), + severity: vscode.DiagnosticSeverity.Information, + source: 'ex' + }, + { + message: 'Unexpected duplicate operator.', + range: toRange(20, 15, 20, 16), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + }, + { + message: 'Enum declarations cannot appear below a Sub, Function, or Property declaration.', + range: toRange(37, 7, 41, 8), + severity: vscode.DiagnosticSeverity.Error, + source: 'ex' + } ]); }); }); function toRange(sLine: number, sChar: number, eLine: number, eChar: number) { - const start = new vscode.Position(sLine, sChar); - const end = new vscode.Position(eLine, eChar); + const start = new vscode.Position(sLine - 1, sChar); + const end = new vscode.Position(eLine - 1, eChar); return new vscode.Range(start, end); } @@ -30,12 +154,12 @@ async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.D const actualDiagnostics = vscode.languages.getDiagnostics(docUri); - assert.equal(actualDiagnostics.length, expectedDiagnostics.length); + assert.equal(actualDiagnostics.length, expectedDiagnostics.length, "Count"); expectedDiagnostics.forEach((expectedDiagnostic, i) => { const actualDiagnostic = actualDiagnostics[i]; - assert.equal(actualDiagnostic.message, expectedDiagnostic.message); - assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range); - assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity); + assert.equal(actualDiagnostic.message, expectedDiagnostic.message, "Message"); + assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range, "Range"); + assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity, "Severity"); }); } \ No newline at end of file diff --git a/client/src/test/helper.ts b/client/src/test/helper.ts index 6e6724d..50a5e88 100644 --- a/client/src/test/helper.ts +++ b/client/src/test/helper.ts @@ -16,7 +16,7 @@ export let platformEol: string; */ export async function activate(docUri: vscode.Uri) { // The extensionId is `publisher.name` from package.json - const ext = vscode.extensions.getExtension('vscode-samples.lsp-sample')!; + const ext = vscode.extensions.getExtension('NotisDataAnalytics.vba-lsp')!; await ext.activate(); try { doc = await vscode.workspace.openTextDocument(docUri); diff --git a/client/src/test/index.ts b/client/src/test/index.ts index b9de4dc..cf7e13a 100644 --- a/client/src/test/index.ts +++ b/client/src/test/index.ts @@ -4,7 +4,7 @@ * ------------------------------------------------------------------------------------------ */ import * as path from 'path'; import * as Mocha from 'mocha'; -import * as glob from 'glob'; +import { glob } from 'glob'; export function run(): Promise { // Create the mocha test @@ -16,28 +16,25 @@ export function run(): Promise { const testsRoot = __dirname; - return new Promise((resolve, reject) => { - glob('**.test.js', { cwd: testsRoot }, (err, files) => { - if (err) { - return reject(err); - } + return glob.glob('**.test.js', { cwd: testsRoot }).then(async files => { - // Add files to the test suite - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + // Add files to the test suite + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); - try { - // Run the mocha test + try { + // Run the mocha test + await new Promise((resolve, reject) => { mocha.run(failures => { if (failures > 0) { - reject(new Error(`${failures} tests failed.`)); + reject(`${failures} tests failed.`); } else { resolve(); } }); - } catch (err) { - console.error(err); - reject(err); - } - }); + }); + } catch (err) { + console.error(err); + throw err; + } }); } \ No newline at end of file diff --git a/client/testFixture/Diagnostics.bas b/client/testFixture/Diagnostics.bas new file mode 100644 index 0000000..eedef31 --- /dev/null +++ b/client/testFixture/Diagnostics.bas @@ -0,0 +1,32 @@ +Attribute VB_Name = "Diagnostics" +Attribute VB_Name = "Diagnostics" +Attribute VB_Creatable = False +Attribute VB_Foo = False + +Option Explicit + +Public Sub Foo() + Dim i As Long + While True + i = i ++ 1 + i = i <> 1 + Wend +End Sub + +Sub Identifier() +Attribute Identifier.VB_Description = "Dosctring." +Attribute Identifier.VB_Description = "Dosctring." +' Dosctring. +' +' Args: +' param1: +' +' Raises: +' +End Sub + +Public Enum EnumFoo + Enum1 + Enum2 + Enum3 +End Enum diff --git a/client/testFixture/Diagnostics.cls b/client/testFixture/Diagnostics.cls new file mode 100644 index 0000000..70ec3a7 --- /dev/null +++ b/client/testFixture/Diagnostics.cls @@ -0,0 +1,41 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "Diagnostics" +Attribute VB_Description = "Class description goes here" +Attribute VB_GlobalNameSpace = False +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = False +Attribute VB_Exxposed = False +Attribute VB_Exxposed = False + +Option Explicit + +Public Sub Foo() + Dim i As Long + While True + i = i ++ 1 + i = i <> 1 + Wend +End Sub + +Sub Identifier() +Attribute Identifier.VB_Description = "Dosctring." +Attribute Identifier.VB_Description = "Dosctring." +' Dosctring. +' +' Args: +' param1: +' +' Raises: +' +End Sub + +Public Enum EnumFoo + Enum1 + Enum2 + Enum3 +End Enum diff --git a/client/testFixture/EmptyClass.cls b/client/testFixture/EmptyClass.cls new file mode 100644 index 0000000..0f2affe --- /dev/null +++ b/client/testFixture/EmptyClass.cls @@ -0,0 +1,29 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "EmptyClass" +Attribute VB_Description = "Class description goes here" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = False +' Copyright 2024 Class description goes here +' +' Permission is hereby granted, free of charge, to any person obtaining a copy +' of this software and associated documentation files (the "Software"), to deal +' in the Software without restriction, including without limitation the rights +' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +' copies of the Software, and to permit persons to whom the Software is +' furnished to do so, subject to the following conditions: +' +' The above copyright notice and this permission notice shall be included in +' all copies or substantial portions of the Software. +' +' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +' IN THE SOFTWARE. diff --git a/client/testFixture/EmptyModule.bas b/client/testFixture/EmptyModule.bas new file mode 100644 index 0000000..e720881 --- /dev/null +++ b/client/testFixture/EmptyModule.bas @@ -0,0 +1,21 @@ +Attribute VB_Name = "EmptyModule" +' Copyright 2024 Sam Vanderslink +' +' Permission is hereby granted, free of charge, to any person obtaining a copy +' of this software and associated documentation files (the "Software"), to deal +' in the Software without restriction, including without limitation the rights +' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +' copies of the Software, and to permit persons to whom the Software is +' furnished to do so, subject to the following conditions: +' +' The above copyright notice and this permission notice shall be included in +' all copies or substantial portions of the Software. +' +' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +' IN THE SOFTWARE. + diff --git a/client/testFixture/MissingAttributeClass.cls b/client/testFixture/MissingAttributeClass.cls new file mode 100644 index 0000000..4f97aa8 --- /dev/null +++ b/client/testFixture/MissingAttributeClass.cls @@ -0,0 +1 @@ +Option Explicit diff --git a/client/testFixture/MissingAttributeModule.bas b/client/testFixture/MissingAttributeModule.bas new file mode 100644 index 0000000..4f97aa8 --- /dev/null +++ b/client/testFixture/MissingAttributeModule.bas @@ -0,0 +1 @@ +Option Explicit diff --git a/client/testFixture/completion.txt b/client/testFixture/completion.txt deleted file mode 100644 index e69de29..0000000 diff --git a/client/testFixture/diagnostics.txt b/client/testFixture/diagnostics.txt deleted file mode 100644 index d910cfb..0000000 --- a/client/testFixture/diagnostics.txt +++ /dev/null @@ -1 +0,0 @@ -ANY browsers, ANY OS. \ No newline at end of file diff --git a/client/tsconfig.tsbuildinfo b/client/tsconfig.tsbuildinfo deleted file mode 100644 index d5e29aa..0000000 --- a/client/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/extension.ts","./src/test/completion.test.ts","./src/test/diagnostics.test.ts","./src/test/helper.ts","./src/test/index.ts","./src/test/runtest.ts"],"version":"5.6.3"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4ba8b97..39d10ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,52 +1,65 @@ { "name": "vba-lsp", - "version": "1.4.4", + "version": "1.4.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.4.4", + "version": "1.4.5", "hasInstallScript": true, "license": "MIT", "dependencies": { - "antlr4ng": "^3.0.7" + "antlr4ng": "^3.0.14" }, "devDependencies": { - "@types/mocha": "^10.0.9", - "@types/node": "^22.7.5", - "@typescript-eslint/eslint-plugin": "^8.8.1", - "@typescript-eslint/parser": "^8.8.1", + "@types/mocha": "^10.0.10", + "@types/node": "^22.10.6", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.4.1", "antlr4ng-cli": "^2.0.0", - "eslint": "^9.12.0", + "eslint": "^9.18.0", "js-yaml": "^4.1.0", - "mocha": "^10.7.3", - "typescript": "^5.6.3" + "mocha": "^10.8.2", + "typescript": "^5.7.3", + "vscode-tmgrammar-test": "^0.1.3" }, "engines": { "vscode": "^1.63.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -54,13 +67,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -93,19 +106,22 @@ } }, "node_modules/@eslint/core": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", - "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "license": "MIT", "dependencies": { @@ -151,9 +167,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", - "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -161,9 +177,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -171,12 +187,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -184,9 +201,9 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", - "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -194,19 +211,33 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", - "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.0", + "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -222,9 +253,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -235,6 +266,62 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -273,6 +360,17 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -280,6 +378,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -288,38 +393,38 @@ "license": "MIT" }, "node_modules/@types/mocha": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", - "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "version": "22.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz", + "integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -330,25 +435,21 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4" }, "engines": { @@ -359,23 +460,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -386,16 +483,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -404,16 +501,15 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", "dev": true, "license": "MIT", "engines": { @@ -425,20 +521,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -447,23 +543,21 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -473,18 +567,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.20.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -494,10 +589,64 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz", + "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", + "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^7.0.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -517,6 +666,16 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -545,13 +704,16 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { @@ -571,9 +733,9 @@ } }, "node_modules/antlr4ng": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/antlr4ng/-/antlr4ng-3.0.7.tgz", - "integrity": "sha512-e9VzrS6fErCUTmkZX2eKEMCsmYY87gVhmHkzmOXtUDgvwGhivUvtiHqlOByDx4Nd1PIQ0lMlhflOWEz6cGXqKA==", + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/antlr4ng/-/antlr4ng-3.0.14.tgz", + "integrity": "sha512-EVEn3B3zpxgbq/731dhwMYCls9e8mAudBvo479hoXbX/yTL24Do1HNZEU+v1U6GayIFrow5EcHMdyXqqRXTtBw==", "license": "BSD-3-Clause", "peerDependencies": { "antlr4ng-cli": "^2.0.0" @@ -616,6 +778,27 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -629,6 +812,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -659,6 +876,57 @@ "dev": true, "license": "ISC" }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -699,6 +967,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -724,62 +1005,168 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -792,9 +1179,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -839,13 +1226,34 @@ "node": ">=0.3.1" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, + "node_modules/enhanced-resolve": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -870,32 +1278,32 @@ } }, "node_modules/eslint": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.12.0.tgz", - "integrity": "sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.6.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.12.0", - "@eslint/plugin-kit": "^0.2.0", - "@humanfs/node": "^0.16.5", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.1", + "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.1.0", - "eslint-visitor-keys": "^4.1.0", - "espree": "^10.2.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -909,8 +1317,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -931,9 +1338,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -972,9 +1379,9 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -984,6 +1391,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -998,15 +1418,15 @@ } }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1016,9 +1436,9 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1082,9 +1502,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -1092,25 +1512,12 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1126,9 +1533,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, "license": "ISC", "dependencies": { @@ -1203,12 +1610,29 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1242,50 +1666,37 @@ } }, "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, "node_modules/globals": { @@ -1301,6 +1712,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1328,6 +1746,62 @@ "he": "bin/he" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1338,6 +1812,13 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -1430,6 +1911,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1463,6 +1957,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1470,32 +1971,100 @@ "dev": true, "license": "ISC" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" } }, - "node_modules/json-buffer": { + "node_modules/istanbul-lib-report": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -1504,6 +2073,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1528,6 +2110,16 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1568,6 +2160,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1592,6 +2207,16 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -1608,10 +2233,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", "dev": true, "license": "MIT", "dependencies": { @@ -1644,6 +2279,56 @@ "node": ">= 14.0.0" } }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mocha/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -1657,6 +2342,34 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1673,6 +2386,53 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1707,6 +2467,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1725,51 +2501,157 @@ "node": ">= 0.8.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/ora": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", + "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.3.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/ora/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/ora/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" }, "engines": { - "node": ">=6" - } - }, + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", + "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1780,6 +2662,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1790,6 +2682,23 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1813,6 +2722,13 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1854,6 +2770,22 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1887,6 +2819,30 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1923,24 +2879,10 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT" }, "node_modules/semver": { @@ -1966,6 +2908,13 @@ "randombytes": "^2.1.0" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1989,7 +2938,65 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", @@ -2004,7 +3011,54 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -2017,6 +3071,16 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2031,24 +3095,88 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -2064,16 +3192,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/type-check": { @@ -2090,9 +3218,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2104,9 +3232,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" }, @@ -2120,6 +3248,196 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-textmate": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-7.0.4.tgz", + "integrity": "sha512-9hJp0xL7HW1Q5OgGe03NACo7yiCTMEk3WU/rtKXUbncLtdg6rVVNJnHwD88UhbIYU2KoxY0Dih0x+kIsmUKn2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-tmgrammar-test": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/vscode-tmgrammar-test/-/vscode-tmgrammar-test-0.1.3.tgz", + "integrity": "sha512-Wg6Pz+ePAT1O+F/A1Fc4wS5vY2X+HNtgN4qMdL+65NLQYd1/zdDWH4fhwsLjX8wTzeXkMy49Cr4ZqWTJ7VnVxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bottleneck": "^2.19.5", + "chalk": "^2.4.2", + "commander": "^9.2.0", + "diff": "^4.0.2", + "glob": "^7.1.6", + "vscode-oniguruma": "^1.5.1", + "vscode-textmate": "^7.0.1" + }, + "bin": { + "vscode-tmgrammar-snap": "dist/snapshot.js", + "vscode-tmgrammar-test": "dist/unit.js" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-tmgrammar-test/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/vscode-tmgrammar-test/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2154,6 +3472,25 @@ "license": "Apache-2.0" }, "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", @@ -2171,6 +3508,64 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2189,32 +3584,32 @@ } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -2233,6 +3628,51 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index cdeff67..87cf6ce 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "icon": "images/vba-lsp-icon.png", "author": "SSlinky", "license": "MIT", - "version": "1.4.4", + "version": "1.4.5", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" @@ -68,6 +68,28 @@ "default": true, "description": "The language server should warn when Option Explicit is not present." }, + "vbaLanguageServer.environment.os": { + "scope": "resource", + "type": "string", + "enum": [ + "Win16", + "Win32", + "Win64", + "Mac" + ], + "default": "Win64", + "description": "Controls which compiler sections to display." + }, + "vbaLanguageServer.environment.version": { + "scope": "resource", + "type": "string", + "enum": [ + "Vba6", + "Vba7" + ], + "default": "Vba7", + "description": "Controls which compiler sections to display." + }, "vbaLanguageServer.trace.server": { "scope": "window", "type": "string", @@ -96,27 +118,37 @@ ] }, "scripts": { - "vscode:prepublish": "npm run antlr4ng && npm run compile", + "vscode:prepublish": "npm run antlr4ngPre && npm run antlr4ng && npm run compile", "compile": "tsc -b", "watch": "tsc -b -w", "lint": "eslint ./client/src ./server/src --ext .ts,.tsx", "postinstall": "cd client && npm install && cd ../server && npm install && cd ..", - "test": "sh ./scripts/e2e.sh", "textMate": "npx js-yaml client/src/syntaxes/vba.tmLanguage.yaml > client/out/vba.tmLanguage.json", - "antlr4ng": "antlr4ng -Dlanguage=TypeScript -visitor ./server/src/antlr/vba.g4 -o ./server/src/antlr/out/" + "antlr4ng": "antlr4ng -Dlanguage=TypeScript -visitor ./server/src/antlr/vba.g4 -o ./server/src/antlr/out/", + "antlr4ngPre": "antlr4ng -Dlanguage=TypeScript -visitor ./server/src/antlr/vbapre.g4 -o ./server/src/antlr/out/", + "test": "npm run tmSnapTest && npm run tmUnitTest && npm run vsctest", + "testsh": "sh ./scripts/e2e.sh", + "testps": "powershell ./scripts/e2e.ps1", + "tmUnitTest": "vscode-tmgrammar-test ./test/textmate/**/*.vba", + "tmSnapTest": "vscode-tmgrammar-snap ./test/textmate/snapshot/*.??s", + "tmSnapUpdate": "vscode-tmgrammar-snap --updateSnapshot ./test/textmate/snapshot/*.??s", + "vsctest": "vscode-test" }, "dependencies": { - "antlr4ng": "^3.0.7" + "antlr4ng": "^3.0.14" }, "devDependencies": { - "@types/mocha": "^10.0.9", - "@types/node": "^22.7.5", - "@typescript-eslint/eslint-plugin": "^8.8.1", - "@typescript-eslint/parser": "^8.8.1", + "@types/mocha": "^10.0.10", + "@types/node": "^22.10.6", + "@typescript-eslint/eslint-plugin": "^8.20.0", + "@typescript-eslint/parser": "^8.20.0", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.4.1", "antlr4ng-cli": "^2.0.0", - "eslint": "^9.12.0", + "eslint": "^9.18.0", "js-yaml": "^4.1.0", - "mocha": "^10.7.3", - "typescript": "^5.6.3" + "mocha": "^10.8.2", + "typescript": "^5.7.3", + "vscode-tmgrammar-test": "^0.1.3" } } diff --git a/scripts/e2e.ps1 b/scripts/e2e.ps1 new file mode 100644 index 0000000..8b94b9d --- /dev/null +++ b/scripts/e2e.ps1 @@ -0,0 +1,3 @@ +$ENV:CODE_TESTS_PATH="$(Get-Location)\client\out\test" +$ENV:CODE_TESTS_WORKSPACE="$(Get-Location)\client\testFixture" +Invoke-Expression "node $(Get-Location)\client\out\test\runTest.js" diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index a012b8a..4ec4a2e 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -42,7 +42,7 @@ classBeginBlock ; beginBlockConfigElement - : endOfLine+ (OBJECT '.')? '_'? ambiguousIdentifier WS? EQ WS? (('-'? literalExpression) | FILEOFFSET) + : endOfLine+ (OBJECT '.')? '_'? ambiguousIdentifier WS? eqOperator WS? (('-'? literalExpression) | FILEOFFSET) | formBeginBlock | beginPropertyBlock ; @@ -56,7 +56,7 @@ formVersionIdentification : VERSION WS FLOATLITERAL ; formObjectAssign - : endOfLine+ OBJECT WS? EQ WS? STRINGLITERAL (';' WS? STRINGLITERAL)? + : endOfLine+ OBJECT WS? eqOperator WS? STRINGLITERAL (';' WS? STRINGLITERAL)? ; formBeginBlock : endOfLine+ BEGIN WS (GUID | (ambiguousIdentifier '.' ambiguousIdentifier)) WS ambiguousIdentifier beginBlockConfigElement+ endOfLine+ END @@ -76,26 +76,41 @@ classModule // Compare STRINGLITERAL to quoted-identifier proceduralModuleHeader - : endOfLine* nameAttr? + : (endOfLine* proceduralModuleAttr)* ; -classModuleHeader: (endOfLine+ (classAttr | nameAttr | ignoredAttr))* WS?; + +proceduralModuleAttr + : nameAttr + | ignoredProceduralAttr + ; + +ignoredProceduralAttr + : classAttr + | ignoredAttr + ; + +classModuleHeader: (endOfLine+ (classAttr | nameAttr | ignoredClassAttr))* WS?; // VBA Library Projects are allowed to have GoobalNamespace and creatable as true. classAttr - : ATTRIBUTE WS? VB_DESCRIPTION WS? EQ WS? STRINGLITERAL - | ATTRIBUTE WS? VB_GLOBALNAMESPACE WS? EQ WS? booleanLiteralIdentifier - | ATTRIBUTE WS? VB_CREATABLE WS? EQ WS? booleanLiteralIdentifier - | ATTRIBUTE WS? VB_PREDECLAREDID WS? EQ WS? booleanLiteralIdentifier - | ATTRIBUTE WS? VB_EXPOSED WS? EQ WS? booleanLiteralIdentifier - | ATTRIBUTE WS? VB_CUSTOMIZABLE WS? EQ WS? booleanLiteralIdentifier + : ATTRIBUTE WS? VB_DESCRIPTION WS? eqOperator WS? STRINGLITERAL + | ATTRIBUTE WS? VB_GLOBALNAMESPACE WS? eqOperator WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_CREATABLE WS? eqOperator WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_PREDECLAREDID WS? eqOperator WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_EXPOSED WS? eqOperator WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_CUSTOMIZABLE WS? eqOperator WS? booleanLiteralIdentifier + ; + +ignoredClassAttr + : ignoredAttr ; ignoredAttr - : ATTRIBUTE WS? ambiguousIdentifier WS? EQ WS? expression + : ATTRIBUTE WS? ambiguousIdentifier WS? eqOperator WS? expression ; nameAttr - : ATTRIBUTE WS? VB_NAME WS? EQ WS? STRINGLITERAL + : ATTRIBUTE WS? VB_NAME WS? eqOperator WS? STRINGLITERAL ; //--------------------------------------------------------------------------------------- @@ -219,7 +234,7 @@ commonModuleDeclarationElement // 5.2.3.1 Module Variable Declaration Lists // Added variableHelpAttribute, not in MS-VBAL moduleVariableDeclaration - : publicVariableDecalation + : publicVariableDeclaration | privateVariableDeclaration | variableHelpAttribute ; @@ -227,8 +242,15 @@ moduleVariableDeclaration variableHelpAttribute : ATTRIBUTE WS ambiguousIdentifier '.' VB_VARHELPID WS? '=' WS? '-'? INTEGERLITERAL ; + +// ---------------------------- +// TODO!! Global, as well as staticVariableDeclaration and localVariableDeclaration should parse +// with the more flexible moduleVariableDeclarationList but should raise diagnostic if not done right. +// Similarly, WithEvents shoudn't require a type but should raise diag if one isn't provided. +// ---------------------------- + globalVariableDeclaration: GLOBAL WS variableDeclarationList; -publicVariableDecalation: PUBLIC (WS SHARED)? WS moduleVariableDeclarationList; +publicVariableDeclaration: PUBLIC (WS SHARED)? WS moduleVariableDeclarationList; privateVariableDeclaration: ((PRIVATE | DIM) wsc) (SHARED wsc)? moduleVariableDeclarationList; moduleVariableDeclarationList: (witheventsVariableDcl | variableDcl) (wsc? ',' wsc? (witheventsVariableDcl | variableDcl))*; variableDeclarationList: variableDcl (wsc? ',' wsc? variableDcl)*; @@ -281,8 +303,8 @@ constItem : typedNameConstItem | untypedNameConstItem ; -typedNameConstItem: typedName wsc? EQ wsc? constantExpression; -untypedNameConstItem: ambiguousIdentifier (wsc constAsClause)? wsc? EQ wsc? constantExpression; +typedNameConstItem: typedName wsc? eqOperator wsc? constantExpression; +untypedNameConstItem: ambiguousIdentifier (wsc constAsClause)? wsc? eqOperator wsc? constantExpression; constAsClause: AS wsc builtinType; // 5.2.3.3 User Defined Type Declarations @@ -323,7 +345,7 @@ enumElement : remStatement | enumMember ; -enumMember: untypedName (wsc? EQ wsc? constantExpression)?; +enumMember: untypedName (wsc? eqOperator wsc? constantExpression)?; // 5.2.3.5 External Procedure Declaration publicExternalProcedureDeclaration: (PUBLIC wsc)? externalProcDcl; @@ -598,7 +620,7 @@ nestedForStatement | explicitForEachStatement ; forClause - : FOR wsc boundVariableExpression wsc? EQ wsc? startValue wsc TO wsc endValue (wsc stepClause)?; + : FOR wsc boundVariableExpression wsc? eqOperator wsc? startValue wsc TO wsc endValue (wsc stepClause)?; startValue: expression; endValue: expression; stepClause: STEP wsc stepIncrement; @@ -686,12 +708,12 @@ rangeClause | IS? wsc comparisonOperator wsc? expression; selectExpression: expression; comparisonOperator - : EQ + : eqOperator | NEW - | LT - | GT - | LEQ - | GEQ + | ltOperator + | gtOperator + | leqOperator + | geqOperator ; // 5.4.2.11 Stop Statement @@ -751,6 +773,7 @@ dataManipulationStatement ; // 5.4.3.1 Local Variable Declarations +// TODO: Shared is not listed as a keyword in VBA and the IDE removes it. localVariableDeclaration: DIM wsc? SHARED? wsc? variableDeclarationList; staticVariableDeclaration: STATIC wsc variableDeclarationList; @@ -785,7 +808,7 @@ eraseList: eraseElement (wsc? ',' wsc? eraseElement)*; eraseElement: lExpression; // 5.4.3.5 Mid/MidB/Mid$/MidB$ Statement -midStatement: modeSpecifier wsc? '(' wsc? stringArgument wsc? ',' wsc? startMid wsc? (',' wsc? length)? ')' wsc? EQ wsc? expression; +midStatement: modeSpecifier wsc? '(' wsc? stringArgument wsc? ',' wsc? startMid wsc? (',' wsc? length)? ')' wsc? eqOperator wsc? expression; modeSpecifier : MID | MIDB @@ -798,16 +821,16 @@ startMid: integerExpression; length: integerExpression; // 5.4.3.6 LSet Statement -lsetStatement: LSET wsc? boundVariableExpression wsc? EQ wsc? expression; +lsetStatement: LSET wsc? boundVariableExpression wsc? eqOperator wsc? expression; // 5.4.3.7 RSet Statement -rsetStatement: RSET wsc? boundVariableExpression wsc? EQ wsc? expression; +rsetStatement: RSET wsc? boundVariableExpression wsc? eqOperator wsc? expression; // 5.4.3.8 Let Statement -letStatement: (LET wsc)? lExpression wsc? EQ wsc? expression; +letStatement: (LET wsc)? lExpression wsc? eqOperator wsc? expression; // 5.4.3.9 Set Statement -setStatement: SET wsc lExpression wsc? EQ wsc? expression; +setStatement: SET wsc lExpression wsc? eqOperator wsc? expression; // 5.4.4 Error Handling Statements errorHandlingStatement @@ -871,7 +894,7 @@ lock | LOCK wsc WRITE | LOCK wsc READ wsc WRITE ; -lenClause: LEN wsc EQ wsc recLength; +lenClause: LEN wsc eqOperator wsc recLength; recLength: expression; // 5.4.5.1.1 File Numbers @@ -948,9 +971,9 @@ variable: variableExpression; // Attribute Statement attributeStatement - : ATTRIBUTE WS ambiguousIdentifier '.' attributeDescName WS? EQ WS? STRINGLITERAL - | ATTRIBUTE WS ambiguousIdentifier '.' attributeUsrName WS? EQ WS? '-'? INTEGERLITERAL - | ATTRIBUTE WS ambiguousIdentifier '.' VB_PROCDATA '.' VB_INVOKE_FUNC WS EQ WS STRINGLITERAL + : ATTRIBUTE WS ambiguousIdentifier '.' attributeDescName WS? eqOperator WS? STRINGLITERAL + | ATTRIBUTE WS ambiguousIdentifier '.' attributeUsrName WS? eqOperator WS? '-'? INTEGERLITERAL + | ATTRIBUTE WS ambiguousIdentifier '.' VB_PROCDATA '.' VB_INVOKE_FUNC WS eqOperator WS STRINGLITERAL ; attributeDescName @@ -990,15 +1013,30 @@ expression | parenthesizedExpression | typeofIsExpression | newExpress - | expression wsc? POW wsc? expression + | expression wsc? powOperator wsc? expression | unaryMinusExpression - | expression wsc? (DIV | MULT) wsc? expression - | expression wsc? MOD wsc? expression - | expression wsc? (PLUS | MINUS) wsc? expression - | expression wsc? AMPERSAND wsc? expression - | expression wsc? (IS | LIKE | GEQ | LEQ | GT | LT | NEQ | EQ) wsc? expression + | expression wsc? (divOperator | multOperator) wsc? expression + | expression wsc? modOperator wsc? expression + | expression wsc? (plusOperator | minusOperator) wsc? expression + | expression wsc? ampOperator wsc? expression + | expression wsc? ( + IS + | LIKE + | geqOperator + | leqOperator + | gtOperator + | ltOperator + | neqOperator + | eqOperator + ) wsc? expression | notOperatorExpression - | expression wsc? (AND | OR | XOR | EQV | IMP) wsc? expression + | expression wsc? ( + andOperator + | orOperator + | xorOperator + | eqvOperator + | impOperator + ) wsc? expression | lExpression ; @@ -1509,6 +1547,115 @@ ambiguousKeyword | WIDTH ; +//--------------------------------------------------------------------------------------- +// Helpers + +// Operators rolled up like this allows parsing of multiple operators +// so that they can be detected and reported as a diagnostic. +anyOperator + : powOperator + | divOperator + | multOperator + | modOperator + | plusOperator + | minusOperator + | ampOperator + | isOperator + | likeOperator + | leqOperator + | gtOperator + | ltOperator + | neqOperator + | eqOperator + | andOperator + | orOperator + | orOperator + | xorOperator + | eqvOperator + | impOperator + ; + +powOperator + : POW (wsc? anyOperator)* + ; + +divOperator + : DIV (wsc? anyOperator)* + ; + +multOperator + : MULT (wsc? anyOperator)* + ; + +modOperator + : MOD (wsc? anyOperator)* + ; + +plusOperator + : PLUS (wsc? anyOperator)* + ; + +minusOperator + : MINUS (wsc? anyOperator)* + ; + +ampOperator + : AMPERSAND (wsc? anyOperator)* + ; + +isOperator + : IS (wsc? anyOperator)* + ; + +likeOperator + : LIKE (wsc? anyOperator)* + ; + +geqOperator + : GEQ (wsc? anyOperator)* + ; + +leqOperator + : LEQ (wsc? anyOperator)* + ; + +gtOperator + : GT (wsc? anyOperator)* + ; + +ltOperator + : LT (wsc? anyOperator)* + ; + +neqOperator + : NEQ (wsc? anyOperator)* + ; + +eqOperator + : EQ (wsc? anyOperator)* + ; + +andOperator + : AND (wsc? anyOperator)* + ; + +orOperator + : OR (wsc? anyOperator)* + ; + +xorOperator + : XOR (wsc? anyOperator)* + ; + +eqvOperator + : EQV (wsc? anyOperator)* + ; + +impOperator + : IMP (wsc? anyOperator)* + ; + + // keywords ABS : 'ABS' diff --git a/server/src/antlr/vbapre.g4 b/server/src/antlr/vbapre.g4 new file mode 100644 index 0000000..5e77e1a --- /dev/null +++ b/server/src/antlr/vbapre.g4 @@ -0,0 +1,227 @@ +grammar vbapre; + +options { + caseInsensitive = true; +} + +// PARSER RULES +startRule + : document EOF + // : anyOtherLine EOF + ; + +document + : (documentLines | compilerIfBlock)* + ; + +documentLines + : (anyOtherLine | endOfLine)+ + ; + +compilerIfBlock + : compilerConditionalBlock+ + compilerDefaultBlock? + compilerEndIfStatement + ; + +compilerConditionalBlock + : compilerConditionalStatement compilerBlockContent? + ; + +compilerDefaultBlock + : compilerElseStatement compilerBlockContent? + ; + +compilerConditionalStatement + : compilerIfStatement + | compilerElseIfStatement + ; + +compilerIfStatement + : IF WS? booleanExpression WS? THEN endOfStatement + ; + +compilerElseIfStatement + : ELSEIF WS? booleanExpression WS? THEN endOfStatement + ; + +compilerElseStatement + : ELSE endOfStatement + ; + +compilerEndIfStatement + : ENDIF endOfStatement? + ; + +compilerBlockContent + : (anyOtherLine | endOfLine)+ + ; + +// ************************* + +booleanExpression + : booleanPart+ + ; + +booleanPart + : WS? (AND | OR | XOR | EQV | IMP)? WS? NOT? WS? (compilerConstant | anyWord) + ; + +compilerConstant + : VBA6 + | VBA7 + | WIN16 + | WIN32 + | WIN64 + | MAC + ; + +anyWord + : ANYCHARS+ + ; + +anyOtherLine + : (WS* anyWord)+ //endOfLine? + ; + +endOfLine + : (WS* (NEWLINE | commentBody | remStatement))+ + ; + +endOfLineNoWs + : (NEWLINE | commentBody | remStatement) + ; + +endOfStatement + : (endOfLine | COLON)+ + ; + +commentBody: COMMENT; +remStatement: REMCOMMENT; + +// wsc: (WS | LINE_CONTINUATION)+; + + + + +// LEXER RULES + +NEWLINE + // Match at least one new line but then any number + // of blank lines, including white space. + : ([\r\n\u2028\u2029]) (WS* ([\r\n\u2028\u2029]))* + ; + +ELSE + : '#ELSE' + ; + +ELSEIF + : '#ELSE IF' + ; + +ENDIF + : '#END IF' + ; + +IF + : '#IF' + ; + +THEN + : 'THEN' + ; + +VBA6 + : 'VBA6' + ; + +VBA7 + : 'VBA7' + ; + +WIN16 + : 'WIN16' + ; + +WIN32 + : 'WIN32' + ; + +WIN64 + : 'WIN64' + ; + +MAC + : 'MAC' + ; + +REMCOMMENT + : COLON? REM WS (LINE_CONTINUATION | ~[\r\n\u2028\u2029])* + ; + +COMMENT + : SINGLEQUOTE (LINE_CONTINUATION | ~[\r\n\u2028\u2029])* + ; + +LINE_CONTINUATION + : WS UNDERSCORE WS? '\r'? '\n' -> channel(HIDDEN) + ; + +WS + : NBSP NBSP* + ; + +fragment NBSP + : [ \t\u0019\u3000] + ; + +fragment UNDERSCORE + : '_' + ; + +COLON + : ':' + ; + +SINGLEQUOTE + : '\'' + ; + +REM + : 'REM' + ; + +EQV + : 'EQV' + ; + +IMP + : 'IMP' + ; + +AND + : 'AND' + ; + +OR + : 'OR' + ; + +XOR + : 'XOR' + ; + +NOT + : 'NOT' + ; + + +// Any non-whitespace or new line characters. +ANYCHARS + : ANYCHAR + ; + +fragment ANYCHAR + : . + ; \ No newline at end of file diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts new file mode 100644 index 0000000..25293d7 --- /dev/null +++ b/server/src/capabilities/capabilities.ts @@ -0,0 +1,129 @@ +// Core +import { + Diagnostic, + Range, + SemanticTokenModifiers, + SemanticTokenTypes, + SymbolInformation, + SymbolKind +} from 'vscode-languageserver'; + +// Antlr +import { ParserRuleContext, TerminalNode } from 'antlr4ng'; + +// Project +import { SemanticToken } from '../capabilities/semanticTokens'; +import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; +import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, HasSemanticTokenCapability } from '../project/elements/base'; + + +abstract class BaseCapability { + element: BaseContextSyntaxElement + + constructor(element: BaseContextSyntaxElement) { + this.element = element; + } +} + + +export class FoldingRangeCapability extends BaseCapability { + foldingRangeKind?: FoldingRangeKind; + + get foldingRange(): FoldingRange { + return new FoldingRange(this.element.context.range, this.foldingRangeKind); + } + + constructor(element: BaseContextSyntaxElement, foldingRangeKind?: FoldingRangeKind) { + super(element); + this.foldingRangeKind = foldingRangeKind; + } +} + + +export class DiagnosticCapability extends BaseCapability { + diagnostics: Diagnostic[] = []; + evaluate: (...args: any[]) => Diagnostic[] + + constructor(element: BaseContextSyntaxElement, evaluate?: (...args: any[]) => Diagnostic[]) { + super(element); + this.evaluate = evaluate ?? (() => this.diagnostics); + } +} + + +export class SemanticTokenCapability extends BaseCapability { + semanticToken: SemanticToken; + + constructor(element: BaseContextSyntaxElement & HasSemanticTokenCapability, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[], range?: Range, tokLength?: number) { + super(element); + + const context = !!element.identifierCapability + ? element.identifierCapability.element.context + : element.context; + + const startLine = range?.start.line ?? context.range.start.line; + const startChar = range?.start.character ?? context.range.start.character; + const textLength = tokLength ?? context.text.length; + + this.semanticToken = new SemanticToken( + element, + startLine, + startChar, + textLength, + tokenType, + tokenModifiers + ); + } +} + + +export class SymbolInformationCapability extends BaseCapability { + private symbolKind: SymbolKind + + get SymbolInformation(): SymbolInformation { + const element = this.element as BaseIdentifyableSyntaxElement; + return SymbolInformation.create( + element.identifierCapability.name, + this.symbolKind, + element.context.range, + element.context.document.uri + ) + } + + constructor(element: BaseIdentifyableSyntaxElement, symbolKind: SymbolKind) { + super(element); + this.symbolKind = symbolKind; + } +} + +interface IdentifierArgs { + element: BaseContextSyntaxElement, + getNameContext?: () => ParserRuleContext | TerminalNode | null | undefined, + formatName?: (name: string) => string, + defaultName?: string; + defaultRange?: () => Range; +} + + +export class IdentifierCapability extends BaseCapability { + nameContext?: ParserRuleContext | TerminalNode | null; + range: Range; + name: string; + + constructor(args: IdentifierArgs) { + super(args.element); + + this.nameContext = (args.getNameContext ?? (() => args.element.context.rule))(); + + if (!!this.nameContext) { + // Use the context to set the values. + this.name = (args.formatName ?? ((name: string) => name))(this.nameContext.getText()); + this.range = this.nameContext.toRange(args.element.context.document); + } else { + // Use the defaults to set the values. + if (!args.defaultRange) throw new Error("Default range not optional where name context not found."); + this.name = (args.defaultName ?? "Unknown Element"); + this.range = !!args.defaultRange ? args.defaultRange() : args.element.context.range; + } + } +} \ No newline at end of file diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index cac929d..e1da9e1 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -1,81 +1,88 @@ -import { CodeDescription, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Position, Range, TextDocumentClientCapabilities } from 'vscode-languageserver'; +// Core +import { CodeDescription, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Position, Range } from 'vscode-languageserver'; -function hasDiagnosticRelatedInformationCapability(x: TextDocumentClientCapabilities) { - return !!(x && x.publishDiagnostics && x.publishDiagnostics.relatedInformation); -} - abstract class BaseDiagnostic implements Diagnostic { range: Range; + message: string severity?: DiagnosticSeverity | undefined; code?: string | number | undefined; codeDescription?: CodeDescription | undefined; source?: string | undefined; - abstract message: string; tags?: DiagnosticTag[] | undefined; relatedInformation?: DiagnosticRelatedInformation[] | undefined; data?: unknown; - constructor(range: Range) { + constructor(range: Range) + constructor(range: Range, message: string) + constructor(range: Range, message?: string) { this.range = range; + this.message = message ?? 'Generic diagnostic.'; } } export class MultipleOperatorsDiagnostic extends BaseDiagnostic { - message = "Unexpected duplicate operator"; + message = "Unexpected duplicate operator."; constructor(range: Range) { super(range); } } + export class WhileWendDeprecatedDiagnostic extends BaseDiagnostic { message = "The Do...Loop statement provides a more structured and flexible way to perform looping."; severity = DiagnosticSeverity.Information; constructor(range: Range) { - super(Range.create( - range.start, - Position.create(range.start.line, range.start.character + 4) - )); + super(Range.create(range.start, Position.create(range.start.line, range.start.character + 4))); } } + export class MissingAttributeDiagnostic extends BaseDiagnostic { - message: string; severity = DiagnosticSeverity.Error; - constructor(range: Range, attributeName: string) { - super(range); - this.message = `Module missing attribute ${attributeName}.`; + super(range, `Module missing attribute ${attributeName}.`); } } + export class DuplicateAttributeDiagnostic extends BaseDiagnostic { - message: string; severity = DiagnosticSeverity.Error; - constructor(range: Range, attributeName: string) { - super(range); - this.message = `Module duplicate attribute ${attributeName}.`; + super(range, `Module duplicate attribute ${attributeName}.`); } } + +// test export class DuplicateDeclarationDiagnostic extends BaseDiagnostic { - message = "Duplicate declaration in current scope"; + message = "Duplicate declaration in current scope."; severity = DiagnosticSeverity.Error; constructor(range: Range) { super(range); } } -export class IgnoredAttributeDiagnostic extends BaseDiagnostic { - message = "This attribute will be ignored."; - severity = DiagnosticSeverity.Information; + +// test +export class ShadowDeclarationDiagnostic extends BaseDiagnostic { + message = "Declaration is shadowed in the local scope."; + severity = DiagnosticSeverity.Error; constructor(range: Range) { super(range); } } + +export class IgnoredAttributeDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Warning; + constructor(range: Range, attributeName: string) { + super(range, `Unknown attribute '${attributeName}' will be ignored.`); + } +} + + export class MissingOptionExplicitDiagnostic extends BaseDiagnostic { message = "Option Explicit is missing from module header."; severity = DiagnosticSeverity.Warning; @@ -84,11 +91,18 @@ export class MissingOptionExplicitDiagnostic extends BaseDiagnostic { } } + export class ElementOutOfPlaceDiagnostic extends BaseDiagnostic { - message: string; severity = DiagnosticSeverity.Error; constructor(range: Range, elementName: string) { - super(range); - this.message = `${elementName}s cannot appear below a Sub, Function, or Property declaration.` + super(range, `${elementName}s cannot appear below a Sub, Function, or Property declaration.`); + } +} + + +export class LegacyFunctionalityDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Warning; + constructor(range: Range, functionalityType: string) { + super(range, `${functionalityType} are legacy functionality and should be avoided.`); } } \ No newline at end of file diff --git a/server/src/capabilities/folding.ts b/server/src/capabilities/folding.ts index da503c2..97c772b 100644 --- a/server/src/capabilities/folding.ts +++ b/server/src/capabilities/folding.ts @@ -1,5 +1,4 @@ -import { FoldingRange as LSFoldingRange } from 'vscode-languageserver'; -import { FoldableElement } from '../project/elements/special'; +import { FoldingRange as VscFoldingRange, Range } from 'vscode-languageserver'; /** * Enum of known range kinds @@ -21,7 +20,7 @@ export enum FoldingRangeKind { } -export class FoldingRange implements LSFoldingRange { +export class FoldingRange implements VscFoldingRange { /** * The zero-based line number from where the folded range starts. */ @@ -49,9 +48,9 @@ export class FoldingRange implements LSFoldingRange { */ kind?: string; - constructor(element: FoldableElement, foldingRangeKind?: FoldingRangeKind) { - this.startLine = element.range.start.line; - this.endLine = element.range.end.line; + constructor(range: Range, foldingRangeKind?: FoldingRangeKind) { + this.startLine = range.start.line; + this.endLine = range.end.line; this.kind = foldingRangeKind; } } \ No newline at end of file diff --git a/server/src/capabilities/semanticTokens.ts b/server/src/capabilities/semanticTokens.ts index 5725e33..0377afb 100644 --- a/server/src/capabilities/semanticTokens.ts +++ b/server/src/capabilities/semanticTokens.ts @@ -1,9 +1,23 @@ -import {InitializeResult, Range, SemanticTokenModifiers, SemanticTokenTypes, uinteger, _LanguagesImpl, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams} from 'vscode-languageserver'; -import { HasSemanticToken } from '../project/elements/base'; +// Core +import { + InitializeResult, + Range, + SemanticTokenModifiers, + SemanticTokenTypes, + uinteger, + SemanticTokens +} from 'vscode-languageserver'; + +// Antlr +import { ParserRuleContext } from 'antlr4ng/dist/ParserRuleContext'; + +// Project +import { BaseContextSyntaxElement, HasSemanticTokenCapability } from '../project/elements/base'; const registeredTokenTypes = new Map((Object.keys(SemanticTokenTypes) as (keyof typeof SemanticTokenTypes)[]).map((k, i) => ([k, i]))); const registeredTokenModifiers = new Map((Object.keys(SemanticTokenModifiers) as (keyof typeof SemanticTokenModifiers)[]).map((k, i) => ([k, 2**i]))); + export function activateSemanticTokenProvider(result: InitializeResult) { result.capabilities.semanticTokensProvider = { "full": true, @@ -14,18 +28,9 @@ export function activateSemanticTokenProvider(result: InitializeResult) { }; } -export function sortSemanticTokens(tokens: SemanticToken[]): SemanticToken[] { - return tokens.sort((a, b) => { - // Sort a before than b. - if ((a.line < b.line) || (a.line === b.line && a.char < b.char)) - return -1; - // Sort b before a. - if ((a.line > b.line) || (a.line === b.line && a.char > b.char)) - return 1; - // No difference. - return 0; - }); -} + +type SemanticElementType = HasSemanticTokenCapability + & BaseContextSyntaxElement; export class SemanticToken { line: uinteger; @@ -33,49 +38,18 @@ export class SemanticToken { length: uinteger; tokenType: uinteger; tokenModifiers: uinteger = 0; - element: HasSemanticToken; + element: SemanticElementType; - constructor(element: HasSemanticToken, line: uinteger, startChar: uinteger, length: uinteger, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[]) { + constructor(element: SemanticElementType, line: uinteger, startChar: uinteger, length: uinteger, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[]) { this.element = element; this.line = line; this.char = startChar; this.length = length; this.tokenType = registeredTokenTypes.get(tokenType)!; tokenModifiers.forEach((x) => this.tokenModifiers += registeredTokenModifiers.get(x) ?? 0); - } - - static create(element: HasSemanticToken): SemanticToken { - return new SemanticToken( - element, - element.identifier.range.start.line, - element.identifier.range.start.character, - element.identifier.context.getText().length, - element.tokenType, - element.tokenModifiers - ); - } - - toNewRange(range: Range): SemanticToken { - const token = new SemanticToken( - this.element, - range.start.line, - range.start.character, - this.length, - SemanticTokenTypes.class, - [] - ); - token.tokenType = this.tokenType; - token.tokenModifiers = this.tokenModifiers; - return token; } - toDeltaToken(line: uinteger = 0, startChar: uinteger = 0): uinteger[] { - const deltaLine = this.line - line; - const deltaChar = deltaLine === 0 ? this.char - startChar : this.char; - return [deltaLine, deltaChar, this.length, this.tokenType, this.tokenModifiers]; - } - - toSemanticTokensArray(reference?: SemanticToken): uinteger[] { + toUintegerArray(reference?: SemanticToken): uinteger[] { const line = this.line - (reference?.line ?? 0); const char = line === 0 ? this.char - reference!.char : this.char; return [ @@ -93,33 +67,35 @@ export class SemanticToken { * Tracks, sorts, and provides LSP response for SemanticTokens. */ export class SemanticTokensManager { - private _tokens: SemanticToken[] = []; + private tokens: SemanticToken[] = []; - private _tokensInRange = (range: Range) => - this._tokens.filter(token => token.element.isChildOf(range)); + private tokensInRange = (range: Range) => + this.tokens.filter(token => token.element.isChildOf(range)); - add(element: HasSemanticToken) { - this._tokens.push(SemanticToken.create(element)); + add(element: HasSemanticTokenCapability) { + this.tokens.push(element.semanticTokenCapability.semanticToken); } - // getSemanticTokens(params: SemanticTokensParams): SemanticTokens | null; - // getSemanticTokens(rangeParams: SemanticTokensRangeParams): SemanticTokens | null; - // getSemanticTokens(_?: SemanticTokensParams, rangeParams?: SemanticTokensRangeParams): SemanticTokens | null { getSemanticTokens(range?: Range): SemanticTokens | null { - // Get the range if we have one. - // const range: Range | undefined = rangeParams?.range; - // Filter and sort the semantic tokens. - const filteredTokens = range ? this._tokensInRange(range) : this._tokens; - const sortedTokens = sortSemanticTokens(filteredTokens); + const filteredTokens = range ? this.tokensInRange(range) : this.tokens; + const sortedTokens = this.sortSemanticTokens(filteredTokens); if (sortedTokens.length === 0) return null; // Get an array of SemanticTokens relative to previous token. - const packedData: uinteger[][] = sortedTokens.map((token, i) => - token.toSemanticTokensArray(i === 0 ? undefined : sortedTokens[i - 1])); + const relativeResult: uinteger[] = sortedTokens.map((token, i) => + token.toUintegerArray(i === 0 ? undefined : sortedTokens[i - 1])).flat(); // Return the flattened array. - return { data: ([] as uinteger[]).concat(...packedData) }; + return { data: relativeResult }; } -} + + sortSemanticTokens(tokens: SemanticToken[]): SemanticToken[] { + return tokens.sort((a, b) => { + if ((a.line < b.line) || (a.line === b.line && a.char < b.char)) return -1; + if ((a.line > b.line) || (a.line === b.line && a.char > b.char)) return 1; + return 0; + }); + } +} \ No newline at end of file diff --git a/server/src/capabilities/symbolInformation.ts b/server/src/capabilities/symbolInformation.ts deleted file mode 100644 index bf94b5c..0000000 --- a/server/src/capabilities/symbolInformation.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { NamedSyntaxElement } from '../project/elements/base'; - - -export class SymbolInformationFactory { - static create(element: NamedSyntaxElement, symbolKind: SymbolKind): SymbolInformation { - return SymbolInformation.create( - element.name, - symbolKind, - element.range, - element.uri - ); - } -} - diff --git a/server/src/capabilities/workspaceFolder.ts b/server/src/capabilities/workspaceFolder.ts index d42d09c..c85b575 100644 --- a/server/src/capabilities/workspaceFolder.ts +++ b/server/src/capabilities/workspaceFolder.ts @@ -1,15 +1,20 @@ +// Core import { ClientCapabilities, InitializeResult } from 'vscode-languageserver'; + +// Project import { LanguageServerConfiguration } from '../server'; +function hasWorkspaceFolderCapability(x: ClientCapabilities) { + return !!(x.workspace && !!x.workspace.workspaceFolders); +} + + export function hasConfigurationCapability(languageServerConfiguration: LanguageServerConfiguration) { const capabilities = languageServerConfiguration.params.capabilities; return !!(capabilities.workspace && !!capabilities.workspace.configuration); } -function hasWorkspaceFolderCapability(x: ClientCapabilities) { - return !!(x.workspace && !!x.workspace.workspaceFolders); -} export function activateWorkspaceFolderCapability(capabilities: ClientCapabilities, result: InitializeResult) { if (hasWorkspaceFolderCapability(capabilities)) { diff --git a/server/src/extensions/parserExtensions.ts b/server/src/extensions/parserExtensions.ts index 1f6052e..10e67ee 100644 --- a/server/src/extensions/parserExtensions.ts +++ b/server/src/extensions/parserExtensions.ts @@ -1,63 +1,242 @@ +// Core import { Range, SymbolKind } from 'vscode-languageserver'; import { TextDocument } from 'vscode-languageserver-textdocument'; -// import { ParserRuleContext } from 'antlr4ts'; - -// This extension throws a compiler error TS2693: 'ParserRuleContext' only refers to a type, but is being used as a value here. -// Can maybe review later down the track, but for now have just made it a private method on BaseSyntaxElement. -// declare module 'antlr4ts' { -// export interface ParserRuleContext { -// toRange(document: TextDocument): Range; -// } -// } - -// ParserRuleContext.prototype.toRange = function (document: TextDocument): Range { -// const startIndex = this.start.startIndex; -// const stopIndex = this.stop?.stopIndex ?? startIndex; -// return Range.create( -// document.positionAt(startIndex), -// document.positionAt(stopIndex) -// ); -// }; - - -// declare module '../antlr/out/vbaParser' { -// export interface BaseTypeContext { -// toSymbolKind(): SymbolKind; -// } - -// export interface ComplexTypeContext { -// toSymbolKind(): SymbolKind; -// } -// } - -// BaseTypeContext.prototype.toSymbolKind = function (): SymbolKind { -// return toSymbolKind(this); -// }; - -// ComplexTypeContext.prototype.toSymbolKind = function (): SymbolKind { -// return toSymbolKind(this); -// }; - -// function toSymbolKind(context: BaseTypeContext | ComplexTypeContext): SymbolKind { -// switch (context.text.toLocaleLowerCase()) { -// case 'boolean': -// return SymbolKind.Boolean; -// case 'byte': -// case 'string': -// return SymbolKind.String; -// case 'double': -// case 'currency': -// case 'integer': -// case 'long': -// case 'longPtr': -// case 'longLong': -// return SymbolKind.Number; -// case 'object': -// return SymbolKind.Object; -// default: -// return SymbolKind.Class; -// } -// } + +// Antlr +import { ParserRuleContext, TerminalNode } from 'antlr4ng'; + +// Project +import { CompilerConditionalStatementContext } from '../antlr/out/vbapreParser'; +import { + AmbiguousIdentifierContext, + BuiltinTypeContext, + ClassTypeNameContext, + ConstItemContext, + GlobalVariableDeclarationContext, + PrivateConstDeclarationContext, + PrivateVariableDeclarationContext, + PublicConstDeclarationContext, + PublicVariableDeclarationContext, + TypeSpecContext, + TypeSuffixContext, + VariableDclContext, + WitheventsVariableDclContext +} from '../antlr/out/vbaParser'; + + +declare module 'antlr4ng' { + interface ParserRuleContext { + /** Convert the node to a range. */ + toRange(doc: TextDocument): Range; + } + + interface TerminalNode { + /** Convert the node to a range. */ + toRange(doc: TextDocument): Range; + } +} + + +declare module '../antlr/out/vbapreParser' { + interface CompilerConditionalStatementContext { + vbaExpression(): string; + } +} + + +declare module '../antlr/out/vbaParser' { + interface PublicConstDeclarationContext { + /** Shortcut to get the declaration contexts. */ + declarationContexts(): ConstItemContext[]; + } + + interface PrivateConstDeclarationContext { + /** Shortcut to get the declaration contexts. */ + declarationContexts(): ConstItemContext[]; + } + + interface ConstItemContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): BuiltinTypeContext | TypeSuffixContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } + + interface PublicVariableDeclarationContext { + /** Shortcut the variable list */ + declarationContexts(): (VariableDclContext | WitheventsVariableDclContext)[]; + } + + interface PrivateVariableDeclarationContext { + /** Shortcut the variable list */ + declarationContexts(): (VariableDclContext | WitheventsVariableDclContext)[]; + } + + interface GlobalVariableDeclarationContext { + /** Shortcut the variable list */ + declarationContexts(): VariableDclContext[]; + } + + interface VariableDclContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } + + interface WitheventsVariableDclContext { + /** Shortcut the identifier as we know it will always exist. */ + ambiguousIdentifier(): AmbiguousIdentifierContext; + /** Shortcut to get the type context */ + typeContext(): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined; + /** Extension method to get the symbol kind. */ + toSymbolKind(): SymbolKind; + } +} + + +ParserRuleContext.prototype.toRange = function (doc: TextDocument): Range { + const startIndex = this.start?.start ?? 0; + const stopIndex = this.stop?.stop ?? startIndex; + return Range.create( + doc.positionAt(startIndex), + doc.positionAt(stopIndex + 1) + ); +}; + + +TerminalNode.prototype.toRange = function (doc: TextDocument): Range { + const startIndex = this.getPayload()?.start ?? 0; + const stopIndex = this.getPayload()?.stop ?? startIndex; + return Range.create( + doc.positionAt(startIndex), + doc.positionAt(stopIndex + 1) + ); +}; + + +CompilerConditionalStatementContext.prototype.vbaExpression = function (): string { + return (this.compilerIfStatement() ?? this.compilerElseIfStatement())! + .booleanExpression() + .getText() + .toLowerCase(); +}; + + +// Constants + +PublicConstDeclarationContext.prototype.declarationContexts = function (): ConstItemContext[] { + return this.moduleConstDeclaration() + .constDeclaration() + .constItemList() + .constItem(); +}; + + +PrivateConstDeclarationContext.prototype.declarationContexts = function (): ConstItemContext[] { + return this.moduleConstDeclaration() + .constDeclaration() + .constItemList() + .constItem(); +}; + + +ConstItemContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { + // A variable will always be typed or untyped. + return this.typedNameConstItem()?.typedName().ambiguousIdentifier() + ?? this.untypedNameConstItem()!.ambiguousIdentifier(); +}; + +ConstItemContext.prototype.typeContext = function (): BuiltinTypeContext | TypeSuffixContext | undefined { + return this.typedNameConstItem()?.typedName().typeSuffix() + ?? this.untypedNameConstItem()?.constAsClause()?.builtinType() +}; + + +// Variables + +PublicVariableDeclarationContext.prototype.declarationContexts = function (): (VariableDclContext | WitheventsVariableDclContext)[] { + const dims = this.moduleVariableDeclarationList(); + return [dims.witheventsVariableDcl(), dims.variableDcl()].flat(); +}; + + +PrivateVariableDeclarationContext.prototype.declarationContexts = function (): (VariableDclContext | WitheventsVariableDclContext)[] { + const dims = this.moduleVariableDeclarationList(); + return [dims.witheventsVariableDcl(), dims.variableDcl()].flat(); +}; + + +GlobalVariableDeclarationContext.prototype.declarationContexts = function (): VariableDclContext[] { + const varList = this.variableDeclarationList(); + return varList.variableDcl(); +}; + + +VariableDclContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifierContext { + // A variable will always be typed or untyped. + return this.typedVariableDcl()?.typedName().ambiguousIdentifier() + ?? this.untypedVariableDcl()!.ambiguousIdentifier(); +}; + + +VariableDclContext.prototype.typeContext = function (): TypeSpecContext | TypeSuffixContext | ClassTypeNameContext | undefined { + return this.typedVariableDcl()?.typedName().typeSuffix() + ?? this.untypedVariableDcl()?.asClause()?.asType()?.typeSpec() + ?? this.untypedVariableDcl()?.asClause()?.asAutoObject()?.classTypeName(); +}; + + +// SymbolKind + +ConstItemContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + + +WitheventsVariableDclContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + + +VariableDclContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this.typeContext()); +}; + + +function toSymbolKind(context: BuiltinTypeContext | TypeSuffixContext | TypeSpecContext | ClassTypeNameContext | undefined): SymbolKind { + switch (context?.getText().toLocaleLowerCase()) { + case undefined: + return SymbolKind.Class + case 'boolean': + return SymbolKind.Boolean; + case '$': // string + case 'byte': + case 'string': + return SymbolKind.String; + case '%': // integer + case '&': // long + case '^': // longlong + case '@': // decimal + case '!': // single + case '#': // double + case 'double': + case 'currency': + case 'integer': + case 'long': + case 'longPtr': + case 'longLong': + return SymbolKind.Number; + case 'object': + return SymbolKind.Object; + default: + return SymbolKind.Class; + } +} /** * const File: 1; diff --git a/server/src/extensions/stringExtensions.ts b/server/src/extensions/stringExtensions.ts index 9c43ace..f8fb6fb 100644 --- a/server/src/extensions/stringExtensions.ts +++ b/server/src/extensions/stringExtensions.ts @@ -2,6 +2,7 @@ interface String { stripQuotes(): string; } + String.prototype.stripQuotes = function (): string { const exp = /^"?(.*?)"?$/; return exp.exec(this.toString())![1]; diff --git a/server/src/project/document.ts b/server/src/project/document.ts index 5db51a4..2bf9ef2 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -1,44 +1,73 @@ -import { CancellationToken, Diagnostic, PublishDiagnosticsParams, SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { Workspace } from './workspace'; -import { FoldableElement } from './elements/special'; -import { HasDiagnosticCapability, HasSemanticToken, HasSymbolInformation, IdentifiableSyntaxElement, ScopeElement } from './elements/base'; +// Core import { Range, TextDocument } from 'vscode-languageserver-textdocument'; -import { SyntaxParser } from './parser/vbaSyntaxParser'; +import { CancellationToken, Diagnostic, DocumentDiagnosticReport, DocumentDiagnosticReportKind, SymbolInformation, SymbolKind } from 'vscode-languageserver'; + +// Antlr +import { ParseCancellationException, ParserRuleContext } from 'antlr4ng'; + +// Project +import { Workspace } from './workspace'; +import { Dictionary } from '../utils/helpers'; +import { SyntaxParser } from './parser/vbaParser'; import { FoldingRange } from '../capabilities/folding'; import { SemanticTokensManager } from '../capabilities/semanticTokens'; -import { ParseCancellationException } from 'antlr4ng'; +import { BaseContextSyntaxElement, + BaseSyntaxElement, + DeclarableElement, + HasDiagnosticCapability, + HasFoldingRangeCapability, + HasSemanticTokenCapability, + HasSymbolInformationCapability +} from './elements/base'; + +import { PropertyDeclarationElement, + PropertyGetDeclarationElement, + PropertyLetDeclarationElement, + PropertySetDeclarationElement +} from './elements/procedure'; + export interface DocumentSettings { maxDocumentLines: number; maxNumberOfProblems: number; doWarnOptionExplicitMissing: boolean; + environment: { + os: string; + version: string; + } } + +// TODO --------------------------------------------- +// * Create a special register property that registers the name +// normally and tracks to avoid readding duplicates (unless we have two gets). +// Consider making all variables get/set/let for simplicity in assignment. +// Obviously Const will only have a get. +// * Fix the rest of the scope and variable bits. +// * Write tests for it all. +// -------------------------------------------------- + + export abstract class BaseProjectDocument { readonly name: string; readonly workspace: Workspace; readonly textDocument: TextDocument; - protected _documentConfiguration?: DocumentSettings; - - protected _hasDiagnosticElements: HasDiagnosticCapability[] = []; - protected _unhandledNamedElements: [] = []; - protected _publicScopeDeclarations: Map = new Map(); - protected _documentScopeDeclarations: Map> = new Map(); + + protected diagnostics: Diagnostic[] = []; + protected documentConfiguration?: DocumentSettings; + protected documentScopeDeclarations: Map> = new Map(); + protected foldableElements: FoldingRange[] = []; + protected hasDiagnosticElements: HasDiagnosticCapability[] = []; + protected properties: Dictionary = new Dictionary(() => new PropertyDeclarationElement()); + protected redactedElements: BaseContextSyntaxElement[] = []; + protected semanticTokens: SemanticTokensManager = new SemanticTokensManager(); + protected symbolInformations: SymbolInformation[] = []; + protected unhandledNamedElements: [] = []; - protected _diagnostics: Diagnostic[] = []; - protected _elementParents: ScopeElement[] = []; - // protected _attributeElements: HasAttribute[] = []; - protected _foldableElements: FoldingRange[] = []; - protected _symbolInformations: SymbolInformation[] = []; - protected _semanticTokens: SemanticTokensManager = new SemanticTokensManager(); + abstract symbolKind: SymbolKind protected _isBusy = true; - // protected _hasParseResult = false; - abstract symbolKind: SymbolKind - - get isBusy() { - return this._isBusy; - } + get isBusy() { return this._isBusy; } get isOversize() { // Workaround for async getter. @@ -47,42 +76,38 @@ export abstract class BaseProjectDocument { )(); } - // get hasParseResult() { - // return this._hasParseResult; - // } - - get currentScopeElement() { - return this._elementParents.at(-1); + get redactedText() { + return this.subtractTextFromRanges(this.redactedElements.map(x => x.context.range)); } async getDocumentConfiguration(): Promise { // Get the stored configuration. - if (this._documentConfiguration) { - return this._documentConfiguration; + if (this.documentConfiguration) { + return this.documentConfiguration; } // Get the configuration from the client. if (this.workspace.hasConfigurationCapability) { - this._documentConfiguration = await this.workspace.requestDocumentSettings(this.textDocument.uri); - if (this._documentConfiguration) { - return this._documentConfiguration; + this.documentConfiguration = await this.workspace.requestDocumentSettings(this.textDocument.uri); + if (this.documentConfiguration) { + return this.documentConfiguration; } } // Use the defaults. - this._documentConfiguration = { + this.documentConfiguration = { maxDocumentLines: 1500, maxNumberOfProblems: 100, doWarnOptionExplicitMissing: true, + environment: { + os: "Win64", + version: "Vba7" + } }; - return this._documentConfiguration; + return this.documentConfiguration; } - clearDocumentConfiguration = () => this._documentConfiguration = undefined; - - // get activeAttributeElement() { - // return this._attributeElements?.at(-1); - // } + clearDocumentConfiguration = () => this.documentConfiguration = undefined; constructor(workspace: Workspace, name: string, document: TextDocument) { this.textDocument = document; @@ -113,25 +138,23 @@ export abstract class BaseProjectDocument { } languageServerSemanticTokens = (range?: Range) => { - return this._semanticTokens.getSemanticTokens(range); + return this.semanticTokens.getSemanticTokens(range); }; languageServerFoldingRanges(): FoldingRange[] { - return this._foldableElements; + return this.foldableElements; } languageServerSymbolInformation(): SymbolInformation[] { - return this._symbolInformations; + return this.symbolInformations; } - languageServerDiagnostics(): PublishDiagnosticsParams { - this._hasDiagnosticElements.forEach(e => - e.evaluateDiagnostics() - ); + languageServerDiagnostics(): DocumentDiagnosticReport { return { - uri: this.textDocument.uri, - diagnostics: this._hasDiagnosticElements - .map((e) => e.diagnostics).flat(1) }; + kind: DocumentDiagnosticReportKind.Full, + items: this.hasDiagnosticElements + .map((e) => e.diagnosticCapability.diagnostics).flat(1) + }; } async parseAsync(token: CancellationToken): Promise { @@ -154,82 +177,57 @@ export abstract class BaseProjectDocument { } // Parse the document. - await (new SyntaxParser()).parseAsync(this, token) + await (new SyntaxParser()).parseAsync(this) // Evaluate the diagnostics. - this._hasDiagnosticElements.forEach(element => { - element.evaluateDiagnostics; - this._diagnostics.concat(element.diagnostics); - }); + this.diagnostics = this.hasDiagnosticElements + .map(e => e.diagnosticCapability.evaluate()) + .flat(); + this._isBusy = false; }; - registerNamedElementDeclaration(element: any) { - // Check workspace if public. - // Check for existing entry in local scope. - // Check for overriding name in method (if relevant) - // Check for overriding name in document scope. - // Check for overriding name in workspace. - throw new Error("Not implemented"); + /** + * Auto registers the element based on capabilities. + * @returns This for chaining. + */ + registerElement(element: BaseSyntaxElement) { + if (!!element.diagnosticCapability) this.registerDiagnosticElement(element as HasDiagnosticCapability); + if (!!element.foldingRangeCapability) this.registerFoldableElement(element as HasFoldingRangeCapability); + if (!!element.semanticTokenCapability) this.registerSemanticToken(element as HasSemanticTokenCapability); + if (!!element.symbolInformationCapability) this.registerSymbolInformation(element as HasSymbolInformationCapability); + return this; } - registerDiagnosticElement(element: HasDiagnosticCapability) { - this._hasDiagnosticElements.push(element); + registerPropertyElementDeclaration(element: PropertyGetDeclarationElement | PropertySetDeclarationElement | PropertyLetDeclarationElement) { + const elementName = element.propertyName; + this.properties.getOrSet(elementName) + .addPropertyDeclaration(element); return this; } - /** - * Pushes an element to the attribute elements stack. - * Be careful to pair a register action with an appropriate deregister. - * @param element the element to register. - * @returns nothing of interest. - */ - // registerAttributeElement = (element: HasAttribute) => { - // this._attributeElements.push(element); - // return this; - // }; - - /** - * Pops an element from the attribute elements stack. - * Popping allows actions to be performed on the same element, - * e.g., registered in the entry event and deregistered in the exit event. - * @param element the element to register. - * @returns the element at the end of the stack. - */ - // deregisterAttributeElement = () => { - // return this._attributeElements.pop(); - // }; + registerNamedElementDeclaration(element: DeclarableElement) { + this.workspace.namespaceManager.addNameItem(element); + return this; + } - registerFoldableElement = (element: FoldableElement) => { - this._foldableElements.push(new FoldingRange(element)); + registerNamespaceElement(element: DeclarableElement) { + this.workspace.namespaceManager.addNamespace(element); return this; - }; + } - registerNamedElement(element: IdentifiableSyntaxElement) { - this.currentScopeElement?.pushDeclaredName(element); + deregisterNamespaceElement() { + this.workspace.namespaceManager.popNamespace(); return this; } - /** - * Registers an element as a parent to be attached to subsequent elemements. - * Should be called when the parser context is entered and matched with - * deregisterScopedElement when the context exits. - * @param element the element to register. - * @returns this for chaining. - */ - registerScopedElement(element: ScopeElement) { - this._elementParents.push(element); + registerDiagnosticElement(element: HasDiagnosticCapability) { + this.hasDiagnosticElements.push(element); return this; } - /** - * Deregisters an element as a parent so it isn't attached to subsequent elemements. - * Should be called when the parser context is exited and matched with - * deregisterScopedElement when the context is entered. - * @returns this for chaining. - */ - deregisterScopedElement = () => { - this._elementParents.pop(); + registerFoldableElement = (element: HasFoldingRangeCapability) => { + this.foldableElements.push(element.foldingRangeCapability.foldingRange); return this; }; @@ -238,20 +236,63 @@ export abstract class BaseProjectDocument { * @param element element The element that has a semantic token. * @returns this for chaining. */ - registerSemanticToken = (element: HasSemanticToken) => { - this._semanticTokens.add(element); + registerSemanticToken = (element: HasSemanticTokenCapability) => { + this.semanticTokens.add(element); return this; - }; + } + + registerSubtractElement = (element: BaseContextSyntaxElement) => { + this.redactedElements.push(element); + return this; + } /** * Registers a SymbolInformation. * @param element The element that has symbol information. * @returns this for chaining. */ - registerSymbolInformation = (element: HasSymbolInformation) => { - this._symbolInformations.push(element.symbolInformation); + registerSymbolInformation = (element: HasSymbolInformationCapability) => { + this.symbolInformations.push(element.symbolInformationCapability.SymbolInformation); return this; - }; + } + + + private subtractTextFromRanges(ranges: Range[]): string { + const text = this.textDocument.getText(); + return ranges.reduce((x, y) => this.subtractTextRange(x, y), text); + } + + /** + * Subtracts text by replacing it with white space. + * @param text the text to act on. + * @param range the range to subtract. + * @returns the original text with the range replaced by spaces. + */ + private subtractTextRange(text: string, range: Range): string { + const docLines = text.split('\r\n'); + + if (range.start.line === range.end.line) { + // When the start and end lines are the same, subtract a substring. + const x = range.end.character; + const y = range.start.character; + const i = range.start.line - 1; + const line = docLines[i]; + const subtraction = ' '.repeat(x - y + 1); + docLines[i] = line.slice(0, y) + subtraction + line.slice(x + 1); + return docLines.join('\r\n'); + } else { + // When they aren't, subtract whole lines between start and end. + const x = range.end.line; + const y = range.start.line; + + // Replace the subtracted lines with spaces to maintain individual + // character positional integrity. + const result = docLines.map((line, i) => + i >= y && i < x ? ' '.repeat(line.length) : line + ); + return result.join('\r\n') + } + } } diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index d521eb5..ad6e8ee 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -1,112 +1,154 @@ +// Core +import { Position, Range } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-languageserver-textdocument'; + +// Antlr import { ParserRuleContext } from 'antlr4ng'; -import { Diagnostic, Range, SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { Position, TextDocument } from 'vscode-languageserver-textdocument'; -import { FoldingRangeKind } from '../../capabilities/folding'; -import { IdentifierElement, PropertyDeclarationElement } from './memory'; -import '../../extensions/parserExtensions'; - -export interface ContextOptionalSyntaxElement { - range?: Range; - parent?: ContextOptionalSyntaxElement; - context?: ParserRuleContext; - get text(): string; - get uri(): string; +// Project +import { + DiagnosticCapability, + FoldingRangeCapability, + IdentifierCapability, + SemanticTokenCapability, + SymbolInformationCapability +} from '../../capabilities/capabilities'; + + +export abstract class BaseSyntaxElement { + // Base Properties + context?: Context + identifierCapability?: IdentifierCapability; + + // Capabilities + diagnosticCapability?: DiagnosticCapability; + foldingRangeCapability?: FoldingRangeCapability; + semanticTokenCapability?: SemanticTokenCapability; + symbolInformationCapability?: SymbolInformationCapability; + + get isPublic(): boolean { return false; } + + constructor(); + constructor(ctx: T, doc: TextDocument) + constructor(ctx?: T, doc?: TextDocument) { + if (!!ctx && !!doc) this.context = new Context(ctx, doc); + } + + /** + * Checks if this element is a child of another element. + * @returns True if the element is a child of the passed element. + */ isChildOf(range: Range): boolean; -} + isChildOf(element: BaseSyntaxElement): boolean; + isChildOf(elementOrRange: BaseSyntaxElement | Range): boolean { + const a = this.context?.range; + const b = ((o: any): o is BaseSyntaxElement => 'context' in o)(elementOrRange) + ? elementOrRange.context?.range + : elementOrRange; -interface SyntaxElement extends ContextOptionalSyntaxElement { - range: Range; - context: ParserRuleContext; -} + if (!a || !b) return false; -export interface HasDiagnosticCapability { - diagnostics: Diagnostic[]; - evaluateDiagnostics(): void; -} + const isPositionBefore = (x: Position, y: Position) => x.line < y.line + || (x.line === y.line && x.character <= y.character); + + return isPositionBefore(b.start, a.start) + && isPositionBefore(a.end, b.end); + } -export interface NamedSyntaxElement extends SyntaxElement { - get name(): string; + /** + * Compare two syntax elements for equality. + * @returns True if the document, range, and text match. + */ + equals(element: BaseSyntaxElement): boolean { + const a = this.context; + const b = element.context; + + return !!a && !!b + && a.document.uri === b.document.uri + && a.range === b.range + && a.text === b.text; + } } -export interface IdentifiableSyntaxElement extends NamedSyntaxElement { - identifier: IdentifierElement; - isPropertyElement(): this is PropertyDeclarationElement + +export abstract class BaseContextSyntaxElement extends BaseSyntaxElement implements HasContext { + context!: Context; + + constructor(ctx: T, doc: TextDocument) { + super(ctx, doc); + } } -export interface HasSymbolInformation extends NamedSyntaxElement { - get symbolInformation(): SymbolInformation; + +export abstract class BaseIdentifyableSyntaxElement extends BaseContextSyntaxElement implements IsIdentifiable { + abstract identifierCapability: IdentifierCapability; + + constructor(ctx: T, doc: TextDocument) { + super(ctx, doc); + } } -export interface HasSemanticToken extends NamedSyntaxElement, IdentifiableSyntaxElement { - tokenType: SemanticTokenTypes; - tokenModifiers: SemanticTokenModifiers[]; + +// --------------------------------------------------------- +// Utilities +// --------------------------------------------------------- + +export class Context { + rule: T; + document: TextDocument; + range: Range; + + get text(): string { + return this.rule.getText(); + } + + get startIndex() { return this.rule.start?.start ?? 0; } + get stopIndex() { return this.rule.stop?.stop ?? 0; } + + constructor(context: T, document: TextDocument) { + this.rule = context; + this.document = document; + this.range = context.toRange(document); + } } -export interface MemoryElement extends BaseSyntaxElement { - name: string; - returnType: any; - symbol: SymbolKind; + +// --------------------------------------------------------- +// Interfaces +// --------------------------------------------------------- + +export interface HasContext { + context: Context; } -export interface FoldingRangeElement { - range: Range; - foldingRangeKind?: FoldingRangeKind; + +export interface HasDiagnosticCapability extends HasContext { + diagnosticCapability: DiagnosticCapability; } -export interface ScopeElement { - declaredNames: Map; - pushDeclaredName(element: IdentifiableSyntaxElement): void + +export interface IsIdentifiable { + identifierCapability: IdentifierCapability } -export abstract class BaseSyntaxElement implements ContextOptionalSyntaxElement { - protected document: TextDocument; - - range?: Range; - parent?: ContextOptionalSyntaxElement; - context?: ParserRuleContext; - get text(): string { return this.context?.getText() ?? ''; } - get uri(): string { return this.document.uri; } +export interface HasSemanticTokenCapability { + semanticTokenCapability: SemanticTokenCapability +} - constructor(context: ParserRuleContext | undefined, document: TextDocument) { - this.context = context; - this.document = document; - this.range = this._contextToRange(); - } - isChildOf = (range: Range): boolean => { - if (!this.range) { - return false; - } - - const isPositionBefore = (x: Position, y: Position) => - x.line < y.line || (x.line === y.line && x.character <= y.character); - - return isPositionBefore(range.start, this.range.start) - && isPositionBefore(this.range.end, range.end); - }; - - private _contextToRange(): Range | undefined { - if (!this.context) { - return; - } - - const startIndex = this.context?.start?.start ?? 0; - const stopIndex = this.context.stop?.stop ?? startIndex; - return Range.create( - this.document.positionAt(startIndex), - this.document.positionAt(stopIndex + 1) - ); - } +export interface HasSymbolInformationCapability extends IsIdentifiable { + symbolInformationCapability: SymbolInformationCapability } -export abstract class BaseContextSyntaxElement extends BaseSyntaxElement { - range!: Range; - context!: ParserRuleContext; - constructor(ctx: ParserRuleContext, doc: TextDocument) { - super(ctx, doc); - } +export interface HasFoldingRangeCapability { + foldingRangeCapability: FoldingRangeCapability; } + +// --------------------------------------------------------- +// Compound Types +// --------------------------------------------------------- + +export type DeclarableElement = BaseContextSyntaxElement & IsIdentifiable & HasDiagnosticCapability; diff --git a/server/src/project/elements/flow.ts b/server/src/project/elements/flow.ts index 0afd9a5..25af6bf 100644 --- a/server/src/project/elements/flow.ts +++ b/server/src/project/elements/flow.ts @@ -1,20 +1,36 @@ -import { Diagnostic } from 'vscode-languageserver'; +// Core import { TextDocument } from 'vscode-languageserver-textdocument'; -import { WhileStatementContext } from '../../antlr/out/vbaParser'; +// Antlr +import { AnyOperatorContext, WhileStatementContext } from '../../antlr/out/vbaParser'; + +// Project +import { DiagnosticCapability } from '../../capabilities/capabilities'; import { BaseContextSyntaxElement, HasDiagnosticCapability } from './base'; -import { WhileWendDeprecatedDiagnostic } from '../../capabilities/diagnostics'; +import { MultipleOperatorsDiagnostic, WhileWendDeprecatedDiagnostic } from '../../capabilities/diagnostics'; -export class WhileLoopElement extends BaseContextSyntaxElement implements HasDiagnosticCapability { - diagnostics: Diagnostic[] = []; +export class WhileLoopElement extends BaseContextSyntaxElement implements HasDiagnosticCapability { + diagnosticCapability: DiagnosticCapability; constructor(context: WhileStatementContext, document: TextDocument) { super(context, document); - + this.diagnosticCapability = new DiagnosticCapability(this, () => { + this.diagnosticCapability.diagnostics.push(new WhileWendDeprecatedDiagnostic(this.context.range)); + return this.diagnosticCapability.diagnostics; + }); } +} + - evaluateDiagnostics(): void { - this.diagnostics.push(new WhileWendDeprecatedDiagnostic(this.range)) +export class DuplicateOperatorElement extends BaseContextSyntaxElement implements HasDiagnosticCapability { + diagnosticCapability: DiagnosticCapability; + + constructor(context: AnyOperatorContext, document: TextDocument) { + super(context, document); + this.diagnosticCapability = new DiagnosticCapability(this, () => { + this.diagnosticCapability.diagnostics.push(new MultipleOperatorsDiagnostic(this.context.range)); + return this.diagnosticCapability.diagnostics; + }); } -} +} \ No newline at end of file diff --git a/server/src/project/elements/memory.ts b/server/src/project/elements/memory.ts deleted file mode 100644 index a69c160..0000000 --- a/server/src/project/elements/memory.ts +++ /dev/null @@ -1,420 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { Diagnostic, SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { AmbiguousIdentifierContext, ConstItemContext, EnumDeclarationContext, EnumMemberContext, FunctionDeclarationContext, ProcedureDeclarationContext, PropertyGetDeclarationContext, PropertySetDeclarationContext, ReservedMemberNameContext, SubroutineDeclarationContext, UdtDeclarationContext, UdtElementContext, UntypedNameContext, VariableDclContext } from '../../antlr/out/vbaParser'; - -import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSemanticToken, HasSymbolInformation, NamedSyntaxElement } from './base'; - -import { ScopeElement } from './special'; -import { VbaClassDocument, VbaModuleDocument } from '../document'; -import { SymbolInformationFactory } from '../../capabilities/symbolInformation'; -import '../../extensions/parserExtensions'; -import { DuplicateDeclarationDiagnostic, ElementOutOfPlaceDiagnostic } from '../../capabilities/diagnostics'; - - - -export class IdentifierElement extends BaseContextSyntaxElement { - constructor(ctx: UntypedNameContext | ConstItemContext | AmbiguousIdentifierContext | ReservedMemberNameContext, doc: TextDocument) { - super(ctx, doc); - } -} - -export abstract class DeclarationElement extends ScopeElement implements HasDiagnosticCapability { - abstract diagnostics: Diagnostic[]; - abstract identifier: IdentifierElement; - - constructor(context: ProcedureDeclarationContext, document: TextDocument) { - super(context, document); - } - - evaluateDiagnostics(): void { - return; - } - - get name(): string { - throw new Error('Method not implemented.'); - } - - static create(context: ProcedureDeclarationContext, document: VbaClassDocument | VbaModuleDocument) { - let methodContext: SubroutineDeclarationContext | FunctionDeclarationContext | PropertyGetDeclarationContext | null; - - // Create a sub if we have one. - methodContext = context.subroutineDeclaration(); - if (methodContext) { - return new SubDeclarationElement(context, document.textDocument, methodContext); - } - - // Create a function if we have one. - methodContext = context.functionDeclaration(); - if (methodContext) { - return new FunctionDeclarationElement(context, document.textDocument, methodContext); - } - - // Check if we already have a property with this name. - const propertyDeclaration = new PropertyDeclarationElement(context, document.textDocument); - const identifierText = propertyDeclaration.identifier.text; - const predeclaredElements = document.currentScopeElement?.declaredNames.get(identifierText) ?? []; - - // Add to an existing property rather than creating. - for (const element of predeclaredElements) { - if (element.isPropertyElement() && element.identifier.text === identifierText) { - element.addPropertyDeclaration(context, document.textDocument); - return element; - } - } - - // Return a new property. - return propertyDeclaration; - } -} - -export class SubDeclarationElement extends DeclarationElement implements HasSymbolInformation { - identifier: IdentifierElement; - symbolInformation: SymbolInformation; - diagnostics: Diagnostic[] = []; - - constructor(context: ProcedureDeclarationContext, document: TextDocument, methodContext: SubroutineDeclarationContext) { - super(context, document); - - const identifierContext = methodContext.subroutineName()?.ambiguousIdentifier(); - this.identifier = new IdentifierElement(identifierContext!, document); - this.symbolInformation = SymbolInformation.create( - this.identifier.text, - SymbolKind.Method, - this.range, - this.document.uri - ); - } -} - -export class FunctionDeclarationElement extends DeclarationElement implements HasSymbolInformation { - identifier: IdentifierElement; - symbolInformation: SymbolInformation; - diagnostics: Diagnostic[] = []; - - constructor(context: ProcedureDeclarationContext, document: TextDocument, methodContext: FunctionDeclarationContext) { - super(context, document); - const identifierContext = methodContext.functionName()!.ambiguousIdentifier()!; - this.identifier = new IdentifierElement(identifierContext, document); - this.symbolInformation = SymbolInformation.create( - this.identifier.text, - SymbolKind.Method, - this.range, - this.document.uri - ); - } -} - -export class PropertyDeclarationElement extends DeclarationElement implements HasSymbolInformation { - identifier: IdentifierElement; - diagnostics: Diagnostic[] = []; - symbolInformation: SymbolInformation; - getDeclarations: PropertyGetDeclarationElement[] = []; - letDeclarations: PropertyLetDeclarationElement[] = []; - setDeclarations: PropertyLetDeclarationElement[] = []; - - get countDeclarations(): number { - return this.getDeclarations.length - + this.letDeclarations.length - + this.setDeclarations.length; - } - - constructor(context: ProcedureDeclarationContext, document: TextDocument) { - super(context, document); - const identifier = this.addPropertyDeclaration(context, document); - this.identifier = identifier.value - this.symbolInformation = SymbolInformation.create( - `${identifier.type} ${this.identifier.text}`, - SymbolKind.Property, - this.range, - this.document.uri - ); - } - - evaluateDiagnostics(): void { - this._evaluateDuplicateDeclarationsDiagnostics(); - } - - addPropertyDeclaration(context: ProcedureDeclarationContext, document: TextDocument) { - let property: PropertyGetDeclarationElement | PropertyLetDeclarationElement | PropertySetDeclarationElement; - let propertyType: string; - switch (true) { - case !!context.propertyGetDeclaration(): - propertyType = 'Get'; - property = new PropertyGetDeclarationElement(context, document, context.propertyGetDeclaration()!); - this.getDeclarations.push(new PropertyGetDeclarationElement(context, document, context.propertyGetDeclaration()!)); - break; - case !!context.propertySetDeclaration()?.LET(): - propertyType = 'Let'; - property = new PropertyLetDeclarationElement(context, document, context.propertySetDeclaration()!); - this.letDeclarations.push(property); - break; - default: - propertyType = 'Set'; - property = new PropertyLetDeclarationElement(context, document, context.propertySetDeclaration()!); - this.setDeclarations.push(new PropertySetDeclarationElement(context, document, context.propertySetDeclaration()!)); - break; - } - return { type: propertyType, value: property.identifier }; - } - - private _evaluateDuplicateDeclarationsDiagnostics(): void { - [this.getDeclarations, this.letDeclarations, this.setDeclarations].forEach(declarations => { - declarations.forEach((declaration, i) => { - if (i > 0) this.diagnostics.push(new DuplicateDeclarationDiagnostic(declaration.identifier.range)); - }); - }); - } -} - -class PropertyGetDeclarationElement extends DeclarationElement { - identifier: IdentifierElement; - diagnostics: Diagnostic[] = []; - - constructor(context: ProcedureDeclarationContext, document: TextDocument, getContext: PropertyGetDeclarationContext) { - super(context, document); - this.identifier = new IdentifierElement(getContext.functionName()!.ambiguousIdentifier()!, document); - } -} - -class PropertyLetDeclarationElement extends DeclarationElement { - identifier: IdentifierElement; - diagnostics: Diagnostic[] = []; - - constructor(context: ProcedureDeclarationContext, document: TextDocument, setContext: PropertySetDeclarationContext) { - super(context, document); - this.identifier = new IdentifierElement(setContext.subroutineName()!.ambiguousIdentifier()!, document); - } -} - -class PropertySetDeclarationElement extends DeclarationElement { - identifier: IdentifierElement; - diagnostics: Diagnostic[] = []; - - constructor(context: ProcedureDeclarationContext, document: TextDocument, setContext: PropertySetDeclarationContext) { - super(context, document); - this.identifier = new IdentifierElement(setContext.subroutineName()!.ambiguousIdentifier()!, document); - } -} - -abstract class BaseEnumDeclarationElement extends ScopeElement implements HasSemanticToken, HasSymbolInformation { - identifier: IdentifierElement; - tokenModifiers: SemanticTokenModifiers[] = []; - declaredNames: Map = new Map(); - - abstract tokenType: SemanticTokenTypes; - abstract symbolInformation: SymbolInformation; - - get name(): string { - return this.identifier.text; - } - - constructor(context: EnumDeclarationContext | EnumMemberContext, document: TextDocument) { - super(context, document); - this.identifier = new IdentifierElement(context.untypedName().ambiguousIdentifier()!, document); - } - -} - -export class EnumDeclarationElement extends BaseEnumDeclarationElement implements ScopeElement, HasDiagnosticCapability { - diagnostics: Diagnostic[] = []; - tokenType: SemanticTokenTypes; - isDeclaredAfterMethod: boolean; - - get symbolInformation(): SymbolInformation { - return SymbolInformationFactory.create( - this, SymbolKind.Enum - ); - } - - constructor(context: EnumDeclarationContext, document: TextDocument, isDeclaredAfterMethod: boolean) { - super(context, document); - this.tokenType = SemanticTokenTypes.enum; - this.isDeclaredAfterMethod = isDeclaredAfterMethod; - this.identifier = new IdentifierElement(context.untypedName().ambiguousIdentifier()!, document); - context.enumMemberList().enumElement().forEach(enumElementContext => - this.pushDeclaredName(new EnumMemberDeclarationElement(enumElementContext.enumMember()!, document)) - ); - } - - evaluateDiagnostics(): void { - if (this.isDeclaredAfterMethod) { - this.diagnostics.push(new ElementOutOfPlaceDiagnostic(this.range, 'Enum declaration')); - } - } - -} - -class EnumMemberDeclarationElement extends BaseEnumDeclarationElement { - tokenType: SemanticTokenTypes; - - get symbolInformation(): SymbolInformation { - return SymbolInformationFactory.create( - this, SymbolKind.EnumMember - ); - } - - constructor(context: EnumMemberContext, document: TextDocument) { - super(context, document); - this.tokenType = SemanticTokenTypes.enumMember; - this.identifier = new IdentifierElement(context.untypedName().ambiguousIdentifier()!, document); - } -} - -abstract class BaseVariableDeclarationStatementElement extends BaseContextSyntaxElement implements HasSemanticToken, HasSymbolInformation, NamedSyntaxElement { - tokenType: SemanticTokenTypes; - tokenModifiers: SemanticTokenModifiers[] = []; - readonly symbolKind: SymbolKind; - - abstract identifier: IdentifierElement; - - get name(): string { - return this.identifier.text; - } - - get symbolInformation(): SymbolInformation { - return SymbolInformation.create( - this.identifier.text, - this.symbolKind, - this.range, - this.document.uri - ); - } - - isPropertyElement(): this is PropertyDeclarationElement { - return false; - } - - constructor(context: VariableDclContext | ConstItemContext | UdtElementContext, document: TextDocument, tokenType: SemanticTokenTypes, symbolKind: SymbolKind) { - super(context, document); - this.tokenType = tokenType; - this.symbolKind = symbolKind; - } -} - -export class ConstDeclarationElement extends BaseVariableDeclarationStatementElement { - tokenModifiers: SemanticTokenModifiers[] = []; - identifier: IdentifierElement; - - get name(): string { - return this.identifier.text; - } - - constructor(context: ConstItemContext, document: TextDocument) { - super(context, document, SemanticTokenTypes.variable, SymbolKind.Constant); - this.identifier = new IdentifierElement(context, document); - } -} - -export class TypeDeclarationElement extends ScopeElement implements HasSemanticToken, HasSymbolInformation, NamedSyntaxElement { - tokenType: SemanticTokenTypes; - tokenModifiers: SemanticTokenModifiers[] = []; - identifier: IdentifierElement; - symbolKind: SymbolKind; - declaredNames: Map = new Map(); - - constructor(context: UdtDeclarationContext, document: TextDocument) { - super(context, document); - this.symbolKind = SymbolKind.Struct; - this.tokenType = SemanticTokenTypes.struct; - this.identifier = new IdentifierElement(context.untypedName(), document); - context.udtMemberList().udtElement().forEach(member => - this.pushDeclaredName(new TypeMemberDeclarationElement(member, document)) - ); - } - - get name(): string { return this.identifier.text; } - get symbolInformation(): SymbolInformation { - return SymbolInformationFactory.create( - this as NamedSyntaxElement, this.symbolKind - ); - } -} - -export class TypeMemberDeclarationElement extends BaseVariableDeclarationStatementElement { - tokenModifiers: SemanticTokenModifiers[] = []; - identifier: IdentifierElement; - - get name(): string { - return this.identifier.text; - } - - constructor(context: UdtElementContext, document: TextDocument) { - super(context, document, SemanticTokenTypes.property, SymbolKind.Property); - const identifierContext = context.udtMember()?.untypedNameMemberDcl()?.ambiguousIdentifier() ?? context.udtMember()?.reservedNameMemberDcl()?.reservedMemberName(); - this.identifier = new IdentifierElement(identifierContext!, document); - } -} - -// export class VariableDeclarationsElement extends BaseVariableDeclarationStatementElement { -// declarations: VariableDeclarationElement[] = []; - -// constructor(context: VariableStmtContext, document: TextDocument) { -// super(context, document); -// context.variableListStmt().variableSubStmt().forEach((element) => -// this.declarations.push(new VariableDeclarationElement( -// element, document -// )) -// ); -// } -// } - -// class VariableDeclarationElement extends BaseContextSyntaxElement implements HasSymbolInformation { -// identifier: IdentifierElement; -// asType: VariableType; -// arrayBounds?: ArrayBounds; - -// constructor(context: ConstSubStmtContext | VariableSubStmtContext, document: TextDocument) { -// super(context, document); -// this.asType = new VariableType(context.asTypeClause(), document); -// this.arrayBounds = ArrayBounds.create(context); -// this.identifier = new IdentifierElement(context.ambiguousIdentifier(), document); -// } - -// get name(): string { return this.identifier.text; } -// get symbolInformation(): SymbolInformation { -// return SymbolInformationFactory.create( -// this, this.asType.symbolKind -// ); -// } -// } - -// class VariableType extends BaseSyntaxElement { -// typeName: string; -// symbolKind: SymbolKind; - -// constructor(context: AsTypeClauseContext | undefined, document: TextDocument, isArray?: boolean) { -// super(context, document); -// this.symbolKind = isArray ? SymbolKind.Array : SymbolKind.Variable; - -// // Needs more ternery. -// const type = context?.type_()?.baseType() ?? context?.type_()?.complexType(); -// this.typeName = type?.text ?? type?.text ?? 'Variant'; -// this.symbolKind = type ? type.toSymbolKind() : SymbolKind.Variable; -// } -// } - -// class ArrayBounds { -// dimensions: { lower: number, upper: number }[] = []; - -// constructor(subStmt: VariableSubStmtContext) { -// subStmt.subscripts()?.subscript_().forEach((x) => { -// const vals = x.valueStmt(); -// this.dimensions.push({ -// lower: vals.length === 1 ? 0 : +vals[0].text, -// upper: vals.length === 1 ? +vals[0].text : +vals[1].text -// }); -// }); -// } - -// /** -// * Creates an ArrayBounds if the context is a variable and an array. -// * @param subStmt a subStmt context for a variable or a constant. -// * @returns A new array bounds if the context is an array variable. -// */ -// static create(subStmt: VariableSubStmtContext | ConstSubStmtContext) { -// const hasLparenMethod = (x: any): x is VariableSubStmtContext => 'LPAREN' in x; -// if (hasLparenMethod(subStmt) && subStmt.LPAREN()) { -// return new ArrayBounds(subStmt); -// } -// } -// } \ No newline at end of file diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index a608eed..ca40024 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -1,228 +1,201 @@ +// Core import { TextDocument } from 'vscode-languageserver-textdocument'; -import { Diagnostic, Range, SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { ClassModuleContext, IgnoredAttrContext, ProceduralModuleContext } from '../../antlr/out/vbaParser'; -import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSymbolInformation } from './base'; -import { SymbolInformationFactory } from '../../capabilities/symbolInformation'; -import { IgnoredAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics'; -import '../../extensions/stringExtensions'; -import { ScopeElement } from './special'; +import { Diagnostic, Range, SymbolKind } from 'vscode-languageserver'; + +// Antlr +import { ParserRuleContext } from 'antlr4ng'; +import { + ClassModuleCodeElementContext, + ClassModuleContext, + ClassModuleHeaderContext, + IgnoredClassAttrContext, + IgnoredProceduralAttrContext, + ProceduralModuleCodeElementContext, + ProceduralModuleContext, + ProceduralModuleHeaderContext +} from '../../antlr/out/vbaParser'; + +// Project +import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, HasDiagnosticCapability } from './base'; +import { DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; +import { DuplicateAttributeDiagnostic, IgnoredAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics'; + interface DocumentSettings { doWarnOptionExplicitMissing: boolean; } -abstract class BaseModuleElement extends ScopeElement implements HasSymbolInformation, HasDiagnosticCapability { - protected abstract _name: string; - symbolKind: SymbolKind; - diagnostics: Diagnostic[] = []; - context: ProceduralModuleContext | ClassModuleContext; + +abstract class BaseModuleElement extends BaseIdentifyableSyntaxElement { + abstract attrubutes: ParserRuleContext[]; + abstract diagnosticCapability: DiagnosticCapability; + abstract hasOptionExplicit: boolean; + settings: DocumentSettings; + foldingRangeCapability: FoldingRangeCapability; + symbolInformationCapability: SymbolInformationCapability; - constructor(context: ProceduralModuleContext | ClassModuleContext, document: TextDocument, symbolKind: SymbolKind, documentSettings: DocumentSettings) { - super(context, document); - this.context = context; - this.symbolKind = symbolKind; + constructor(ctx: T, doc: TextDocument, documentSettings: DocumentSettings, symbolKind: SymbolKind) { + super(ctx, doc); this.settings = documentSettings; + this.foldingRangeCapability = new FoldingRangeCapability(this); + this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); } - get name(): string { - return this._name; + // Helpers + protected addMissingAttributesDiagnostics(diagnostics: Diagnostic[]): void { + if (!!this.identifierCapability.nameContext) return; + diagnostics.push(new MissingAttributeDiagnostic( + Range.create(this.context.range.start, this.context.range.start), + 'VB_NAME' + )); } - get symbolInformation(): SymbolInformation { - return SymbolInformationFactory.create( - this, this.symbolKind + protected addDuplicateAttributeDiagnostics(diagnostics: Diagnostic[]): void { + this.duplicateAttributes(this.attrubutes).forEach(attr => + diagnostics.push(new DuplicateAttributeDiagnostic( + attr.toRange(this.context.document), + getAttributeName(attr) + )) ); } - abstract evaluateDiagnostics(): void; - - protected get _hasOptionExplicit(): boolean { - const getCodeElements = () => { - if (this._isClassModule(this.context)) { - return this.context.classModuleBody().classModuleCode().classModuleCodeElement() - } - return this.context.proceduralModuleBody().proceduralModuleCode().proceduralModuleCodeElement(); - } - const codeElements = getCodeElements() - if (!codeElements) { - return false; - } - - for (const declaration of codeElements) { - const element = declaration.commonModuleCodeElement(); - if (element && element.commonOptionDirective()?.optionExplicitDirective()) { - return true; - } + protected addOptionExplicitMissingDiagnostic(diagnostics: Diagnostic[], header: ClassModuleHeaderContext | ProceduralModuleHeaderContext): void { + if (this.settings.doWarnOptionExplicitMissing && !this.hasOptionExplicit) { + const startLine = header.stop?.line ?? 0 + 1; + diagnostics.push(new MissingOptionExplicitDiagnostic( + Range.create(startLine, 1, startLine, 1) + )); } - - return false; } - private _isClassModule(context: ProceduralModuleContext | ClassModuleContext): context is ClassModuleContext { - return 'classModuleHeader' in context; - } -} - -export class ModuleElement extends BaseModuleElement { - context: ProceduralModuleContext; - protected _name: string; - - constructor(context: ProceduralModuleContext, document: TextDocument, documentSettings: DocumentSettings) { - super(context, document, SymbolKind.File, documentSettings); - this.context = context; - this._name = this._getName(context); + protected resolveConfiguration(diagnostics: Diagnostic[], header: ClassModuleHeaderContext | ProceduralModuleHeaderContext): void { + this.addMissingAttributesDiagnostics(diagnostics); + this.addDuplicateAttributeDiagnostics(diagnostics); + this.addOptionExplicitMissingDiagnostic(diagnostics, header); } - evaluateDiagnostics(): void { - if (this.settings.doWarnOptionExplicitMissing && !this._hasOptionExplicit) { - const header = this.context.proceduralModuleHeader(); - const startLine = header.stop?.line ?? 0 + 1; - this.diagnostics.push(new MissingOptionExplicitDiagnostic( - Range.create( - startLine, 1, - startLine, 1 - ) - )); + protected evaluateHasOptionExplicit(codeElements: (ProceduralModuleCodeElementContext | ClassModuleCodeElementContext)[]): boolean { + for (const codeElement of codeElements) { + const isOptionExplicitDirective = codeElement + .commonModuleCodeElement() + ?.commonOptionDirective() + ?.optionExplicitDirective(); + if (!!isOptionExplicitDirective) return true; } + return false; } - private _getName(context: ProceduralModuleContext) { - const nameAttribute = context.proceduralModuleHeader()?.nameAttr(); - const name = nameAttribute?.STRINGLITERAL().getText(); - - if (!name) { - this.diagnostics.push(new MissingAttributeDiagnostic( - Range.create(this.range.start, this.range.start), - 'VB_NAME' - )); - } - - return name?.stripQuotes() ?? 'Unknown Module'; + private duplicateAttributes(attrs: ParserRuleContext[]) { + const catalogue = new Map(); + const result: ParserRuleContext[] = []; + attrs.forEach(attr => { + const attrName = getAttributeName(attr); + if (catalogue.has(attrName)) { + result.push(attr) + } else { + catalogue.set(attrName, null); + } + }) + return result; } } -export class ClassElement extends BaseModuleElement { - context: ClassModuleContext; - protected _name: string; - - constructor(context: ClassModuleContext, document: TextDocument, documentSettings: DocumentSettings) { - super(context, document, SymbolKind.Class, documentSettings); - this.context = context; - this._name = this._getName(context); - } - evaluateDiagnostics(): void { - if (this.settings.doWarnOptionExplicitMissing && !this._hasOptionExplicit) { - const header = this.context.classModuleHeader(); - const startLine = header.stop?.line ?? 0 + 1; - this.diagnostics.push(new MissingOptionExplicitDiagnostic( - Range.create( - startLine, 1, - startLine, 1 - ) - )); - } +export class ModuleElement extends BaseModuleElement { + diagnosticCapability: DiagnosticCapability; + identifierCapability: IdentifierCapability; + + attrubutes: ParserRuleContext[]; + hasOptionExplicit: boolean; + + constructor(ctx: ProceduralModuleContext, doc: TextDocument, documentSettings: DocumentSettings) { + super(ctx, doc, documentSettings, SymbolKind.File); + this.attrubutes = ctx.proceduralModuleHeader().proceduralModuleAttr(); + this.diagnosticCapability = new DiagnosticCapability(this); + + this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx + .proceduralModuleBody() + .proceduralModuleCode() + .proceduralModuleCodeElement()); + + this.identifierCapability = new IdentifierCapability({ + element: this, + formatName: (x: string) => x.stripQuotes(), + defaultName: 'Unknown Module', + defaultRange: () => Range.create(this.context.range.start, this.context.range.start), + getNameContext: () => ctx + .proceduralModuleHeader() + .proceduralModuleAttr() + .map(x => x.nameAttr()) + .filter(x => !!x)[0] + ?.STRINGLITERAL() + }); + + this.resolveConfiguration( + this.diagnosticCapability.diagnostics, + this.context.rule.proceduralModuleHeader() + ); } +} - private _getName(context: ClassModuleContext) { - const nameAttributes = context.classModuleHeader().nameAttr(); - if (nameAttributes.length === 0) { - this.diagnostics.push(new MissingAttributeDiagnostic( - Range.create(this.range.start, this.range.start), - 'VB_NAME' - )); - return 'Unknown Class'; - } - - const nameAttribute = nameAttributes[0]; - return nameAttribute.STRINGLITERAL().getText().stripQuotes(); +export class ClassElement extends BaseModuleElement { + diagnosticCapability: DiagnosticCapability; + identifierCapability: IdentifierCapability; + + attrubutes: ParserRuleContext[]; + hasOptionExplicit: boolean; + + constructor(ctx: ClassModuleContext, doc: TextDocument, documentSettings: DocumentSettings) { + super(ctx, doc, documentSettings, SymbolKind.File); + this.attrubutes = [ + ctx.classModuleHeader().nameAttr(), + ctx.classModuleHeader().classAttr(), + ctx.classModuleHeader().ignoredClassAttr() + ].flat(); + this.diagnosticCapability = new DiagnosticCapability(this); + + this.hasOptionExplicit = this.evaluateHasOptionExplicit(ctx + .classModuleBody() + .classModuleCode() + .classModuleCodeElement()); + + this.identifierCapability = new IdentifierCapability({ + element: this, + formatName: (x: string) => x.stripQuotes(), + defaultName: 'Unknown Class', + defaultRange: () => Range.create(this.context.range.start, this.context.range.start), + getNameContext: () => ctx + .classModuleHeader() + .nameAttr()[0] + .STRINGLITERAL() + }); + + this.resolveConfiguration( + this.diagnosticCapability.diagnostics, + this.context.rule.classModuleHeader() + ); } } -export class IgnoredAttributeElement extends BaseContextSyntaxElement implements HasDiagnosticCapability { - diagnostics: Diagnostic[] = []; - constructor(context: IgnoredAttrContext, document: TextDocument) { - super(context, document); - } +export class ModuleIgnoredAttributeElement extends BaseContextSyntaxElement implements HasDiagnosticCapability { + diagnosticCapability: DiagnosticCapability; - evaluateDiagnostics(): void { - this.diagnostics.push( - new IgnoredAttributeDiagnostic(this.range) - ); + constructor(ctx: IgnoredClassAttrContext | IgnoredProceduralAttrContext, doc: TextDocument) { + super(ctx, doc); + this.diagnosticCapability = new DiagnosticCapability(this, () => { + this.diagnosticCapability.diagnostics.push(new IgnoredAttributeDiagnostic( + this.context.range, this.context.text.split(' ')[1] + )); + return this.diagnosticCapability.diagnostics; + }) } - } -// export class ModuleElement2 extends BaseContextSyntaxElement implements HasSymbolInformation, HasAttribute, HasDiagnosticCapability { -// private _hasName = false; -// private _name: string; -// symbolKind: SymbolKind; -// diagnostics: Diagnostic[] = []; - -// constructor(context: ModuleContext, document: TextDocument, symbolKind: SymbolKind) { -// super(context, document); -// this._name = "Unknown Module"; -// this.symbolKind = symbolKind; -// } - -// get name(): string { -// return this._name; -// } - -// get symbolInformation(): SymbolInformation { -// return SymbolInformationFactory.create( -// this, this.symbolKind -// ); -// } - -// evaluateDiagnostics(): void { -// const optionExplicitDiagnotic = this._getOptionExplicitDiagnostic(); -// if (optionExplicitDiagnotic) { -// this.diagnostics.push(optionExplicitDiagnotic); -// } -// } - -// private _getOptionExplicitDiagnostic(): Diagnostic | undefined { -// let optionExplicitFound = false; -// const context = this.context as ModuleContext; -// const declarations = context.moduleHeader().moduleDeclarations()?.moduleDeclarationsElement(); - -// if (declarations) { -// for (const declaration of declarations) { -// if ((declaration.moduleOption()?.text ?? '') === 'Option Explicit') { -// optionExplicitFound = true; -// break; -// } -// } -// } - -// return optionExplicitFound ? undefined : new MissingOptionExplicitDiagnostic( -// (new ModuelHeaderElement(context.moduleHeader(), this.document)).range -// ); -// } - -// processAttribute(context: AttributeStmtContext): void { -// if (this._hasName) { -// return; -// } - -// const text = context.text; -// if (text.startsWith("Attribute VB_Name = ")) { -// const unquote = (x: string): string => -// x.replace(/^"+|"+$/g, ''); - -// this._name = unquote(text.split("= ")[1]); -// this._hasName = true; -// } -// } -// } - -// class ModuelHeaderElement extends BaseContextSyntaxElement { -// constructor(context: ModuleHeaderContext, document: TextDocument) { -// super(context, document); -// } -// } \ No newline at end of file +// TODO: Move to helpers. +function getAttributeName(e: ParserRuleContext): string { + return e.getText().split(' ')[1] +} \ No newline at end of file diff --git a/server/src/project/elements/operator.ts b/server/src/project/elements/operator.ts deleted file mode 100644 index 31e542c..0000000 --- a/server/src/project/elements/operator.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { BaseContextSyntaxElement, HasDiagnosticCapability } from './base'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { Diagnostic } from 'vscode-languageserver'; -import { MultipleOperatorsDiagnostic } from '../../capabilities/diagnostics'; - - -// export class OperatorElement extends BaseContextSyntaxElement implements HasDiagnosticCapability { -// diagnostics: Diagnostic[] = []; - -// constructor(context: OperatorsStmtContext, document: TextDocument) { -// super(context, document); -// } - -// evaluateDiagnostics(): void { -// if (this.context.childCount > 1) { -// this.diagnostics.push(new MultipleOperatorsDiagnostic(this.range)); -// } -// } -// } diff --git a/server/src/project/elements/precompiled.ts b/server/src/project/elements/precompiled.ts new file mode 100644 index 0000000..62c53b5 --- /dev/null +++ b/server/src/project/elements/precompiled.ts @@ -0,0 +1,132 @@ +// Core +import * as ts from "typescript"; +import { Position, Range, SemanticTokenTypes } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-languageserver-textdocument'; + +// Antlr +import { ParserRuleContext } from 'antlr4ng'; +import { CompilerConditionalBlockContext, CompilerDefaultBlockContext, CompilerIfBlockContext } from '../../antlr/out/vbapreParser'; + +// Project +import { SemanticTokenCapability } from '../../capabilities/capabilities'; +import { BaseContextSyntaxElement, HasSemanticTokenCapability } from '../elements/base'; + + +export class CompilerLogicalBlock extends BaseContextSyntaxElement { + conditionalBlocks: CompilerConditionBlock[] = []; + inactiveBlocks: CompilerConditionBlock[] = []; + + constructor(ctx: CompilerIfBlockContext, doc: TextDocument, env: {environment: { os: string, version: string }}) { + super(ctx, doc); + + // Create the block elements + const blocks = [ctx.compilerConditionalBlock(), ctx.compilerDefaultBlock()].flat(); + blocks.map(x => { if(x) this.conditionalBlocks.push(new CompilerConditionBlock(x, doc, env)) }); + + // Create the comment elements. + let resolved = false; + for (const block of this.conditionalBlocks) { + if (!resolved && block.conditionResult) { + resolved = true; + continue; + } + this.inactiveBlocks.push(block); + } + } +} + + +class CompilerConditionBlock extends BaseContextSyntaxElement { + readonly documentSettings: {environment: { os: string, version: string }}; + + constructor(ctx: CompilerConditionalBlockContext | CompilerDefaultBlockContext, doc: TextDocument, env: {environment: { os: string, version: string }}) { + super(ctx, doc); + this.documentSettings = env; + } + + get blockLines(): string[] { + return this.context.rule.compilerBlockContent()?.getText().split('\n') ?? []; + } + + get linesToComments(): GenericCommentElement[] { + const rowX = this.context.range.start.line; + const rowY = this.context.range.end.line; + + // Iterate the rows -- test what happens when you get to the end of the document. + // May require a try catch where the default offset is the character count of the document. + const result: GenericCommentElement[] = []; + for (let i = rowX; i < rowY; i++) { + const posX = this.context.document.offsetAt({ line: i, character: 0 }); + const posY = this.context.document.offsetAt({ line: i + 1, character: 0 }) - 1; + + const lineRange = Range.create( + this.context.document.positionAt(posX), + this.context.document.positionAt(posY) + ); + result.push(new GenericCommentElement( + this.context.rule, + this.context.document, + lineRange) + ); + } + + return result; + } + + get conditionResult(): boolean { + // Default "Else" block is always true. + const ctx = this.context.rule; + if(((o: any): o is CompilerDefaultBlockContext => 'compilerElseStatement' in o)(ctx)) return true; + + const vbaExpression = ctx.compilerConditionalStatement().vbaExpression(); + const tsExpression = this.transpileVbaToTypescript(vbaExpression); + + // Evaluate the expression and return the result. + const result = eval(ts.transpile(tsExpression)); + if (!(typeof result === "boolean")) { + // TODO: Return false here instead of throwing + // and return an error diagnostic for the expression. + throw new Error("Expected boolean result."); + } + return result; + } + + /** Transpiles a VBA expression into Typescript. */ + private transpileVbaToTypescript(exp: string): string { + // Convert the environment constant to boolean. + const envToBooleanText = (opt: string) => { + const isOs = this.documentSettings.environment.os.toLowerCase() == opt; + const isVer = this.documentSettings.environment.version.toLowerCase() == opt; + return isOs || isVer ? 'true' : 'false'; + } + + // Set up text replacements map. + const constants = ['vba6', 'vba7', 'mac', 'win16', 'win32', 'win64'] + const replacements = new Map(constants.map(x => [x, envToBooleanText(x)])); + replacements.set('or', '||'); + replacements.set('and', '&&'); + replacements.set('not ', '!'); + + // Perform text replacements. + let result = exp; + replacements.forEach((v, k) => { + const regexp = RegExp(`${k}`, 'i') + if (regexp.test(result)) { + result = result.replace(regexp, v); + } + }); + + return result; + } +} + + +export class GenericCommentElement extends BaseContextSyntaxElement implements HasSemanticTokenCapability { + semanticTokenCapability: SemanticTokenCapability; + + constructor(ctx: ParserRuleContext, doc: TextDocument, range?: Range) { + super(ctx, doc); + const textLen = range ? doc.offsetAt(range.end) - doc.offsetAt(range.start) + 1 : undefined; + this.semanticTokenCapability = new SemanticTokenCapability(this, SemanticTokenTypes.comment, [], range, textLen) + } +} diff --git a/server/src/project/elements/procedure.ts b/server/src/project/elements/procedure.ts new file mode 100644 index 0000000..074c560 --- /dev/null +++ b/server/src/project/elements/procedure.ts @@ -0,0 +1,123 @@ +//Core +import { SymbolKind } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-languageserver-textdocument'; + +// Antlr +import { ParserRuleContext } from 'antlr4ng'; +import { + AmbiguousIdentifierContext, + FunctionDeclarationContext, + PropertyGetDeclarationContext, + PropertySetDeclarationContext, + SubroutineDeclarationContext +} from '../../antlr/out/vbaParser'; + +// Project +import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSymbolInformationCapability } from './base'; +import { DiagnosticCapability, IdentifierCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; + + +abstract class BaseProcedureElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability { + diagnosticCapability: DiagnosticCapability; + abstract identifierCapability: IdentifierCapability; + abstract symbolInformationCapability: SymbolInformationCapability; + + constructor(ctx: T, doc: TextDocument) { + super(ctx, doc); + this.diagnosticCapability = new DiagnosticCapability(this); + } +} + + +export class SubDeclarationElement extends BaseProcedureElement { + identifierCapability: IdentifierCapability; + symbolInformationCapability: SymbolInformationCapability; + + constructor(ctx: SubroutineDeclarationContext, doc: TextDocument) { + super(ctx, doc); + this.identifierCapability = new IdentifierCapability({ + element: this, + getNameContext: () => ctx.subroutineName()?.ambiguousIdentifier(), + }); + this.symbolInformationCapability = new SymbolInformationCapability(this, SymbolKind.Method); + } +} + + +export class FunctionDeclarationElement extends BaseProcedureElement { + identifierCapability: IdentifierCapability; + symbolInformationCapability: SymbolInformationCapability; + + constructor(ctx: FunctionDeclarationContext, doc: TextDocument) { + super(ctx, doc); + this.identifierCapability = new IdentifierCapability({ + element: this, + getNameContext: () => ctx.functionName()?.ambiguousIdentifier(), + }); + this.symbolInformationCapability = new SymbolInformationCapability(this, SymbolKind.Method); + } +} + + +export class PropertyDeclarationElement { + getters: PropertyGetDeclarationElement[] = []; + setters: PropertySetDeclarationElement[] = []; + letters: PropertyLetDeclarationElement[] = []; + + addPropertyDeclaration(property: PropertyGetDeclarationElement | PropertySetDeclarationElement | PropertyLetDeclarationElement) { + if (property instanceof PropertyGetDeclarationElement) { + this.getters.push(property); + } else if (property instanceof PropertySetDeclarationElement) { + this.setters.push(property); + } else { + this.letters.push(property); + } + } +} + + +/** + * A base class for property Get, Set, Let types to inherit from. + */ +abstract class BasePropertyDeclarationElement extends BaseProcedureElement { + identifierCapability: IdentifierCapability; + symbolInformationCapability: SymbolInformationCapability; + + private propertyType: string; + private nameContext?: AmbiguousIdentifierContext; + + get propertyName(): string { return `${this.identifierCapability.name.split(' ')[1]}`; } + + constructor(ctx: T, doc: TextDocument, propertyType: string, nameCtx?: AmbiguousIdentifierContext) { + super(ctx, doc); + this.nameContext = nameCtx; + this.propertyType = propertyType; + this.symbolInformationCapability = new SymbolInformationCapability(this, SymbolKind.Property); + this.identifierCapability = new IdentifierCapability({ + element: this, + getNameContext: () => this.nameContext, + formatName: (x: string) => `${this.propertyType} ${x}` + }); + } +} + + +export class PropertyGetDeclarationElement extends BasePropertyDeclarationElement { + constructor(ctx: PropertyGetDeclarationContext, doc: TextDocument) { + super(ctx, doc, 'Get', ctx.functionName()?.ambiguousIdentifier() ?? undefined); + } +} + + +export class PropertySetDeclarationElement extends BasePropertyDeclarationElement { + constructor(ctx: PropertySetDeclarationContext, doc: TextDocument) { + super(ctx, doc, 'Set', ctx.subroutineName()?.ambiguousIdentifier() ?? undefined); + } +} + + +export class PropertyLetDeclarationElement extends BasePropertyDeclarationElement { + constructor(ctx: PropertySetDeclarationContext, doc: TextDocument) { + super(ctx, doc, 'Let', ctx.subroutineName()?.ambiguousIdentifier() ?? undefined); + } +} \ No newline at end of file diff --git a/server/src/project/elements/special.ts b/server/src/project/elements/special.ts deleted file mode 100644 index 6f12f16..0000000 --- a/server/src/project/elements/special.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ParserRuleContext } from 'antlr4ng'; -import { FoldingRangeKind } from '../../capabilities/folding'; -import { BaseContextSyntaxElement, FoldingRangeElement, IdentifiableSyntaxElement } from './base'; -import { Range, TextDocument } from 'vscode-languageserver-textdocument'; -import { PropertyDeclarationElement } from './memory'; - - -export class FoldableElement extends BaseContextSyntaxElement implements FoldingRangeElement { - range!: Range; - foldingRangeKind?: FoldingRangeKind; - - constructor(ctx: ParserRuleContext, doc: TextDocument, foldingRangeKind?: FoldingRangeKind) { - super(ctx, doc); - this.foldingRangeKind = foldingRangeKind; - } -} - -export class ScopeElement extends FoldableElement implements ScopeElement { - declaredNames: Map = new Map(); - - constructor(ctx: ParserRuleContext, doc: TextDocument) { - super(ctx, doc); - } - - pushDeclaredName(element: IdentifiableSyntaxElement): void { - const name = element.identifier.text; - const names: IdentifiableSyntaxElement[] = this.declaredNames.get(name) ?? []; - names.push(element); - this.declaredNames.set(name, names); - } - - isPropertyElement(): this is PropertyDeclarationElement { - return 'getDeclarations' in this; - } -} diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts new file mode 100644 index 0000000..5b9d98d --- /dev/null +++ b/server/src/project/elements/typing.ts @@ -0,0 +1,152 @@ +// Core +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { SemanticTokenModifiers, SemanticTokenTypes, SymbolKind } from 'vscode-languageserver'; + +// Antlr +import { ParserRuleContext } from 'antlr4ng'; +import { ConstItemContext, + EnumDeclarationContext, + GlobalVariableDeclarationContext, + PrivateConstDeclarationContext, + PrivateTypeDeclarationContext, + PrivateVariableDeclarationContext, + PublicConstDeclarationContext, + PublicTypeDeclarationContext, + PublicVariableDeclarationContext, + TypeSuffixContext, + VariableDclContext, + WitheventsVariableDclContext +} from '../../antlr/out/vbaParser'; + +// Project +import { ElementOutOfPlaceDiagnostic, LegacyFunctionalityDiagnostic } from '../../capabilities/diagnostics'; +import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSemanticTokenCapability, HasSymbolInformationCapability } from './base'; +import { DiagnosticCapability, IdentifierCapability, SemanticTokenCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; + + +abstract class BaseTypeDeclarationElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability, HasSemanticTokenCapability { + diagnosticCapability: DiagnosticCapability; + abstract identifierCapability: IdentifierCapability; + symbolInformationCapability: SymbolInformationCapability; + semanticTokenCapability: SemanticTokenCapability; + + // ToDo: Implement callables. + + constructor(ctx: T, doc: TextDocument, symbolKind: SymbolKind, tokenType: SemanticTokenTypes, tokenModifiers?: SemanticTokenModifiers[]) { + super(ctx, doc); + this.diagnosticCapability = new DiagnosticCapability(this); + this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); + this.semanticTokenCapability = new SemanticTokenCapability(this, tokenType, tokenModifiers ?? []); + } +} + + +export class EnumDeclarationElement extends BaseTypeDeclarationElement { + identifierCapability: IdentifierCapability; + + constructor(ctx: EnumDeclarationContext, doc: TextDocument, isAfterProcedure: boolean) { + super(ctx, doc, SymbolKind.Enum, SemanticTokenTypes.enum); + this.identifierCapability = new IdentifierCapability({ + element: this, + getNameContext: () => ctx.untypedName().ambiguousIdentifier() + }); + if (isAfterProcedure) this.diagnosticCapability.diagnostics.push( + new ElementOutOfPlaceDiagnostic(this.context.range, "Enum declaration") + ); + } +} + + +export class TypeDeclarationElement extends BaseTypeDeclarationElement { + identifierCapability: IdentifierCapability; + + private _isPublic: boolean + get isPublic(): boolean { return this._isPublic; } + + constructor(ctx: PublicTypeDeclarationContext | PrivateTypeDeclarationContext, doc: TextDocument, isPublic: boolean) { + super(ctx, doc, SymbolKind.Struct, SemanticTokenTypes.struct); + this._isPublic = isPublic; + this.identifierCapability = new IdentifierCapability({ + element: this, + getNameContext: () => ctx.udtDeclaration().untypedName() + }); + this.symbolInformationCapability = new SymbolInformationCapability(this, SymbolKind.Struct); + } +} + + +type CombinedVariableContext = + PublicVariableDeclarationContext + | GlobalVariableDeclarationContext + | PrivateVariableDeclarationContext + | PublicConstDeclarationContext + | PrivateConstDeclarationContext + +export class DeclarationStatementElement extends BaseContextSyntaxElement { + private _isPublic: boolean; + private isConstant: boolean; + + get isPublic(): boolean { return this._isPublic; } + get declarations() { + return this.context.rule.declarationContexts().map(x => new VariableDeclarationElement( + x, this.context.document, this.isPublic, this.isConstant + )); + } + + constructor(ctx: T, doc: TextDocument, isConstant: boolean, isPublic: boolean) { + super(ctx, doc); + this._isPublic = isPublic; + this.isConstant = isConstant; + } + + static create(ctx: CombinedVariableContext, doc: TextDocument) { + const isPublicOrGlobal = (o: any): boolean => 'PUBLIC' in o || 'GLOBAL' in o; + const isConstantContext = (o: any): o is PublicConstDeclarationContext | PrivateConstDeclarationContext => 'moduleConstDeclaration' in o; + return new DeclarationStatementElement(ctx, doc, isConstantContext(ctx), isPublicOrGlobal(ctx)); + } +} + + +export class VariableDeclarationElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability, HasSemanticTokenCapability { + identifierCapability: IdentifierCapability; + diagnosticCapability: DiagnosticCapability; + symbolInformationCapability: SymbolInformationCapability; + semanticTokenCapability: SemanticTokenCapability; + + private _isPublic: boolean; + get isPublic(): boolean { return this._isPublic; } + + constructor(ctx: VariableDclContext | WitheventsVariableDclContext | ConstItemContext, doc: TextDocument, isPublic: boolean, isConst: boolean) { + super(ctx, doc); + this._isPublic = isPublic; + this.diagnosticCapability = new DiagnosticCapability(this); + this.symbolInformationCapability = new SymbolInformationCapability(this, ctx.toSymbolKind()); + this.semanticTokenCapability = new SemanticTokenCapability(this, SemanticTokenTypes.variable, isConst ? [SemanticTokenModifiers.declaration, SemanticTokenModifiers.readonly] : [SemanticTokenModifiers.declaration]); + this.identifierCapability = new IdentifierCapability({element: this, getNameContext: () => ctx.ambiguousIdentifier()}); + } +} + + +export class TypeSuffixElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSemanticTokenCapability { + diagnosticCapability: DiagnosticCapability; + semanticTokenCapability: SemanticTokenCapability; + + constructor(ctx: TypeSuffixContext, doc: TextDocument) { + super(ctx, doc); + this.semanticTokenCapability = new SemanticTokenCapability(this, SemanticTokenTypes.class, []); + this.diagnosticCapability = new DiagnosticCapability(this, + () => this.evaluateDiagnostics() + ); + } + + private evaluateDiagnostics = () => { + // TODO: Make this diagnostic optional. + this.diagnosticCapability.diagnostics.push( + new LegacyFunctionalityDiagnostic( + this.context.range, + 'Type hints' + ) + ); + return this.diagnosticCapability.diagnostics; + } +} \ No newline at end of file diff --git a/server/src/project/parser/vbaAntlr.ts b/server/src/project/parser/vbaAntlr.ts new file mode 100644 index 0000000..1e5f95c --- /dev/null +++ b/server/src/project/parser/vbaAntlr.ts @@ -0,0 +1,71 @@ +// Antlr +import { CharStream, CommonTokenStream, TokenStream } from 'antlr4ng'; +import { DefaultErrorStrategy, Parser, RecognitionException } from 'antlr4ng'; +import { vbaLexer } from '../../antlr/out/vbaLexer'; +import { vbaParser } from '../../antlr/out/vbaParser'; +import { vbapreLexer } from '../../antlr/out/vbapreLexer'; +import { vbapreParser } from '../../antlr/out/vbapreParser'; + + +export class VbaLexer extends vbaLexer { + constructor(input: CharStream) { + super(input); + } + + static create(doc: string): VbaLexer { + return new VbaLexer(CharStream.fromString(doc)) + } +} + + +export class VbaParser extends vbaParser { + constructor(input: TokenStream) { + super(input); + + } + + static create(document: string): VbaParser { + const lexer = VbaLexer.create(document); + const parser = new VbaParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.errorHandler = new VbaErrorHandler(); + return parser; + } +} + + +export class VbaPreLexer extends vbapreLexer { + constructor(input: CharStream) { + super(input); + } + + static create(doc: string): VbaPreLexer { + return new VbaPreLexer(CharStream.fromString(doc)) + } +} + + +export class VbaPreParser extends vbapreParser { + constructor(input: TokenStream) { + super(input); + } + + static create(document: string): VbaPreParser { + const lexer = VbaPreLexer.create(document); + const parser = new VbaPreParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.errorHandler = new VbaErrorHandler(); + return parser; + } +} + + +export class VbaErrorHandler extends DefaultErrorStrategy { + recover(recognizer: Parser, e: RecognitionException): void { + const inputStream = recognizer.inputStream; + // if (!recognizer.isMatchedEOF) { + inputStream.consume(); + // } + this.endErrorCondition(recognizer); + } +} \ No newline at end of file diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts new file mode 100644 index 0000000..ed37ecf --- /dev/null +++ b/server/src/project/parser/vbaListener.ts @@ -0,0 +1,215 @@ +// Antlr +import { ErrorNode, ParserRuleContext } from 'antlr4ng'; +import { vbaListener } from '../../antlr/out/vbaListener'; +import { vbapreListener } from '../../antlr/out/vbapreListener'; +import { CompilerConditionalStatementContext, CompilerElseStatementContext, CompilerEndIfStatementContext, CompilerIfBlockContext } from '../../antlr/out/vbapreParser'; +import { + AnyOperatorContext, + ClassModuleContext, + EnumDeclarationContext, + GlobalVariableDeclarationContext, + IgnoredClassAttrContext, + IgnoredProceduralAttrContext, + PrivateConstDeclarationContext, + PrivateTypeDeclarationContext, + PrivateVariableDeclarationContext, + ProceduralModuleContext, + ProcedureDeclarationContext, + PropertyGetDeclarationContext, + PropertySetDeclarationContext, + PublicConstDeclarationContext, + PublicTypeDeclarationContext, + PublicVariableDeclarationContext, + TypeSuffixContext, + UdtDeclarationContext, + WhileStatementContext +} from '../../antlr/out/vbaParser'; + +// Project +import { DuplicateOperatorElement, WhileLoopElement } from '../elements/flow'; +import { CompilerLogicalBlock, GenericCommentElement } from '../elements/precompiled'; +import { ClassElement, ModuleElement, ModuleIgnoredAttributeElement } from '../elements/module'; +import { DocumentSettings, VbaClassDocument, VbaModuleDocument } from '../document'; +import { PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement } from '../elements/procedure'; +import { DeclarationStatementElement, EnumDeclarationElement, TypeDeclarationElement, TypeSuffixElement } from '../elements/typing'; + + +class CommonParserCapability { + document: VbaClassDocument | VbaModuleDocument; + protected _documentSettings?: DocumentSettings; + + get documentSettings(): DocumentSettings { + if (!this._documentSettings) { + throw new Error("Sad times"); + + } + return this._documentSettings; + } + + constructor(document: VbaClassDocument | VbaModuleDocument) { + this.document = document; + } + + async ensureHasSettingsAsync() { + this._documentSettings = await this.document.getDocumentConfiguration(); + } +} + + +export class VbaListener extends vbaListener { + document: VbaClassDocument | VbaModuleDocument; + protected documentSettings?: DocumentSettings; + protected isAfterMethodDeclaration = false; + + constructor(document: VbaClassDocument | VbaModuleDocument) { + super(); + this.document = document; + } + + static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + const result = new VbaListener(document); + await result.ensureHasSettingsAsync(); + return result; + } + + async ensureHasSettingsAsync() { + this.documentSettings = await this.document.getDocumentConfiguration(); + } + + enterAnyOperator = (ctx: AnyOperatorContext) => { + const element = new DuplicateOperatorElement(ctx, this.document.textDocument); + this.document.registerDiagnosticElement(element); + } + + enterEnumDeclaration = (ctx: EnumDeclarationContext) => { + const element = new EnumDeclarationElement(ctx, this.document.textDocument, this.isAfterMethodDeclaration); + this.document.registerElement(element) + .registerNamespaceElement(element); + }; + + exitEnumDeclaration = (_: EnumDeclarationContext) => + this.document.deregisterNamespaceElement(); + + enterClassModule = (ctx: ClassModuleContext) => { + const element = new ClassElement(ctx, this.document.textDocument, this.documentSettings ?? { doWarnOptionExplicitMissing: true }); + this.document.registerElement(element) + .registerNamespaceElement(element) + }; + + exitClassModule = (_: ClassModuleContext) => + this.document.deregisterNamespaceElement(); + + enterIgnoredClassAttr = (ctx: IgnoredClassAttrContext) => this.registerIgnoredAttribute(ctx); + enterIgnoredProceduralAttr = (ctx: IgnoredProceduralAttrContext) => this.registerIgnoredAttribute(ctx); + private registerIgnoredAttribute(ctx: IgnoredClassAttrContext | IgnoredProceduralAttrContext) { + this.document.registerDiagnosticElement(new ModuleIgnoredAttributeElement(ctx, this.document.textDocument)) + } + + enterProceduralModule = (ctx: ProceduralModuleContext) => { + const element = new ModuleElement(ctx, this.document.textDocument, this.documentSettings ?? { doWarnOptionExplicitMissing: true }); + this.document.registerElement(element) + .registerNamespaceElement(element) + }; + + exitProceduralModule = (_: ProceduralModuleContext) => + this.document.deregisterNamespaceElement(); + + // Handles exiting of a sub, func, or property. + exitProcedureDeclaration = (ctx: ProcedureDeclarationContext) => { + this.isAfterMethodDeclaration = true; + this.document.deregisterNamespaceElement(); + }; + + enterPropertyGetDeclaration = (ctx: PropertyGetDeclarationContext) => { + const element = new PropertyGetDeclarationElement(ctx, this.document.textDocument); + this.document.registerElement(element) + .registerNamespaceElement(element); + }; + + enterPropertySetDeclaration = (ctx: PropertySetDeclarationContext) => { + const element = !!ctx.LET() + ? new PropertyLetDeclarationElement(ctx, this.document.textDocument) + : new PropertySetDeclarationElement(ctx, this.document.textDocument); + this.document.registerElement(element) + .registerNamespaceElement(element); + }; + + enterPublicTypeDeclaration = (ctx: PublicTypeDeclarationContext) => this.enterTypeDeclaration(ctx, true); + enterPrivateTypeDeclaration = (ctx: PrivateTypeDeclarationContext) => this.enterTypeDeclaration(ctx, false); + private enterTypeDeclaration = (ctx: PublicTypeDeclarationContext | PrivateTypeDeclarationContext, isPrivate: boolean) => { + const element = new TypeDeclarationElement(ctx, this.document.textDocument, isPrivate); + this.document.registerElement(element).registerNamespaceElement(element); + } + + enterTypeSuffix = (ctx: TypeSuffixContext) => + this.document.registerElement(new TypeSuffixElement(ctx, this.document.textDocument)); + + // Handles public and private type declarations. + exitUdtDeclaration = (_: UdtDeclarationContext) => + this.document.deregisterNamespaceElement(); + + // Variables + enterPublicConstDeclaration = (ctx: PublicConstDeclarationContext) => this.enterVariableDeclaration(ctx); + enterPrivateConstDeclaration = (ctx: PrivateConstDeclarationContext) => this.enterVariableDeclaration(ctx); + enterPublicVariableDeclaration = (ctx: PublicVariableDeclarationContext) => this.enterVariableDeclaration(ctx); + enterGlobalVariableDeclaration = (ctx: GlobalVariableDeclarationContext) => this.enterVariableDeclaration(ctx); + enterPrivateVariableDeclaration = (ctx: PrivateVariableDeclarationContext) => this.enterVariableDeclaration(ctx); + private enterVariableDeclaration = (ctx: PublicConstDeclarationContext | PrivateConstDeclarationContext | PublicVariableDeclarationContext | GlobalVariableDeclarationContext | PrivateVariableDeclarationContext) => { + const element = DeclarationStatementElement.create(ctx, this.document.textDocument); + element.declarations.forEach(x => this.document.registerElement(x)); + } + + enterWhileStatement = (ctx: WhileStatementContext) => { + const element = new WhileLoopElement(ctx, this.document.textDocument) + this.document.registerDiagnosticElement(element); + }; + + visitErrorNode(node: ErrorNode) { + console.log(node.getPayload()); + } +} + + +export class VbaPreListener extends vbapreListener { + common: CommonParserCapability; + + get text(): string { + return this.common.document.redactedText; + } + + constructor(document: VbaClassDocument | VbaModuleDocument) { + super(); + this.common = new CommonParserCapability(document); + } + + static async createAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + const result = new VbaPreListener(document); + await result.common.ensureHasSettingsAsync(); + return result; + } + + enterCompilerIfBlock = (ctx: CompilerIfBlockContext) => { + const doc = this.common.document; + const docprops = this.common.documentSettings; + const element = new CompilerLogicalBlock(ctx, doc.textDocument, docprops); + + // Register block subtraction and comment tokens. + element.inactiveBlocks.forEach(b => { + doc.registerSubtractElement(b); + b.linesToComments.forEach(c => + doc.registerSemanticToken(c) + .registerSemanticToken(c) + ); + }); + } + + enterCompilerElseStatement = (ctx: CompilerElseStatementContext) => this.registerSemanticComment(ctx); + enterCompilerEndIfStatement = (ctx: CompilerEndIfStatementContext) => this.registerSemanticComment(ctx); + enterCompilerConditionalStatement = (ctx: CompilerConditionalStatementContext) => this.registerSemanticComment(ctx); + + private registerSemanticComment(ctx: ParserRuleContext) { + const doc = this.common.document; + const element = new GenericCommentElement(ctx, doc.textDocument); + doc.registerSubtractElement(element); + } +} \ No newline at end of file diff --git a/server/src/project/parser/vbaParser.ts b/server/src/project/parser/vbaParser.ts new file mode 100644 index 0000000..4d299fb --- /dev/null +++ b/server/src/project/parser/vbaParser.ts @@ -0,0 +1,35 @@ +// Antlr +import { ParseTreeWalker } from 'antlr4ng'; +import { VbaParser, VbaPreParser } from './vbaAntlr'; +import { VbaListener, VbaPreListener } from './vbaListener'; + +// Project +import { VbaClassDocument, VbaModuleDocument } from '../document'; + + +export class SyntaxParser { + async parseAsync(document: VbaClassDocument | VbaModuleDocument): Promise { + // Preparse the document if we find a precompiler statement. + const regexp = new RegExp(/^\s*#If/gmi) + let docText = document.textDocument.getText(); + if (regexp.test(docText)) { + const prelistener = await VbaPreListener.createAsync(document); + const preparser = VbaPreParser.create(docText); + await this.parseDocumentAsync(prelistener, preparser); + docText = prelistener.text; + } + + // Perform main document parse without compiler directives. + const listener = await VbaListener.createAsync(document); + const parser = VbaParser.create(docText); + await this.parseDocumentAsync(listener, parser); + return true; + } + + private async parseDocumentAsync(listener: VbaListener | VbaPreListener, parser: VbaParser | VbaPreParser) { + ParseTreeWalker.DEFAULT.walk( + listener, + parser.startRule() + ); + } +} diff --git a/server/src/project/parser/vbaSyntaxParser.ts b/server/src/project/parser/vbaSyntaxParser.ts deleted file mode 100644 index b25fccb..0000000 --- a/server/src/project/parser/vbaSyntaxParser.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; - -import { vbaLexer } from '../../antlr/out/vbaLexer'; -import { ClassModuleContext, ConstItemContext, EnumDeclarationContext, IgnoredAttrContext, ProceduralModuleContext, ProcedureDeclarationContext, UdtDeclarationContext, WhileStatementContext, vbaParser } from '../../antlr/out/vbaParser'; -import { vbaListener } from '../../antlr/out/vbaListener'; - -import { DocumentSettings, VbaClassDocument, VbaModuleDocument } from '../document'; -import { CancellationToken } from 'vscode-languageserver'; -import { CharStream, CommonTokenStream, DefaultErrorStrategy, ErrorNode, ParseTreeWalker, Parser, RecognitionException } from 'antlr4ng'; -import { ClassElement, IgnoredAttributeElement, ModuleElement } from '../elements/module'; -import { ConstDeclarationElement, DeclarationElement, EnumDeclarationElement, TypeDeclarationElement } from '../elements/memory'; -import { WhileLoopElement } from '../elements/flow'; - -export class SyntaxParser { - async parseAsync(document: VbaClassDocument | VbaModuleDocument, token: CancellationToken): Promise { - console.debug(`Parse requested: ${document.textDocument.version}`); - const listener = new VbaListener(document); - await listener.ensureHasSettings(); - const parser = this.createParser(document.textDocument); - ParseTreeWalker.DEFAULT.walk( - listener, - parser.startRule() - ); - return true; - } - - private createParser(doc: TextDocument): VbaParser { - const lexer = new VbaLexer(CharStream.fromString(doc.getText())); - const parser = new VbaParser(new CommonTokenStream(lexer)); - - parser.removeErrorListeners(); - parser.errorHandler = new VbaErrorHandler(); - return parser; - } -} - -class VbaLexer extends vbaLexer { - constructor(input: CharStream) { - super(input); - } -} - -class VbaParser extends vbaParser { - -} - -class VbaListener extends vbaListener { - document: VbaClassDocument | VbaModuleDocument; - protected _documentSettings?: DocumentSettings; - protected _isAfterMethodDeclaration = false; - - constructor(document: VbaClassDocument | VbaModuleDocument) { - super(); - this.document = document; - } - - async ensureHasSettings() { - this._documentSettings = await this.document.getDocumentConfiguration(); - } - - enterEnumDeclaration = (ctx: EnumDeclarationContext) => { - const element = new EnumDeclarationElement(ctx, this.document.textDocument, this._isAfterMethodDeclaration); - this.document.registerFoldableElement(element) - .registerScopedElement(element) - .registerSemanticToken(element) - .registerSymbolInformation(element) - .registerDiagnosticElement(element); - element.declaredNames.forEach(names => - names.forEach(name => this.document - .registerSemanticToken(name) - .registerSymbolInformation(name)) - ); - }; - - exitEnumDeclaration = (_: EnumDeclarationContext) => { - this.document.deregisterScopedElement(); - }; - - enterClassModule = (ctx: ClassModuleContext) => { - const element = new ClassElement(ctx, this.document.textDocument, this._documentSettings ?? {doWarnOptionExplicitMissing: true}); - this.document.registerSymbolInformation(element) - .registerDiagnosticElement(element) - .registerScopedElement(element); - }; - - exitClassModule = (ctx: ClassModuleContext) => { - this.document.deregisterScopedElement(); - }; - - enterConstItem = (ctx: ConstItemContext) => { - const element = new ConstDeclarationElement(ctx, this.document.textDocument); - this.document.registerSemanticToken(element) - .registerSymbolInformation(element); - }; - - enterIgnoredAttr = (ctx: IgnoredAttrContext) => { - const element = new IgnoredAttributeElement(ctx, this.document.textDocument); - this.document.registerDiagnosticElement(element); - }; - - enterProceduralModule = (ctx: ProceduralModuleContext) => { - const element = new ModuleElement(ctx, this.document.textDocument, this._documentSettings ?? {doWarnOptionExplicitMissing: true}); - this.document.registerSymbolInformation(element) - .registerDiagnosticElement(element) - .registerScopedElement(element); - }; - - exitProceduralModule = (ctx: ProceduralModuleContext) => { - this.document.deregisterScopedElement(); - }; - - enterProcedureDeclaration = (ctx: ProcedureDeclarationContext) => { - const element = DeclarationElement.create(ctx, this.document); - this.document.registerSymbolInformation(element) - .registerFoldableElement(element) - .registerScopedElement(element); - - if (element.isPropertyElement() && element.countDeclarations === 1) { - this.document.registerDiagnosticElement(element) - .registerNamedElement(element); - } - }; - - exitProcedureDeclaration = (ctx: ProcedureDeclarationContext) => { - this._isAfterMethodDeclaration = true; - this.document.deregisterScopedElement(); - }; - - enterUdtDeclaration = (ctx: UdtDeclarationContext) => { - const element = new TypeDeclarationElement(ctx, this.document.textDocument); - this.document.registerFoldableElement(element) - .registerSemanticToken(element) - .registerSymbolInformation(element); - element.declaredNames.forEach(names => - names.forEach(name => this.document - .registerSemanticToken(name) - .registerSymbolInformation(name)) - ); - }; - - enterWhileStatement = (ctx: WhileStatementContext) => { - const element = new WhileLoopElement(ctx, this.document.textDocument); - this.document.registerDiagnosticElement(element); - }; - - visitErrorNode(node: ErrorNode) { - console.log(node.getPayload()); - } - - // enterAttributeStmt = (ctx: AttributeStmtContext) => { - // this.document.activeAttributeElement?.processAttribute(ctx); - // }; - - // enterConstStmt = (ctx: ConstStmtContext) => { - // const element = new ConstDeclarationsElement(ctx, this.document.textDocument); - // element.declarations.forEach((e) => this.document.registerSymbolInformation(e)); - // }; - - // enterOperatorsStmt = (ctx: OperatorsStmtContext) => { - // const element = new OperatorElement(ctx, this.document.textDocument); - // this.document.registerDiagnosticElement(element); - // enterModule = (ctx: ModuleContext) => { - // const element = new ModuleElement(ctx, this.document.textDocument, this.document.symbolKind); - // this.document.registerAttributeElement(element) - // .registerScopedElement(element); - // }; - - // enterTypeStmt = (ctx: TypeStmtContext) => { - // const element = new TypeDeclarationElement(ctx, this.document.textDocument); - // this.document.registerSymbolInformation(element) - // .registerSemanticToken(element); - // }; - - // enterVariableStmt = (ctx: VariableStmtContext) => { - // const element = new VariableDeclarationsElement(ctx, this.document.textDocument); - // element.declarations.forEach((e) => this.document.registerSymbolInformation(e)); - // }; - - // enterWhileWendStmt = (ctx: WhileWendStmtContext) => { - // const element = new WhileWendLoopElement(ctx, this.document.textDocument); - // this.document.registerDiagnosticElement(element); - // }; -} - -class VbaErrorHandler extends DefaultErrorStrategy { - recover(recognizer: Parser, e: RecognitionException): void { - const inputStream = recognizer.inputStream; - // if (!recognizer.isMatchedEOF) { - inputStream.consume(); - // } - this.endErrorCondition(recognizer); - } -} - -// class VbaErrorListener extends ConsoleErrorListener { -// syntaxError(recognizer: Recognizer, offendingSymbol: T, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined): void { -// super.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); -// console.error(e); -// if (e) { -// const y = recognizer.getErrorHeader(e); -// console.log(y); -// } -// } -// } - -// class VbaTreeWalkListener implements vbaListener { -// document: VbaClassDocument | VbaModuleDocument; - -// constructor(document: VbaClassDocument | VbaModuleDocument) { -// this.document = document; -// } - -// visitErrorNode(node: ErrorNode) { -// console.log(node.payload); -// } - -// enterAttributeStmt = (ctx: AttributeStmtContext) => { -// this.document.activeAttributeElement?.processAttribute(ctx); -// }; - -// enterConstStmt = (ctx: ConstStmtContext) => { -// const element = new ConstDeclarationsElement(ctx, this.document.textDocument); -// element.declarations.forEach((e) => this.document.registerSymbolInformation(e)); -// }; - -// enterEnumerationStmt = (ctx: EnumerationStmtContext) => { -// const element = new EnumBlockDeclarationElement(ctx, this.document.textDocument); -// this.document.registerFoldableElement(element); -// this.document.registerSemanticToken(element); -// this.document.registerSymbolInformation(element); -// this.document.registerScopedElement(element); -// }; - -// exitEnumerationStmt = (_: EnumerationStmtContext) => { -// console.warn("Entered enum statement."); -// this.document.deregisterScopedElement(); -// }; - -// enterEnumerationStmt_Constant = (ctx: EnumerationStmt_ConstantContext) => { -// const element = new EnumMemberDeclarationElement(ctx, this.document.textDocument); -// this.document.registerSymbolInformation(element); -// this.document.registerSemanticToken(element); -// }; - -// enterFoldingBlockStmt = (ctx: FoldingBlockStmtContext) => { -// const element = new FoldableElement(ctx, this.document.textDocument); -// this.document.registerFoldableElement(element); -// }; - -// enterMethodStmt = (ctx: MethodStmtContext) => { -// const element = new MethodBlockDeclarationElement(ctx, this.document.textDocument); -// this.document.registerNamedElement(element); -// this.document.registerFoldableElement(element); -// this.document.registerSymbolInformation(element); -// this.document.registerScopedElement(element); -// }; - -// exitMethodStmt = (_: MethodStmtContext) => { -// this.document.deregisterScopedElement(); -// }; - -// enterModule = (ctx: ModuleContext) => { -// const element = new ModuleElement(ctx, this.document.textDocument, this.document.symbolKind); -// this.document.registerAttributeElement(element); -// this.document.registerScopedElement(element); -// }; - -// exitModule = (_: ModuleContext) => { -// const element = this.document.deregisterAttributeElement() as ModuleElement; -// this.document.registerSymbolInformation(element); -// this.document.deregisterScopedElement(); -// this.document.deregisterAttributeElement(); -// }; - -// enterModuleHeader = (ctx: ModuleHeaderContext) => { -// const element = new FoldableElement(ctx, this.document.textDocument); -// this.document.registerFoldableElement(element); -// }; - -// enterVariableStmt = (ctx: VariableStmtContext) => { -// console.warn("Entered value statement. " + ctx.text); -// const element = new VariableDeclarationsElement(ctx, this.document.textDocument); -// element.declarations.forEach((e) => this.document.registerSymbolInformation(e)); -// }; - -// enterOperatorsStmt = (ctx: OperatorsStmtContext) => { -// const element = new OperatorElement(ctx, this.document.textDocument); -// this.document.registerDiagnosticElement(element); -// }; -// } - -// class VbaErrorListener extends ConsoleErrorListener { -// syntaxError(recognizer: Recognizer, offendingSymbol: T, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined): void { -// super.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); -// console.error(e); -// if (e) { -// const y = recognizer.getErrorHeader(e); -// console.log(y); -// } -// recognizer.inputStream?.consume(); -// } -// } diff --git a/server/src/project/scope.ts b/server/src/project/scope.ts new file mode 100644 index 0000000..6a3b797 --- /dev/null +++ b/server/src/project/scope.ts @@ -0,0 +1,64 @@ +// Core +import { Diagnostic } from 'vscode-languageserver'; + +// Project +import { DeclarableElement } from './elements/base'; +import { DuplicateDeclarationDiagnostic, ShadowDeclarationDiagnostic } from '../capabilities/diagnostics'; + + +export class NamespaceManager { + private names: Map = new Map(); + private scopeStack: {namespace: DeclarableElement, names: Map }[] = []; + + /** + * Begins tracking a namespace item against a namespace. + * @returns A diagnostic if the item has already been declared in this space. + */ + addNameItem = (item: DeclarableElement): void => { + const pushDiagnostic = (x: Diagnostic) => item.diagnosticCapability.diagnostics.push(x); + + // Check current scope for duplicate declaration. + let checkItem = this.scopeStack.at(-1)?.names.get(item.identifierCapability.name); + if (!!checkItem && !checkItem.equals(item)) { + pushDiagnostic(new DuplicateDeclarationDiagnostic(item.identifierCapability.range)); + return; + } + + // Add the name to the current scope. + this.scopeStack.at(-1)?.names.set(item.identifierCapability.name, item); + + // Check higher scopes for shadowed declarations + checkItem = this.names.get(item.identifierCapability.name) + if (!!checkItem && !checkItem.equals(item)) { + pushDiagnostic(new ShadowDeclarationDiagnostic(item.context.range)); + return; + } + this.names.set(item.identifierCapability.name, item); + } + + /** + * Adds a namespace to the stack and tracks names. + * @param scope The namespace to add. + */ + addNamespace = (scope: DeclarableElement) => { + this.addNameItem(scope); // a namespace is also a name + this.scopeStack.push({namespace: scope, names: new Map()}); + } + + /** + * Removes the namespace and all names associated with it. + */ + popNamespace = (): void => { + const ns = this.scopeStack.pop(); + + // Remove the items in the current scope if they are not public. + ns?.names.forEach((_, x) => { + if (!(this.names.get(x)?.isPublic ?? true)) { this.names.delete(x); } + }); + + // Remove the current scope. + if (ns && !ns.namespace.isPublic && this.names.has(ns.namespace.identifierCapability.name)) { + this.names.delete(ns.namespace.identifierCapability.name); + } + } +} diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 6ad75eb..4c59399 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -1,21 +1,45 @@ -import { CancellationToken, CancellationTokenSource, CompletionItem, CompletionParams, DidChangeConfigurationNotification, DidChangeWatchedFilesParams, DidOpenTextDocumentParams, DocumentSymbolParams, FoldingRange, FoldingRangeParams, Hover, HoverParams, SemanticTokensRangeParams, SymbolInformation, TextDocuments, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver'; +// Core +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { + CancellationToken, + CancellationTokenSource, + CompletionItem, + CompletionParams, + DidChangeConfigurationNotification, + DidChangeWatchedFilesParams, + DidOpenTextDocumentParams, + DocumentDiagnosticParams, + DocumentDiagnosticReport, + DocumentDiagnosticReportKind, + DocumentSymbolParams, + FoldingRange, + FoldingRangeParams, + Hover, + HoverParams, + SemanticTokensRangeParams, + SymbolInformation, + TextDocuments, + WorkspaceFoldersChangeEvent, + _Connection +} from 'vscode-languageserver'; + import { BaseProjectDocument } from './document'; import { LanguageServerConfiguration } from '../server'; import { hasConfigurationCapability } from '../capabilities/workspaceFolder'; -import { TextDocument } from 'vscode-languageserver-textdocument'; import { sleep } from '../utils/helpers'; +import { NamespaceManager } from './scope'; /** - * Organises project documents and runs actions - * at a workspace level. + * Organises project documents and runs actions at a workspace level. */ export class Workspace { - private _events: WorkspaceEvents; - private _documents: BaseProjectDocument[] = []; + private events: WorkspaceEvents; + private nsManager: NamespaceManager = new NamespaceManager(); + private documents: BaseProjectDocument[] = []; + private parseCancellationTokenSource?: CancellationTokenSource; + private _activeDocument?: BaseProjectDocument; - private _publicScopeDeclarations: Map = new Map(); - private _parseCancellationTokenSource?: CancellationTokenSource; private readonly _hasConfigurationCapability: boolean; get hasConfigurationCapability() { @@ -28,10 +52,14 @@ export class Workspace { return this._activeDocument; } + get namespaceManager() { + return this.nsManager; + } + constructor(params: {connection: _Connection, capabilities: LanguageServerConfiguration}) { this.connection = params.connection; this._hasConfigurationCapability = hasConfigurationCapability(params.capabilities); - this._events = new WorkspaceEvents({ + this.events = new WorkspaceEvents({ workspace: this, connection: params.connection, configuration: params.capabilities, @@ -46,18 +74,17 @@ export class Workspace { async parseActiveDocument(document?: BaseProjectDocument) { this.activateDocument(document); - this._parseCancellationTokenSource?.cancel(); - this._parseCancellationTokenSource = new CancellationTokenSource(); + this.parseCancellationTokenSource?.cancel(); + this.parseCancellationTokenSource = new CancellationTokenSource(); // Exceptions thrown by the parser should be ignored. try { - await this._activeDocument?.parseAsync(this._parseCancellationTokenSource.token); + await this.activeDocument?.parseAsync(this.parseCancellationTokenSource.token); } catch (error) { this.connection.console.log(`Parser error: ${error}`) } - this._parseCancellationTokenSource = undefined; - this.connection.sendDiagnostics(this._activeDocument?.languageServerDiagnostics() ?? {uri: "", diagnostics: []}); + this.parseCancellationTokenSource = undefined; } /** @@ -85,25 +112,29 @@ export class Workspace { }); clearDocumentsConfiguration = () => { - this._documents.forEach(d => d.clearDocumentConfiguration()); + this.documents.forEach(d => d.clearDocumentConfiguration()); + this.connection.languages.diagnostics.refresh(); } } + // TODO: This class should not be doing anything with connection. class WorkspaceEvents { - private readonly _workspace: Workspace; - private readonly _documents: TextDocuments; - private readonly _configuration: LanguageServerConfiguration; + private readonly workspace: Workspace; + private readonly documents: TextDocuments; + private readonly configuration: LanguageServerConfiguration; + private readonly parsedDocuments: Map; - private _activeDocument?: BaseProjectDocument; + private activeDocument?: BaseProjectDocument; constructor(params: {connection: _Connection, workspace: Workspace, configuration: LanguageServerConfiguration}) { - this._workspace = params.workspace; - this._configuration = params.configuration; - this._documents = new TextDocuments(TextDocument); + this.workspace = params.workspace; + this.configuration = params.configuration; + this.documents = new TextDocuments(TextDocument); + this.parsedDocuments = new Map(); this.initialiseConnectionEvents(params.connection); - this._initialiseDocumentsEvents(); - this._documents.listen(params.connection); + this.initialiseDocumentsEvents(); + this.documents.listen(params.connection); } /** @@ -114,7 +145,7 @@ class WorkspaceEvents { */ private async activeParsedDocument(version: number, token: CancellationToken): Promise { let document: BaseProjectDocument | undefined; - document = this._activeDocument; + document = this.activeDocument; // Sleep between attempting to grab the document. // Loop while we have undefined or an earlier version. @@ -123,7 +154,7 @@ class WorkspaceEvents { return; } await sleep(5); - document = this._activeDocument; + document = this.activeDocument; } // Return if the version somehow outpaced us. @@ -138,28 +169,56 @@ class WorkspaceEvents { return document; } + private async getParsedDocument(uri: string, version: number, token: CancellationToken): Promise { + // Handle token cancellation. + if (token.isCancellationRequested) { throw new Error("Request cancelled before start."); } + token.onCancellationRequested(() => { throw new Error("Request cancelled during run."); }); + + let document: BaseProjectDocument | undefined; + document = this.parsedDocuments.get(uri); + + // Ensure we have the appropriately versioned document. + while (!document || document.textDocument.version < version) { + await sleep(5); + document = this.parsedDocuments.get(uri); + } + + // Return nothing if the document version is newer than requested. + if (version > 0 && document.textDocument.version != version) { + return; + } + + // Ensure the document is parsed. + while (document.isBusy) { + await sleep(5); + } + + return document; + } + private initialiseConnectionEvents(connection: _Connection) { - connection.onInitialized(() => this._onInitialized()); - connection.onDidOpenTextDocument(params => this._onDidOpenTextDocumentAsync(params)); - connection.onCompletion(params => this._onCompletion(params)); - connection.onCompletionResolve(item => this._onCompletionResolve(item)); - connection.onDidChangeConfiguration(_ => this._workspace.clearDocumentsConfiguration()); - connection.onDidChangeWatchedFiles(params => this._onDidChangeWatchedFiles(params)); - connection.onDocumentSymbol(async (params, token) => await this._onDocumentSymbolAsync(params, token)); - connection.onHover(params => this._onHover(params)); - - if (hasConfigurationCapability(this._configuration)) { - connection.onFoldingRanges(async (params, token) => this._onFoldingRangesAsync(params, token)); + connection.onInitialized(() => this.onInitialized()); + connection.onDidOpenTextDocument(params => this.onDidOpenTextDocumentAsync(params)); + connection.onCompletion(params => this.onCompletion(params)); + connection.onCompletionResolve(item => this.onCompletionResolve(item)); + connection.onDidChangeConfiguration(_ => this.workspace.clearDocumentsConfiguration()); + connection.onDidChangeWatchedFiles(params => this.onDidChangeWatchedFiles(params)); + connection.onDocumentSymbol(async (params, token) => await this.onDocumentSymbolAsync(params, token)); + connection.onHover(params => this.onHover(params)); + connection.languages.diagnostics.on(async (params, token) => await this.onDiagnosticAsync(params, token)); + + if (hasConfigurationCapability(this.configuration)) { + connection.onFoldingRanges(async (params, token) => this.onFoldingRangesAsync(params, token)); } connection.onRequest((method: string, params: object | object[] | any) => { switch (method) { case 'textDocument/semanticTokens/full': { - return this._activeDocument?.languageServerSemanticTokens(); + return this.activeDocument?.languageServerSemanticTokens(); } case 'textDocument/semanticTokens/range': { const rangeParams = params as SemanticTokensRangeParams; - return this._activeDocument?.languageServerSemanticTokens(rangeParams.range); + return this.activeDocument?.languageServerSemanticTokens(rangeParams.range); } default: console.error(`Unresolved request path: ${method}`); @@ -167,56 +226,62 @@ class WorkspaceEvents { }); } - - - private _initialiseDocumentsEvents() { - this._documents.onDidChangeContent(async (e) => await this.onDidChangeContentAsync(e.document)); + private initialiseDocumentsEvents() { + this.documents.onDidChangeContent(async (e) => await this.onDidChangeContentAsync(e.document)); } /** Connection event handlers */ - private _onCompletion(params: CompletionParams): never[] { + private onCompletion(params: CompletionParams): never[] { return []; } - private _onCompletionResolve(item: CompletionItem): CompletionItem { + private onCompletionResolve(item: CompletionItem): CompletionItem { return item; } - private _onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) { + private onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) { return; } // TODO: Should trigger a full workspace refresh. - private _onDidChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent) { - this._workspace.connection.console.log(`Workspace folder change event received.\n${e}`); + private onDidChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent) { + this.workspace.connection.console.log(`Workspace folder change event received.\n${e}`); } - private async _onDocumentSymbolAsync(params: DocumentSymbolParams, token: CancellationToken): Promise { + private async onDocumentSymbolAsync(params: DocumentSymbolParams, token: CancellationToken): Promise { const document = await this.activeParsedDocument(0, token); return document?.languageServerSymbolInformation() ?? []; } - private async _onFoldingRangesAsync(params: FoldingRangeParams, token: CancellationToken): Promise { + private async onDiagnosticAsync(params: DocumentDiagnosticParams, token: CancellationToken): Promise { const document = await this.activeParsedDocument(0, token); + return document?.languageServerDiagnostics() ?? { + kind: DocumentDiagnosticReportKind.Full, + items: [] + } satisfies DocumentDiagnosticReport; + } + + private async onFoldingRangesAsync(params: FoldingRangeParams, token: CancellationToken): Promise { + const document = await this.getParsedDocument(params.textDocument.uri, 0, token); const result = document?.languageServerFoldingRanges(); return result ?? []; } - private _onHover(params: HoverParams): Hover { - console.debug(`_onHover`); + private onHover(params: HoverParams): Hover { + console.debug(`onHover`); return { contents: '' }; } - private _onInitialized(): void { - const connection = this._workspace.connection; + private onInitialized(): void { + const connection = this.workspace.connection; // Register for client configuration notification changes. connection.client.register(DidChangeConfigurationNotification.type, undefined); // This is how we can listen for changes to workspace folders. - if (hasConfigurationCapability(this._configuration)) { + if (hasConfigurationCapability(this.configuration)) { connection.workspace.onDidChangeWorkspaceFolders(e => - this._onDidChangeWorkspaceFolders(e) + this.onDidChangeWorkspaceFolders(e) ); connection.client.register(DidChangeConfigurationNotification.type, undefined); } @@ -228,8 +293,8 @@ class WorkspaceEvents { * This event handler is called whenever a `TextDocuments` is changed. * @param doc The document that changed. */ - async _onDidOpenTextDocumentAsync(params: DidOpenTextDocumentParams) { - await this._handleChangeOrOpenAsync(TextDocument.create( + async onDidOpenTextDocumentAsync(params: DidOpenTextDocumentParams) { + await this.handleChangeOrOpenAsync(TextDocument.create( params.textDocument.uri, params.textDocument.languageId, params.textDocument.version, @@ -238,11 +303,13 @@ class WorkspaceEvents { } async onDidChangeContentAsync(document: TextDocument) { - await this._handleChangeOrOpenAsync(document); + await this.handleChangeOrOpenAsync(document); } - protected async _handleChangeOrOpenAsync(document: TextDocument) { - this._activeDocument = BaseProjectDocument.create(this._workspace, document); - await this._workspace.parseActiveDocument(this._activeDocument); + protected async handleChangeOrOpenAsync(document: TextDocument) { + const doc = BaseProjectDocument.create(this.workspace, document); + this.parsedDocuments.set(document.uri, doc); + this.activeDocument = doc; + await this.workspace.parseActiveDocument(this.activeDocument); } } diff --git a/server/src/server.ts b/server/src/server.ts index 8c4ca13..adf83f6 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -12,10 +12,14 @@ import { ServerCapabilities, } from 'vscode-languageserver/node'; +// Ensures globally available type extensions. +import './extensions/stringExtensions'; +import './extensions/parserExtensions'; import { Workspace } from './project/workspace'; import { activateSemanticTokenProvider } from './capabilities/semanticTokens'; import { activateWorkspaceFolderCapability } from './capabilities/workspaceFolder'; + class LanguageServer { workspace?: Workspace; configuration?: LanguageServerConfiguration; @@ -45,10 +49,13 @@ class LanguageServer { }); + + this.connection.listen(); } } + export class LanguageServerConfiguration { params: InitializeParams; capabilities: ServerCapabilities = { @@ -56,6 +63,10 @@ export class LanguageServerConfiguration { documentSymbolProvider: true, foldingRangeProvider: true, textDocumentSync: TextDocumentSyncKind.Incremental, + diagnosticProvider: { + interFileDependencies: false, + workspaceDiagnostics: false + }, // Implement soon. codeActionProvider: false, @@ -92,6 +103,7 @@ export class LanguageServerConfiguration { } } + class ConnectionInitializeResult implements InitializeResult { [custom: string]: any; capabilities: ServerCapabilities; diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 7e27081..73ae4a0 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,3 +1,34 @@ +export class Dictionary extends Map { + private defaultFactory: (...args: any) => V; + + constructor(defaultFactory: (...args: any) => V) { + super(); + this.defaultFactory = defaultFactory; + } + + /** + * Gets the value if the key exists or creates it in the dictionary if it does not. + */ + getOrSet(key: K, ...args: any): V { + if (this.has(key)) return this.get(key)!; + const defaultValue = this.defaultFactory(args); + this.set(key, defaultValue); + return defaultValue; + } +} + + +export function isOfType(obj: unknown, property: keyof T, nullable: boolean = true): obj is T { + if (nullable) return (obj as T)[property] !== undefined; + return ( + typeof obj === 'object' + && obj !== null + && property in obj + && (!!(obj as T)[property]) + ) +} + + export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms) ); -} +} \ No newline at end of file diff --git a/server/tsconfig.json b/server/tsconfig.json index ca2be50..184e983 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -7,7 +7,13 @@ "sourceMap": true, "strict": true, "outDir": "out", - "rootDir": "src" + "rootDir": "src", + // "baseUrl": ".", + // "paths": { + // "@antlr/*": ["src/antlr/out/*"], + // "@project/*": ["src/project/*"], + // "@capabilities/*": ["src/capabilities/*"], + // } }, "include": ["src"], "exclude": ["node_modules", ".vscode-test"] diff --git a/server/tsconfig.tsbuildinfo b/server/tsconfig.tsbuildinfo deleted file mode 100644 index dfe055a..0000000 --- a/server/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/server.ts","./src/antlr/out/vbalexer.ts","./src/antlr/out/vbalistener.ts","./src/antlr/out/vbaparser.ts","./src/antlr/out/vbavisitor.ts","./src/capabilities/diagnostics.ts","./src/capabilities/folding.ts","./src/capabilities/semantictokens.ts","./src/capabilities/symbolinformation.ts","./src/capabilities/workspacefolder.ts","./src/extensions/parserextensions.ts","./src/extensions/stringextensions.ts","./src/project/document.ts","./src/project/workspace.ts","./src/project/elements/base.ts","./src/project/elements/flow.ts","./src/project/elements/memory.ts","./src/project/elements/module.ts","./src/project/elements/operator.ts","./src/project/elements/special.ts","./src/project/parser/vbasyntaxparser.ts","./src/utils/helpers.ts"],"version":"5.6.3"} \ No newline at end of file diff --git a/snippets/vba.json b/snippets/vba.json index 61f5556..ec4e546 100644 --- a/snippets/vba.json +++ b/snippets/vba.json @@ -208,7 +208,7 @@ "Attribute VB_Creatable = False", "Attribute VB_PredeclaredId = False", "Attribute VB_Exposed = False", - "' Copyright 2024 ${2:Name}", + "' Copyright 2024 ${3:Name}", "' ", "' Permission is hereby granted, free of charge, to any person obtaining a copy ", "' of this software and associated documentation files (the \"Software\"), to deal ", diff --git a/test/textmate/snapshot/class.cls b/test/textmate/snapshot/class.cls new file mode 100644 index 0000000..dfce942 --- /dev/null +++ b/test/textmate/snapshot/class.cls @@ -0,0 +1,12 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "ClassName" +Attribute VB_Description = "Class description goes here" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = False + +Option Explicit diff --git a/test/textmate/snapshot/class.cls.snap b/test/textmate/snapshot/class.cls.snap new file mode 100644 index 0000000..31d0110 --- /dev/null +++ b/test/textmate/snapshot/class.cls.snap @@ -0,0 +1,55 @@ +>VERSION 1.0 CLASS +#^^^^^^^ source.vba entity.other.attribute-name.block.vba +# ^ source.vba entity.other.attribute-name.block.vba +# ^^^ source.vba entity.other.attribute-name.block.vba constant.numeric.vba +# ^^^^^^^ source.vba entity.other.attribute-name.block.vba +>BEGIN +#^^^^^^ source.vba entity.other.attribute-name.block.vba +> MultiUse = -1 'True +#^^^^^^^^^^^ source.vba entity.other.attribute-name.block.vba +# ^ source.vba entity.other.attribute-name.block.vba keyword.operator.comparison.vba +# ^ source.vba entity.other.attribute-name.block.vba +# ^^ source.vba entity.other.attribute-name.block.vba constant.numeric.vba +# ^^^^^^^ source.vba entity.other.attribute-name.block.vba comment.line.apostrophe.vba +>END +#^^^ source.vba entity.other.attribute-name.block.vba +>Attribute VB_Name = "ClassName" +#^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^^^^^^^ source.vba meta.attribute.vba string.quoted.double.vba +>Attribute VB_Description = "Class description goes here" +#^^^^^^^^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba string.quoted.double.vba +>Attribute VB_GlobalNameSpace = False +#^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^ source.vba meta.attribute.vba constant.language.boolean.vba +>Attribute VB_Creatable = False +#^^^^^^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^ source.vba meta.attribute.vba constant.language.boolean.vba +>Attribute VB_PredeclaredId = False +#^^^^^^^^^^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^ source.vba meta.attribute.vba constant.language.boolean.vba +>Attribute VB_Exposed = False +#^^^^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^ source.vba meta.attribute.vba constant.language.boolean.vba +> +>Option Explicit +#^^^^^^^^^^^^^^^ source.vba keyword.control.vba +> \ No newline at end of file diff --git a/test/textmate/snapshot/module.bas b/test/textmate/snapshot/module.bas new file mode 100644 index 0000000..ae87803 --- /dev/null +++ b/test/textmate/snapshot/module.bas @@ -0,0 +1,3 @@ +Attribute VB_Name = "ModuleName" + +Option Explicit diff --git a/test/textmate/snapshot/module.bas.snap b/test/textmate/snapshot/module.bas.snap new file mode 100644 index 0000000..05a4dde --- /dev/null +++ b/test/textmate/snapshot/module.bas.snap @@ -0,0 +1,10 @@ +>Attribute VB_Name = "ModuleName" +#^^^^^^^^^^^^^^^^^ source.vba meta.attribute.vba entity.other.attribute-name.vba +# ^ source.vba meta.attribute.vba +# ^ source.vba meta.attribute.vba keyword.operator.comparison.vba +# ^ source.vba meta.attribute.vba +# ^^^^^^^^^^^^ source.vba meta.attribute.vba string.quoted.double.vba +> +>Option Explicit +#^^^^^^^^^^^^^^^ source.vba keyword.control.vba +> \ No newline at end of file diff --git a/test/textmate/unit/module/classHeaders.vba b/test/textmate/unit/module/classHeaders.vba new file mode 100644 index 0000000..563ccb0 --- /dev/null +++ b/test/textmate/unit/module/classHeaders.vba @@ -0,0 +1,57 @@ +// SYNTAX TEST "source.vba" "class headers" + + +VERSION 1.0 CLASS +// <---------------- entity.other.attribute-name.block.vba +// ^^^ constant.numeric.vba + +BEGIN +// <----- entity.other.attribute-name.block.vba + + MultiUse = -1 'True +// ^^^^^^^^^^^^^^^^^^^^ entity.other.attribute-name.block.vba +// ^ keyword.operator.comparison.vba +// ^^ constant.numeric.vba +// ^^^^^ comment.line.apostrophe.vba + +END +// <--- entity.other.attribute-name.block.vba + +Attribute VB_Name = "ClassName" +// <------------------------------ meta.attribute.vba +// <---------------- entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^^^^^^^ string.quoted.double.vba + +Attribute VB_Description = "Class description goes here" +// <------------------------------------------------------- meta.attribute.vba +// <-------------------- entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string.quoted.double.vba + +Attribute VB_GlobalNameSpace = False +// <----------------------------------- meta.attribute.vba +// <------------------------ entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^ constant.language.boolean.vba + +Attribute VB_Creatable = False +// <----------------------------- meta.attribute.vba +// <------------------ entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^ constant.language.boolean.vba + +Attribute VB_PredeclaredId = False +// <--------------------------------- meta.attribute.vba +// <------------------------- entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^ constant.language.boolean.vba + +Attribute VB_Exposed = False +// <--------------------------- meta.attribute.vba +// <------------------- entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^ constant.language.boolean.vba + +Option Explicit +// <--------------- keyword.control.vba \ No newline at end of file diff --git a/test/textmate/unit/module/moduleHeaders.vba b/test/textmate/unit/module/moduleHeaders.vba new file mode 100644 index 0000000..40b04d3 --- /dev/null +++ b/test/textmate/unit/module/moduleHeaders.vba @@ -0,0 +1,10 @@ +// SYNTAX TEST "source.vba" "module headers" + +Attribute VB_Name = "ModuleName" +// <------------------------------- meta.attribute.vba +// <----------------- entity.other.attribute-name.vba +// ^ keyword.operator.comparison.vba +// ^^^^^^^^^^^^ string.quoted.double.vba + +Option Explicit +// <--------------- keyword.control.vba \ No newline at end of file