From 5716f76204fa91a9ae7dba8d6d0b8447fdd333a8 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:00:12 +0800 Subject: [PATCH 01/29] Fix build task --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 46d6033..ec44456 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,7 +13,7 @@ ], "preLaunchTask": { "type": "npm", - "script": "compile" + "script": "build" } }, { From d1e636f46b0936fe6ecbed6dd9b396b4e821b104 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:00:21 +0800 Subject: [PATCH 02/29] Fix gitignore --- .gitignore | 23 +++++++++++------------ .vscode/settings.json | 13 ------------- 2 files changed, 11 insertions(+), 25 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 3ba2591..730ef66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,15 @@ -#Out file is required by command -#`npm run textMate`, so a placeholder is included in client/out to ensure `out` -#folder is available -**/out/* -!client/out/*.tmp -client/src/syntaxes/dev* +# Generated files. +dist/* +**/antlr/*/ +*.vsix +*.tsbuildinfo +*.log -.antlr +# Resources node_modules -client/server +sample*/** template* + +# Workspace settings .vscode-test -sample*/** -*.vsix -*.tsbuildinfo -*.log \ No newline at end of file +.vscode/settings.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index e436cf3..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "editor.insertSpaces": false, - "typescript.tsc.autoDetect": "off", - "typescript.preferences.quoteStyle": "single", - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - }, - "vbaLanguageServer.trace.server": "verbose", - "files.watcherExclude": { - "**/.git/objects/**": true, - "**/node_modules/**": true - } -} \ No newline at end of file From 5c8019b81455b6f71beae4a72f80c22661039b93 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:02:40 +0800 Subject: [PATCH 03/29] New diagnostics and support for related information --- server/src/capabilities/diagnostics.ts | 35 +++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 47e04ae..3b393b8 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -2,7 +2,11 @@ import { CodeDescription, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Position, Range } from 'vscode-languageserver'; -abstract class BaseDiagnostic implements Diagnostic { +export type DiagnosticConstructor = + (new (range: Range) => T) | (new (range: Range, message: string) => T); + + +export abstract class BaseDiagnostic implements Diagnostic { range: Range; message: string severity?: DiagnosticSeverity | undefined; @@ -19,6 +23,13 @@ abstract class BaseDiagnostic implements Diagnostic { this.range = range; this.message = message ?? 'Generic diagnostic.'; } + + addRelatedInformation(information: DiagnosticRelatedInformation): void { + if (!this.relatedInformation) { + this.relatedInformation = []; + } + this.relatedInformation.push(information); + } } @@ -55,12 +66,12 @@ export class DuplicateAttributeDiagnostic extends BaseDiagnostic { } -// test (not yet implemented) +// test export class DuplicateDeclarationDiagnostic extends BaseDiagnostic { - message = "Duplicate declaration in current scope."; severity = DiagnosticSeverity.Error; - constructor(range: Range) { + constructor(range: Range, message: string) { super(range); + this.message = `Duplicate declaration '${message}' in current scope.`; } } @@ -74,6 +85,22 @@ export class ShadowDeclarationDiagnostic extends BaseDiagnostic { } } +export class VariableNotDefinedDiagnostic extends BaseDiagnostic { + message = "Variable not defined."; + severity = DiagnosticSeverity.Error; + constructor(range: Range) { + super(range); + } +} + +export class SubOrFunctionNotDefinedDiagnostic extends BaseDiagnostic { + message = "Sub or Function not defined."; + severity = DiagnosticSeverity.Error; + constructor(range: Range) { + super(range); + } +} + // test export class UnexpectedLineEndingDiagnostic extends BaseDiagnostic { From 66cafe281069565dd0830dae6fecba909f182a5e Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:28:12 +0800 Subject: [PATCH 04/29] Implementation of scopes --- server/src/capabilities/capabilities.ts | 522 ++++++++++++++++++++++- server/src/injection/interface.ts | 2 - server/src/injection/services.ts | 9 + server/src/project/document.ts | 49 +-- server/src/project/elements/base.ts | 6 + server/src/project/elements/module.ts | 3 +- server/src/project/elements/procedure.ts | 10 +- server/src/project/elements/typing.ts | 24 +- server/src/project/parser/vbaListener.ts | 111 +++-- server/src/project/scope.ts | 64 --- server/src/project/workspace.ts | 74 ++-- 11 files changed, 684 insertions(+), 190 deletions(-) delete mode 100644 server/src/project/scope.ts diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 5081ab3..45a27f1 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -15,6 +15,8 @@ import { ParserRuleContext, TerminalNode } from 'antlr4ng'; import { SemanticToken } from '../capabilities/semanticTokens'; import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base'; +import { BaseDiagnostic, DuplicateDeclarationDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; +import { Services } from '../injection/services'; abstract class BaseCapability { @@ -30,7 +32,7 @@ export class FoldingRangeCapability extends BaseCapability { foldingRangeKind?: FoldingRangeKind; openWord?: string; closeWord?: string; - + get foldingRange(): FoldingRange { const trailingLineCount = this.element.context.rule.countTrailingLineEndings(); const start = this.element.context.range.start; @@ -89,9 +91,9 @@ export class SemanticTokenCapability extends BaseCapability { constructor(element: BaseContextSyntaxElement & HasSemanticTokenCapability, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[], overrideRange?: Range, overrideLength?: number) { super(element); - this.tokenType = tokenType; - this.tokenModifiers = tokenModifiers; - this.overrideRange = overrideRange; + this.tokenType = tokenType; + this.tokenModifiers = tokenModifiers; + this.overrideRange = overrideRange; this.overrideLength = overrideLength; } } @@ -148,4 +150,516 @@ export class IdentifierCapability extends BaseCapability { this.range = !!args.defaultRange ? args.defaultRange() : args.element.context.range; } } +} + + +export enum ItemType { + /** Base language. */ + VBA, + /** Application model. */ + APPLICATION, + /** The user's project. */ + PROJECT, + /** Class/Module/Form. */ + MODULE, + /** Function declaration. */ + FUNCTION, + /** Property declaration. */ + PROPERTY, + /** Subroutine declaration. */ + SUBROUTINE, + /** Enum or Type declaration */ + TYPE, + /** Variable declaration. */ + VARIABLE, + /** Any reference type that isn't a declaration. */ + REFERENCE +} + +export enum AssignmentType { + NONE = 0, + GET = 1 << 0, + LET = 1 << 1, + SET = 1 << 2, + CALL = 1 << 3 +} + +export class ScopeItemCapability { + // Scope references + types?: Map; + modules?: Map; + functions?: Map; + subroutines?: Map; + properties?: { + getters?: Map, + setters?: Map, + letters?: Map + }; + references?: Map; + + // Links + link?: ScopeItemCapability; + backLinks?: ScopeItemCapability[]; + + // Properties + explicitSetName?: string; + + + constructor( + readonly element?: BaseContextSyntaxElement, + readonly type: ItemType = ItemType.REFERENCE, + readonly assignmentType: AssignmentType = AssignmentType.NONE, + public parent?: ScopeItemCapability, + ) { } + + /** + * Called on a module after it is re-parsed. + * TODO: Implement logic to self-clean before build. + */ + rebuild(): void { + this.build(); + } + + /** + * Recursively build from this node down. + */ + build(): void { + if (this.type === ItemType.REFERENCE) { + // Link to declaration if it exists. + this.resolveLinks(); + if (!this.link) { + // TODO: + // Check Option Explicit for variables. + // Should always error for method calls. + + // TODO: + // Whether a diagnostic is added for missing + // declarations or not, the first instance should + // be considered the declaration for linking. + const diagnosticType = this.assignmentType & AssignmentType.CALL + ? SubOrFunctionNotDefinedDiagnostic + : VariableNotDefinedDiagnostic; + this.pushDiagnostic(diagnosticType); + } + } else { + // Diagnostic checks on declarations. + this.resolveDuplicateDeclarations(); + this.resolveShadowedDeclarations(); + } + + // Call build on children. + this.types?.forEach(items => items.forEach(item => item.build())); + this.modules?.forEach(items => items.forEach(item => item.build())); + this.functions?.forEach(items => items.forEach(item => item.build())); + this.subroutines?.forEach(items => items.forEach(item => item.build())); + this.properties?.getters?.forEach(items => items.forEach(item => item.build())); + this.properties?.letters?.forEach(items => items.forEach(item => item.build())); + this.properties?.setters?.forEach(items => items.forEach(item => item.build())); + } + + /** Resolves for the current scope, i.e., children of the current item. */ + private resolveDuplicateDeclarations() { + // Reference types are never relevant. + if (this.type == ItemType.REFERENCE) { + return; + } + + // Track diagnostics added by name so that we can + // add a reference instead of a whole new diagnostic. + const diags = new Map(); + + // Functions and subroutines clash each other. + // Build map of all suroutines and functions. + /** Combine {@link ScopeItemCapability} maps by loading items from b into a. */ + const combineNames = (a: Map, b: Map | undefined) => { + b?.forEach((bItems, name) => a.set(name, [a.get(name) ?? [], bItems].flat())); + return a; + } + + const names = new Map(); + combineNames(names, this.types); + combineNames(names, this.modules); + combineNames(names, this.functions); + combineNames(names, this.subroutines); + + names.forEach((items, name) => { + if (items.length === 1) { + return; + } + + // Create diagnostic + const item = items[0]; + const diagnostic = this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, item.name); + if (!diagnostic) { + return; + } + + // Add references + for (let i = 1; i < items.length; i++) { + this.addDiagnosticReference(diagnostic, items[i]); + } + + // Track the diagnostic to avoid multiples. + diags.set(name, diagnostic); + }); + + // Properties clash with properties of same type. Loop through each and check against names. + Object.entries(this.properties ?? {}).forEach(([_, properties]) => { + if (!properties) { + return; + } + + properties.forEach((items, name) => { + // Check if we need to raise a diagnostic. + const combined = [items, names.get(name) ?? []].flat(); + if (combined.length === 1) { + return; + } + + // Get or create diagnostic. + let startIndex = 0; + let diagnostic = diags.get(name); + if (!diagnostic) { + const item = items[0]; + diagnostic = this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, item.name); + if (!diagnostic) { + return; + } + startIndex = 1; + } + + // Add references. + for (let i = startIndex; i < items.length; i++) { + this.addDiagnosticReference(diagnostic, items[i]); + } + }) + }) + } + + private addDiagnosticReference(diagnostic: BaseDiagnostic | undefined, item: ScopeItemCapability): void { + const context = item.element?.context; + if (!context || !diagnostic) { + return; + } + + diagnostic.addRelatedInformation({ + message: "Related Information", + location: { + uri: context.document.uri, + range: context.range + } + }) + } + + private resolveShadowedDeclaration(item: ScopeItemCapability | undefined): void { + if (item) { + const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic); + this.addDiagnosticReference(diagnostic, item) + } + } + + private resolveShadowedDeclarations() { + // Get the parent of the scope where this element is registered. + const parent = this.parent?.parent; + if (!parent) { + return; + } + + // All declaration types check for modules. + this.resolveShadowedDeclaration(parent.findModule(this.name)); + this.resolveShadowedDeclaration(parent.findFunction(this.name)); + this.resolveShadowedDeclaration(parent.findSubroutine(this.name)); + this.resolveShadowedDeclaration(parent.findModule(this.name)); + + // Properties care about everything except properties that + // aren't the same type. Everything else cares about everything. + + // ToDo: + // Variables are registered as props so should also squash their + // get/set/let diagnostics into one single diagnostic. + + // Check get properties. + if (!!(this.assignmentType & AssignmentType.GET)) { + this.resolveShadowedDeclaration(parent.findPropertyGetter(this.name)); + } + + // Check let properties. + if (!!(this.assignmentType & AssignmentType.LET)) { + this.resolveShadowedDeclaration(parent.findPropertyLetter(this.name)); + } + + // Check set properties. + if (!!(this.assignmentType & AssignmentType.SET)) { + this.resolveShadowedDeclaration(parent.findPropertySetter(this.name)); + } + } + + private resolveLinks() { + /** + * Call Foo(bar) + * ^^^ NONE + * ^^^ GET + * + * Let foo = bar + * ^^^ LET + * ^^^ GET + * + * Set foo = bar + * ^^^ SET + * ^^^ GET + */ + + // Handle calls that aren't assignments. + if (this.assignmentType === AssignmentType.CALL) { + this.linkThisToItem(this.findFunction(this.name)); + this.linkThisToItem(this.findSubroutine(this.name)); + return; + } + + // Handle get/set/let relationships. + if (this.assignmentType & AssignmentType.GET) { + this.linkThisToItem(this.findFunction(this.name)); + this.linkThisToItem(this.findPropertyGetter(this.name)); + return; + } + + if (this.assignmentType & AssignmentType.LET) { + this.linkThisToItem(this.findPropertyLetter(this.name)); + return; + } + + if (this.assignmentType & AssignmentType.SET) { + this.linkThisToItem(this.findPropertySetter(this.name)); + return; + } + } + + private linkThisToItem(linkItem?: ScopeItemCapability): void { + if (!linkItem) { + return; + } + + this.link = linkItem; + linkItem.backLinks ??= []; + linkItem.backLinks.push(this); + } + + private removeBacklink(backlinkedItem: ScopeItemCapability): void { + if (!this.backLinks) { + return; + } + + const keep = this.backLinks.filter(x => x !== backlinkedItem); + this.backLinks = keep.length === 0 ? undefined : keep; + } + + /** Returns the module this scope item falls under */ + get module(): ScopeItemCapability | undefined { + if (this.type == ItemType.MODULE) { + return this; + } + return this.parent?.module; + } + + /** Returns the project this scope item falls under */ + get project(): ScopeItemCapability | undefined { + if (this.type == ItemType.PROJECT) { + return this; + } + return this.parent?.project; + } + + get name(): string { + return this.explicitSetName + ?? this.element?.identifierCapability?.name + ?? 'Unknown'; + } + + findModule(name: string): ScopeItemCapability | undefined { + return this.modules?.get(name)?.[0] + ?? this.parent?.findModule(name); + } + + findFunction(name: string): ScopeItemCapability | undefined { + return this.functions?.get(name)?.[0] + ?? this.parent?.findFunction(name); + } + + findSubroutine(name: string): ScopeItemCapability | undefined { + return this.subroutines?.get(name)?.[0] + ?? this.parent?.findSubroutine(name); + } + + findPropertyGetter(name: string): ScopeItemCapability | undefined { + return this.properties?.getters?.get(name)?.[0] + ?? this.parent?.findPropertyGetter(name); + } + + findPropertyLetter(name: string): ScopeItemCapability | undefined { + return this.properties?.letters?.get(name)?.[0] + ?? this.parent?.findPropertyLetter(name); + } + + findPropertySetter(name: string): ScopeItemCapability | undefined { + return this.properties?.setters?.get(name)?.[0] + ?? this.parent?.findPropertySetter(name); + } + + /** + * Registers a scope and returns the new current scope. + * @param item The scope item to register. + * @returns The current scope. + */ + registerScopeItem(item: ScopeItemCapability): ScopeItemCapability { + // ToDo: Get the parent based on visibility. + // Public scoped elements should get the project. + // Check pub/priv declares in same document treated as duplicate instead of shadowed. + + // Set the parent for the item. + item.parent = this; + + let ancestor: ScopeItemCapability | undefined = this; + let ancestorLevel = 0; + while (!!ancestor) { + ancestorLevel += 1; + ancestor = ancestor.parent; + } + Services.logger.debug(`Registering [${ItemType[item.type]}] ${item.name}`, ancestorLevel); + + // Reference types are not declarations. + if (item.type === ItemType.REFERENCE) { + this.references ??= new Map(); + this.addItem(this.references, item); + return this; + } + + // Register functions. + if (item.type === ItemType.FUNCTION) { + this.functions ??= new Map(); + this.addItem(this.functions, item); + return item; + } + + // Register subroutine. + if (item.type === ItemType.SUBROUTINE) { + this.subroutines ??= new Map(); + this.addItem(this.subroutines, item); + return item; + } + + // Register enum or type. + if (item.type === ItemType.TYPE) { + this.types ??= new Map(); + this.addItem(this.types, item); + return item; + } + + // Register properties and variables. + if (item.type === ItemType.PROPERTY || item.type === ItemType.VARIABLE) { + this.properties ??= {}; + if (item.assignmentType & AssignmentType.GET) { + this.properties.getters ??= new Map(); + this.addItem(this.properties.getters, item); + } + if (item.assignmentType & AssignmentType.LET) { + this.properties.letters ??= new Map(); + this.addItem(this.properties.letters, item); + } + if (item.assignmentType & AssignmentType.SET) { + this.properties.setters ??= new Map(); + this.addItem(this.properties.setters, item); + } + return item.type === ItemType.PROPERTY ? item : this; + } + + // Handle module registration + if (item.type === ItemType.MODULE) { + this.modules ??= new Map(); + this.addItem(this.modules, item); + return item; + } + + // Handle container types that aren't registered. + return item; + } + + // Would be relatively simple to also do this via a "dirty" flag. + /** Removes all elements with references to the document uri. */ + invalidate(uri: string): void { + const unlink = (item: ScopeItemCapability): void => { + // Remove backlink from linked item. + item.link?.removeBacklink(item); + // Remove link from any backlinked items. + item.backLinks?.forEach(node => node.link = undefined); + } + + const scan = (map: Map | undefined, uri: string) => { + if (map === undefined) { + return; + } + + const keys = Array.from(map.keys()); + keys.forEach(key => { + const items = map.get(key)! + const keep = items.filter(item => item.element?.context.document.uri !== uri); + const remove = items.filter(item => item.element?.context.document.uri === uri); + + // Sever links for items to be removed. + remove.forEach(x => unlink(x)); + + // Update the map. + if (keep.length === 0) { + map.delete(key); + } else { + map.set(key, keep); + } + }); + }; + + // Invalidate and unlink items. + scan(this.types, uri); + scan(this.modules, uri); + scan(this.functions, uri); + scan(this.subroutines, uri); + if (this.properties !== undefined) { + scan(this.properties.getters, uri); + scan(this.properties.letters, uri); + scan(this.properties.setters, uri); + } + scan(this.references, uri); + + // Call invalidate on children. + this.types?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.modules?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.functions?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.subroutines?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.properties?.getters?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.properties?.letters?.forEach(items => items.forEach(item => item.invalidate(uri))); + this.properties?.setters?.forEach(items => items.forEach(item => item.invalidate(uri))); + } + + private addItem(target: Map, item: ScopeItemCapability): void { + const items = target.get(item.name) ?? []; + items.push(item); + target.set(item.name, items); + } + + /** + * Generates and pushes a diagnostic to the underlying element on the scope item. + * No diagnostic is created unless we have both a range and the capability on the element. + * Range is automatically injected into the constructor but arguments can't be verified at compile time, so it's on you to check. + * @param ctor The diagnostic we're creating. + * @param item The scope item to get the range from. + * @param args Any additional constructor args (don't provide the range). + * @returns A diagnostic if we have a range and capability. + */ + private pushDiagnostic(ctor: new (...args: any[]) => T, item?: ScopeItemCapability, ...args: ConstructorParameters): T | undefined { + const range = (item ?? this).element?.identifierCapability?.range; + const diagnostics = (item ?? this).element?.diagnosticCapability?.diagnostics; + if (range && diagnostics) { + const diagnostic = new ctor(...[range, args].flat()); + diagnostics.push(diagnostic); + return diagnostic; + } + } } \ No newline at end of file diff --git a/server/src/injection/interface.ts b/server/src/injection/interface.ts index e48cea9..5e11959 100644 --- a/server/src/injection/interface.ts +++ b/server/src/injection/interface.ts @@ -2,7 +2,6 @@ import { LanguageServerConfiguration } from '../server'; import { BaseProjectDocument } from '../project/document'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { VbaFmtListener } from '../project/parser/vbaListener'; -import { NamespaceManager } from '../project/scope'; import { CancellationToken } from 'vscode-languageserver'; export interface Logger { @@ -41,5 +40,4 @@ export interface IWorkspace { parseDocument(projectDocument: BaseProjectDocument): Promise; openDocument(document: TextDocument): void; closeDocument(document: TextDocument): void; - namespaceManager: NamespaceManager; } \ No newline at end of file diff --git a/server/src/injection/services.ts b/server/src/injection/services.ts index e6b3608..7397670 100644 --- a/server/src/injection/services.ts +++ b/server/src/injection/services.ts @@ -2,6 +2,7 @@ import { container, InjectionToken } from 'tsyringe'; import { Logger, IWorkspace, ILanguageServer } from './interface'; import { LspLogger } from '../utils/logger'; import { _Connection, createConnection, ProposedFeatures } from 'vscode-languageserver/node'; +import { ScopeItemCapability } from '../capabilities/capabilities'; export class Services { @@ -10,6 +11,10 @@ export class Services { container.registerInstance("_Connection", createConnection(ProposedFeatures.all)); } + static registerProjectScope(scope: ScopeItemCapability): void { + container.registerInstance("ProjectScope", scope); + } + static registerServer(server: ILanguageServer): void { container.registerInstance("ILanguageServer", server); } @@ -30,6 +35,10 @@ export class Services { return container.resolve("ILanguageServer"); } + static get projectScope(): ScopeItemCapability { + return container.resolve("ProjectScope"); + } + static get workspace(): IWorkspace { return container.resolve("IWorkspace"); } diff --git a/server/src/project/document.ts b/server/src/project/document.ts index 9860365..897337e 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -1,6 +1,6 @@ // Core import { Range, TextDocument } from 'vscode-languageserver-textdocument'; -import { CancellationToken, Diagnostic, DocumentDiagnosticReport, DocumentDiagnosticReportKind, SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { CancellationToken, Diagnostic, SymbolInformation, SymbolKind } from 'vscode-languageserver'; // Antlr import { ParserRuleContext } from 'antlr4ng'; @@ -10,16 +10,19 @@ import { Dictionary } from '../utils/helpers'; import { SyntaxParser } from './parser/vbaParser'; import { FoldingRange } from '../capabilities/folding'; import { SemanticTokensManager } from '../capabilities/semanticTokens'; -import { BaseContextSyntaxElement, +import { + BaseContextSyntaxElement, BaseSyntaxElement, DeclarableElement, HasDiagnosticCapability, HasFoldingRangeCapability, + HasScopeItemCapability, HasSemanticTokenCapability, HasSymbolInformationCapability } from './elements/base'; -import { PropertyDeclarationElement, +import { + PropertyDeclarationElement, PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement @@ -27,7 +30,7 @@ import { PropertyDeclarationElement, import { VbaFmtListener } from './parser/vbaListener'; import { Services } from '../injection/services'; import { IWorkspace } from '../injection/interface'; -import { VbaFmtParser } from './parser/vbaAntlr'; +import { ScopeItemCapability } from '../capabilities/capabilities'; // TODO --------------------------------------------- @@ -56,9 +59,10 @@ export abstract class BaseProjectDocument { protected symbolInformations: SymbolInformation[] = []; protected unhandledNamedElements: [] = []; protected isClosed = false; - + protected currentScope: ScopeItemCapability + abstract symbolKind: SymbolKind - + protected _isBusy = true; get isBusy() { return this._isBusy; } @@ -79,7 +83,7 @@ export abstract class BaseProjectDocument { // 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); @@ -108,6 +112,7 @@ export abstract class BaseProjectDocument { this.workspace = Services.workspace; this.version = document.version; this.name = name; + this.currentScope = Services.projectScope; } static create(document: TextDocument): BaseProjectDocument { @@ -179,13 +184,14 @@ export abstract class BaseProjectDocument { // Don't parse oversize documents. if (await this.isOversize) { Services.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`); - Services.logger.warn(`Syntax parsing has been disabled to prevent crashing.`); + Services.logger.warn(`Syntax parsing has been disabled to prevent crashing.`); this._isBusy = false; return; } // Parse the document. await (new SyntaxParser(Services.logger)).parseAsync(token, this); + this.currentScope.build(); // Evaluate the diagnostics. this.diagnostics = this.hasDiagnosticElements @@ -199,7 +205,7 @@ export abstract class BaseProjectDocument { // Don't parse oversize documents. if (await this.isOversize) { Services.logger.debug(`Document oversize: ${this.textDocument.lineCount} lines.`); - Services.logger.warn(`Syntax parsing has been disabled to prevent crashing.`); + Services.logger.warn(`Syntax parsing has been disabled to prevent crashing.`); return; } @@ -216,6 +222,7 @@ export abstract class BaseProjectDocument { if (!!element.foldingRangeCapability) this.registerFoldableElement(element as HasFoldingRangeCapability); if (!!element.semanticTokenCapability) this.registerSemanticToken(element as HasSemanticTokenCapability); if (!!element.symbolInformationCapability) this.registerSymbolInformation(element as HasSymbolInformationCapability); + if (!!element.scopeItemCapability) this.registerScopeItem(element as HasScopeItemCapability); return this; } @@ -226,21 +233,6 @@ export abstract class BaseProjectDocument { return this; } - registerNamedElementDeclaration(element: DeclarableElement) { - this.workspace.namespaceManager.addNameItem(element); - return this; - } - - registerNamespaceElement(element: DeclarableElement) { - this.workspace.namespaceManager.addNamespace(element); - return this; - } - - deregisterNamespaceElement() { - this.workspace.namespaceManager.popNamespace(); - return this; - } - registerDiagnosticElement(element: HasDiagnosticCapability) { this.hasDiagnosticElements.push(element); return this; @@ -276,6 +268,15 @@ export abstract class BaseProjectDocument { return this; } + registerScopeItem = (element: HasScopeItemCapability) => + this.currentScope = this.currentScope.registerScopeItem(element.scopeItemCapability); + + exitContext = (ctx: ParserRuleContext) => { + if (ctx === this.currentScope.element?.context.rule) { + const parent = this.currentScope.parent; + this.currentScope = parent ?? this.currentScope; + } + } private subtractTextFromRanges(ranges: Range[]): string { const text = this.textDocument.getText(); diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index f3da4c9..28d2afb 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -10,6 +10,7 @@ import { DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, + ScopeItemCapability, SemanticTokenCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; @@ -25,6 +26,7 @@ export abstract class BaseSyntaxElement { foldingRangeCapability?: FoldingRangeCapability; semanticTokenCapability?: SemanticTokenCapability; symbolInformationCapability?: SymbolInformationCapability; + scopeItemCapability?: ScopeItemCapability; get isPublic(): boolean { return false; } @@ -141,6 +143,10 @@ export interface HasSymbolInformationCapability extends IsIdentifiable { symbolInformationCapability: SymbolInformationCapability } +export interface HasScopeItemCapability { + scopeItemCapability: ScopeItemCapability; +} + export interface HasFoldingRangeCapability { foldingRangeCapability: FoldingRangeCapability; diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index 095ba27..c48c46a 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -17,7 +17,7 @@ import { // Project import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, HasDiagnosticCapability } from './base'; -import { DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; +import { DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; import { DuplicateAttributeDiagnostic, IgnoredAttributeDiagnostic, MissingAttributeDiagnostic, MissingOptionExplicitDiagnostic } from '../../capabilities/diagnostics'; @@ -40,6 +40,7 @@ abstract class BaseModuleElement extends BaseIdenti this.settings = documentSettings; // this.foldingRangeCapability = new FoldingRangeCapability(this); this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.MODULE); } // Helpers diff --git a/server/src/project/elements/procedure.ts b/server/src/project/elements/procedure.ts index c6b8d58..108a0d5 100644 --- a/server/src/project/elements/procedure.ts +++ b/server/src/project/elements/procedure.ts @@ -14,7 +14,7 @@ import { // Project import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSymbolInformationCapability } from './base'; -import { DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; +import { AssignmentType, DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; abstract class BaseProcedureElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability { @@ -29,6 +29,9 @@ abstract class BaseProcedureElement extends BaseCon this.foldingRangeCapability = new FoldingRangeCapability(this); this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); } + + // ToDo: + // Add a diagnostic when the attribute name doesn't match the method name. } @@ -46,6 +49,7 @@ export class SubDeclarationElement extends BaseProcedureElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability, HasSemanticTokenCapability { @@ -37,6 +38,7 @@ abstract class BaseTypeDeclarationElement extends B this.diagnosticCapability = new DiagnosticCapability(this); this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); this.semanticTokenCapability = new SemanticTokenCapability(this, tokenType, tokenModifiers ?? []); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.TYPE); } } @@ -53,6 +55,24 @@ export class EnumDeclarationElement extends BaseTypeDeclarationElement { + identifierCapability: IdentifierCapability; + + constructor(ctx: EnumMemberContext, doc: TextDocument) { + super(ctx, doc); + this.diagnosticCapability = new DiagnosticCapability(this); + this.identifierCapability = new IdentifierCapability({ + element: this, + getNameContext: () => ctx.untypedName() + }); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE, AssignmentType.GET); } } @@ -96,6 +116,7 @@ export class DeclarationStatementElement exte super(ctx, doc); this._isPublic = isPublic; this.isConstant = isConstant; + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE); } static create(ctx: CombinedVariableContext, doc: TextDocument) { @@ -122,6 +143,7 @@ export class VariableDeclarationElement extends BaseContextSyntaxElement ctx.ambiguousIdentifier()}); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE); } } diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index 92431c2..fc85c03 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -9,28 +9,29 @@ import { vbafmtListener } from '../../antlr/out/vbafmtListener'; import { CompilerIfBlockContext } from '../../antlr/out/vbapreParser'; import { AnyOperatorContext, - ClassModuleContext, - EnumDeclarationContext, - FunctionDeclarationContext, - GlobalVariableDeclarationContext, - IfStatementContext, - IgnoredClassAttrContext, - IgnoredProceduralAttrContext, - PrivateConstDeclarationContext, - PrivateTypeDeclarationContext, - PrivateVariableDeclarationContext, - ProceduralModuleContext, - ProcedureDeclarationContext, - PropertyGetDeclarationContext, - PropertySetDeclarationContext, - PublicConstDeclarationContext, - PublicTypeDeclarationContext, - PublicVariableDeclarationContext, - SubroutineDeclarationContext, - TypeSuffixContext, - UdtDeclarationContext, - UnexpectedEndOfLineContext, - WhileStatementContext + ClassModuleContext, + EnumDeclarationContext, + EnumMemberContext, + FunctionDeclarationContext, + GlobalVariableDeclarationContext, + IfStatementContext, + IgnoredClassAttrContext, + IgnoredProceduralAttrContext, + PrivateConstDeclarationContext, + PrivateTypeDeclarationContext, + PrivateVariableDeclarationContext, + ProceduralModuleContext, + ProcedureDeclarationContext, + PropertyGetDeclarationContext, + PropertySetDeclarationContext, + PublicConstDeclarationContext, + PublicTypeDeclarationContext, + PublicVariableDeclarationContext, + SubroutineDeclarationContext, + TypeSuffixContext, + UdtDeclarationContext, + UnexpectedEndOfLineContext, + WhileStatementContext } from '../../antlr/out/vbaParser'; import { AttributeStatementContext, @@ -58,7 +59,7 @@ import { UnexpectedEndOfLineElement } from '../elements/utils'; import { DuplicateOperatorElement, IfElseBlock as IfStatementElement, WhileLoopElement } from '../elements/flow'; import { VbaClassDocument, VbaModuleDocument } from '../document'; import { ClassElement, ModuleElement, ModuleIgnoredAttributeElement } from '../elements/module'; -import { DeclarationStatementElement, EnumDeclarationElement, TypeDeclarationElement, TypeSuffixElement } from '../elements/typing'; +import { DeclarationStatementElement, EnumDeclarationElement, EnumMemberDeclarationElement, TypeDeclarationElement, TypeSuffixElement } from '../elements/typing'; import { FunctionDeclarationElement, PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement, SubDeclarationElement } from '../elements/procedure'; import { ExtensionConfiguration } from '../workspace'; import { Services } from '../../injection/services'; @@ -106,6 +107,10 @@ export class VbaListener extends vbaListener { this.documentSettings = await Services.server.clientConfiguration; } + exitEveryRule(node: ParserRuleContext): void { + this.document.exitContext(node); + } + enterAnyOperator = (ctx: AnyOperatorContext) => { const element = new DuplicateOperatorElement(ctx, this.document.textDocument); this.document.registerElement(element); @@ -113,22 +118,19 @@ export class VbaListener extends vbaListener { enterEnumDeclaration = (ctx: EnumDeclarationContext) => { const element = new EnumDeclarationElement(ctx, this.document.textDocument, this.isAfterMethodDeclaration); - this.document.registerElement(element) - .registerNamespaceElement(element); + this.document.registerElement(element); }; - exitEnumDeclaration = (_: EnumDeclarationContext) => - this.document.deregisterNamespaceElement(); + enterEnumMember = (ctx: EnumMemberContext) => { + const element = new EnumMemberDeclarationElement(ctx, this.document.textDocument); + this.document.registerElement(element); + } enterClassModule = (ctx: ClassModuleContext) => { const element = new ClassElement(ctx, this.document.textDocument, this.documentSettings ?? { doWarnOptionExplicitMissing: true }); - this.document.registerElement(element) - .registerNamespaceElement(element) + this.document.registerElement(element); }; - exitClassModule = (_: ClassModuleContext) => - this.document.deregisterNamespaceElement(); - enterIfStatement = (ctx: IfStatementContext) => this.document.registerElement(new IfStatementElement(ctx, this.document.textDocument)); @@ -140,31 +142,24 @@ export class VbaListener extends vbaListener { enterProceduralModule = (ctx: ProceduralModuleContext) => { const element = new ModuleElement(ctx, this.document.textDocument, this.documentSettings ?? { doWarnOptionExplicitMissing: true }); - this.document.registerElement(element) - .registerNamespaceElement(element) + this.document.registerElement(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); + this.document.registerElement(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); + this.document.registerElement(element); }; enterSubroutineDeclaration = (ctx: SubroutineDeclarationContext) => { @@ -181,16 +176,12 @@ export class VbaListener extends vbaListener { 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); + this.document.registerElement(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); @@ -263,9 +254,9 @@ class PreIfElseBlockElement { get levelOnExit2(): number { const maxs = this.blocks.map(x => Math.max(...x.levels)); return Math.min(...maxs); - } + } - constructor (ctx: PreIfElseBlockContext, levelBeforeEnter: number) { + constructor(ctx: PreIfElseBlockContext, levelBeforeEnter: number) { this.ctx = ctx; this.levelBeforeEnter = levelBeforeEnter; } @@ -353,7 +344,7 @@ export class VbaFmtListener extends vbafmtListener { // Don't indent if we're already indented. if (continuedElement === activeElement) return; - + // Flag this element as continued / indented. this.continuedElements.push(activeElement); @@ -393,10 +384,10 @@ export class VbaFmtListener extends vbafmtListener { this.activeElements.push(ctx); enterBlock = (ctx: BlockContext) => - this.indentOnEnter({context: ctx}); + this.indentOnEnter({ context: ctx }); exitBlock = (ctx: BlockContext) => - this.outdentAfterExit({context: ctx}); + this.outdentAfterExit({ context: ctx }); enterCaseDefaultStatement = (ctx: CaseDefaultStatementContext) => { this.outdentCaseStatementBlock(ctx); @@ -409,17 +400,17 @@ export class VbaFmtListener extends vbafmtListener { } enterClassHeaderBlock = (ctx: ClassHeaderBlockContext) => - this.indentOnEnter({context: ctx, offset: 1}); + this.indentOnEnter({ context: ctx, offset: 1 }); exitClassHeaderBlock = (ctx: ClassHeaderBlockContext) => - this.outdentAfterExit({context: ctx, offset: -1}); + this.outdentAfterExit({ context: ctx, offset: -1 }); enterIndentAfterElement = (ctx: IndentAfterElementContext) => this.activeElements.push(ctx); enterDocumentElement = (ctx: DocumentElementContext) => this.activeElements.push(ctx); - + exitIndentAfterElement = (ctx: IndentAfterElementContext) => { const offset = ctx.endsWithLineEnding ? 0 : 1 const line = this.getCtxRange(ctx).end.line + offset; @@ -437,7 +428,7 @@ export class VbaFmtListener extends vbafmtListener { this.indentationKeys[line + 1] = this.getIndent(line); this.indentationKeys[line] = 0; } - + enterMethodParameters = (ctx: MethodParametersContext) => this.activeElements.push(ctx); @@ -472,7 +463,7 @@ export class VbaFmtListener extends vbafmtListener { text: `${this.rangeText(ctx)}` }); } - + enterPreBlock = (ctx: PreBlockContext) => { // Get and ensure we have a preCompilerElement const preCompilerElement = this.preCompilerElements.at(-1); @@ -517,7 +508,7 @@ export class VbaFmtListener extends vbafmtListener { }); enterSelectCaseOpen = (_: SelectCaseOpenContext) => - this.selectCaseTrackers.push({statements: []}); + this.selectCaseTrackers.push({ statements: [] }); exitSelectCaseClose = (ctx: SelectCaseCloseContext) => { // Pop the tracker as it's no longer needed after this. @@ -527,7 +518,7 @@ export class VbaFmtListener extends vbafmtListener { // Get the previous case statement and outdent if it had a line ending. const caseElement = selectCaseElement.statements.at(-1); if (!!caseElement && caseElement.endsWithLineEnding) { - this.outdentAfterExit({context: ctx}); + this.outdentAfterExit({ context: ctx }); } } @@ -678,7 +669,7 @@ export class VbaFmtListener extends vbafmtListener { // If current indent is odd, assume we're just inside a #[else]if block. // -1 to offset an indent, 0 to leave it alone. const offset = (currentIndent.isOdd() && newOffset.isEven()) ? 1 : 0; - + // Switch the direction if we have an outdent value. const offsetToggle = offset > 0 ? -1 : 1; return offset * offsetToggle; diff --git a/server/src/project/scope.ts b/server/src/project/scope.ts deleted file mode 100644 index b7bad67..0000000 --- a/server/src/project/scope.ts +++ /dev/null @@ -1,64 +0,0 @@ -// 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.identifierCapability.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 14d4865..bf0eeeb 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -24,7 +24,6 @@ import { import { BaseProjectDocument } from './document'; import { hasWorkspaceConfigurationCapability } from '../capabilities/workspaceFolder'; import { sleep } from '../utils/helpers'; -import { NamespaceManager } from './scope'; import { ParseCancellationException } from 'antlr4ng'; import { getFormattingEdits } from './formatter'; import { VbaFmtListener } from './parser/vbaListener'; @@ -32,6 +31,7 @@ import { returnDefaultOnCancelClientRequest } from '../utils/wrappers'; import { inject, injectable } from 'tsyringe'; import { Logger, ILanguageServer, IWorkspace } from '../injection/interface'; import { Services } from '../injection/services'; +import { ItemType, ScopeItemCapability } from '../capabilities/capabilities'; export interface ExtensionConfiguration { maxDocumentLines: number; @@ -53,7 +53,6 @@ export interface ExtensionConfiguration { @injectable() export class Workspace implements IWorkspace { private events?: WorkspaceEvents; - private nsManager: NamespaceManager = new NamespaceManager(); private documents: BaseProjectDocument[] = []; private parseCancellationTokenSource?: CancellationTokenSource; @@ -66,11 +65,11 @@ export class Workspace implements IWorkspace { // readonly connection: _Connection; logger: Logger; - + get hasConfigurationCapability() { return this._hasConfigurationCapability; } - + get extensionConfiguration() { return (async () => { if (!this._extensionConfiguration && this.hasConfigurationCapability) { @@ -84,17 +83,19 @@ export class Workspace implements IWorkspace { return this._activeDocument; } - get namespaceManager() { - return this.nsManager; - } - constructor( @inject("_Connection") public readonly connection: _Connection, @inject("ILanguageServer") private server: ILanguageServer) { - + this.logger = Services.logger; this.events = new WorkspaceEvents(this.textDocuments, this.projectDocuments); this._hasConfigurationCapability = hasWorkspaceConfigurationCapability(this.server); + + // Configure scopes + const languageScope = new ScopeItemCapability(undefined, ItemType.VBA); + const applicationScope = new ScopeItemCapability(undefined, ItemType.APPLICATION, undefined, languageScope); + const projectScope = new ScopeItemCapability(undefined, ItemType.PROJECT, undefined, applicationScope); + Services.registerProjectScope(projectScope); } activateDocument(document?: BaseProjectDocument) { @@ -112,7 +113,7 @@ export class Workspace implements IWorkspace { this.logger.error('No active document.'); return; } - + // Exceptions thrown by the parser should be ignored. try { await this.activeDocument.parseAsync(this.parseCancellationTokenSource.token); @@ -226,7 +227,7 @@ class WorkspaceEvents { this.documents.listen(connection); } - private async getParsedProjectDocument(uri: string, version: number, token: CancellationToken): Promise { + private async getParsedProjectDocument(uri: string, version: number, token: CancellationToken): Promise { // Handle token cancellation. if (token.isCancellationRequested) return undefined; @@ -260,7 +261,7 @@ class WorkspaceEvents { private initialiseConnectionEvents(connection: _Connection) { const cancellableOnDocSymbol = returnDefaultOnCancelClientRequest( (p: DocumentSymbolParams, t) => this.onDocumentSymbolAsync(p, t), [], 'Document Symbols'); - + const cancellableOnFoldingRanges = returnDefaultOnCancelClientRequest( (p: FoldingRangeParams, t) => this.onFoldingRangesAsync(p, t), [], 'Folding Range'); @@ -272,7 +273,7 @@ class WorkspaceEvents { connection.onDocumentSymbol(async (params, token) => await cancellableOnDocSymbol(params, token)); connection.onHover(params => this.onHover(params)); connection.onDocumentFormatting(async (params, token) => await this.onDocumentFormatting(params, token)); - connection.onDidCloseTextDocument(params => {Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1);}); + connection.onDidCloseTextDocument(params => { Services.logger.debug('[event] onDidCloseTextDocument'); Services.logger.debug(JSON.stringify(params), 1); }); if (hasWorkspaceConfigurationCapability(Services.server)) { connection.onFoldingRanges(async (params, token) => await cancellableOnFoldingRanges(params, token)); @@ -334,7 +335,8 @@ class WorkspaceEvents { } private async onFoldingRangesAsync(params: FoldingRangeParams, token: CancellationToken): Promise { - Services.logger.debug('[Event] onFoldingRanges') + const logger = Services.logger; + logger.debug('[Event] onFoldingRanges') let document: BaseProjectDocument | undefined; try { document = await this.getParsedProjectDocument(params.textDocument.uri, 0, token); @@ -346,14 +348,15 @@ class WorkspaceEvents { } const result = document?.languageServerFoldingRanges(); for (const foldingRange of result ?? []) { - Services.logger.debug(`${JSON.stringify(foldingRange.range)} '${foldingRange.openWord}..${foldingRange.closeWord}'`, 1); + logger.debug(`${JSON.stringify(foldingRange.range)} '${foldingRange.openWord}..${foldingRange.closeWord}'`, 1); } return result?.map(x => x.range) ?? []; } private onHover(params: HoverParams): Hover { - Services.logger.debug('[event] onHover'); - Services.logger.debug(JSON.stringify(params), 1); + const logger = Services.logger; + logger.debug('[event] onHover'); + logger.debug(JSON.stringify(params), 1); return { contents: '' }; } @@ -372,15 +375,16 @@ class WorkspaceEvents { } private async onDocumentFormatting(params: DocumentFormattingParams, token: CancellationToken): Promise { - Services.logger.debug('[event] onDocumentFormatting'); - Services.logger.debug(JSON.stringify(params), 1); + const logger = Services.logger; + logger.debug('[event] onDocumentFormatting'); + logger.debug(JSON.stringify(params), 1); const doc = this.documents.get(params.textDocument.uri); if (!doc) return []; try { const parseResult = await Services.workspace.formatParseDocument(doc, token); return parseResult ? getFormattingEdits(doc, parseResult) : []; } catch { - Services.logger.debug('caught workspace'); + logger.debug('caught workspace'); return []; } @@ -393,10 +397,11 @@ class WorkspaceEvents { * @param document The document being opened. */ onDidOpen(document: TextDocument) { - Services.logger.debug('[event] onDidOpen'); - Services.logger.debug(`uri: ${document.uri}`, 1); - Services.logger.debug(`languageId: ${document.languageId}`, 1); - Services.logger.debug(`version: ${document.version}`, 1); + const logger = Services.logger; + logger.debug('[event] onDidOpen'); + logger.debug(`uri: ${document.uri}`, 1); + logger.debug(`languageId: ${document.languageId}`, 1); + logger.debug(`version: ${document.version}`, 1); const projectDocument = this.projectDocuments.get(document.uri); if (projectDocument) { Services.workspace.openDocument(document); @@ -408,21 +413,23 @@ class WorkspaceEvents { * @param document The document that was changed. */ onDidChangeContent(document: TextDocument): void { - Services.logger.debug('[event] onDidChangeContentAsync'); - Services.logger.debug(`uri: ${document.uri}`, 1); - Services.logger.debug(`languageId: ${document.languageId}`, 1); - Services.logger.debug(`version: ${document.version}`, 1); + const logger = Services.logger; + logger.debug('[event] onDidChangeContentAsync'); + logger.debug(`uri: ${document.uri}`, 1); + logger.debug(`languageId: ${document.languageId}`, 1); + logger.debug(`version: ${document.version}`, 1); // If the event is fired for the same version of the document, don't reparse. const existingDocument = this.projectDocuments.get(document.uri); if ((existingDocument?.version ?? -1) >= document.version) { - Services.logger.debug('Document already parsed.'); + logger.debug('Document already parsed.'); return; } // The document is new or a new version that we should parse. const projectDocument = BaseProjectDocument.create(document); this.projectDocuments.set(document.uri, projectDocument); + Services.projectScope.invalidate(document.uri); Services.workspace.parseDocument(projectDocument); } @@ -431,10 +438,11 @@ class WorkspaceEvents { * @param document The document being closed. */ onDidClose(document: TextDocument) { - Services.logger.debug('[event] onDidClose'); - Services.logger.debug(`uri: ${document.uri}`, 1); - Services.logger.debug(`languageId: ${document.languageId}`, 1); - Services.logger.debug(`version: ${document.version}`, 1); + const logger = Services.logger; + logger.debug('[event] onDidClose'); + logger.debug(`uri: ${document.uri}`, 1); + logger.debug(`languageId: ${document.languageId}`, 1); + logger.debug(`version: ${document.version}`, 1); const projectDocument = this.projectDocuments.get(document.uri); if (projectDocument) { Services.workspace.closeDocument(document); From 1312569dcac11baddd5f6d9b488986a38116b839 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:42:03 +0800 Subject: [PATCH 05/29] Update ToDo notes --- server/src/capabilities/capabilities.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 45a27f1..b610fe4 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -229,13 +229,13 @@ export class ScopeItemCapability { this.resolveLinks(); if (!this.link) { // TODO: - // Check Option Explicit for variables. - // Should always error for method calls. - - // TODO: - // Whether a diagnostic is added for missing - // declarations or not, the first instance should - // be considered the declaration for linking. + // References to variables should get a diagnostic if they aren't declared. + // -- No option explicit: gets a hint with code action to declare. + // -- Option explicit: gets an error with code action to declare. + // -- Subsequent explicit declaration should raise duplicate declaration (current bahaviour). + // References to function or sub calls should raise an error if they aren't declared. + // -- Must always throw even when option explicit not present. + // -- Nothing required on first reference as declaration may come later. const diagnosticType = this.assignmentType & AssignmentType.CALL ? SubOrFunctionNotDefinedDiagnostic : VariableNotDefinedDiagnostic; From 19186d9975cf7d7b553a34bdac40c8114d6f0034 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:46:08 +0800 Subject: [PATCH 06/29] Add findType method --- server/src/capabilities/capabilities.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index b610fe4..99173c3 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -366,10 +366,10 @@ export class ScopeItemCapability { } // All declaration types check for modules. + this.resolveShadowedDeclaration(parent.findType(this.name)); this.resolveShadowedDeclaration(parent.findModule(this.name)); this.resolveShadowedDeclaration(parent.findFunction(this.name)); this.resolveShadowedDeclaration(parent.findSubroutine(this.name)); - this.resolveShadowedDeclaration(parent.findModule(this.name)); // Properties care about everything except properties that // aren't the same type. Everything else cares about everything. @@ -475,6 +475,11 @@ export class ScopeItemCapability { ?? 'Unknown'; } + findType(name: string): ScopeItemCapability | undefined { + return this.types?.get(name)?.[0] + ?? this.parent?.findType(name); + } + findModule(name: string): ScopeItemCapability | undefined { return this.modules?.get(name)?.[0] ?? this.parent?.findModule(name); From 48515a2e75df43261daf10bb869150e0416bb023 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 16:46:40 +0800 Subject: [PATCH 07/29] Simplify duplicate and shadowed logic --- server/src/capabilities/capabilities.ts | 65 ++++++++++--------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 99173c3..b0bd7d0 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -264,9 +264,8 @@ export class ScopeItemCapability { return; } - // Track diagnostics added by name so that we can - // add a reference instead of a whole new diagnostic. - const diags = new Map(); + // Track names we've diagnosed to avoid re-diagnosing when we check properties. + const diagnosedNames = new Map(); // Functions and subroutines clash each other. // Build map of all suroutines and functions. @@ -283,56 +282,42 @@ export class ScopeItemCapability { combineNames(names, this.subroutines); names.forEach((items, name) => { - if (items.length === 1) { + // Base case no name clash. + if (items.length <= 1) { return; } - // Create diagnostic - const item = items[0]; - const diagnostic = this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, item.name); - if (!diagnostic) { - return; - } - - // Add references - for (let i = 1; i < items.length; i++) { - this.addDiagnosticReference(diagnostic, items[i]); - } - - // Track the diagnostic to avoid multiples. - diags.set(name, diagnostic); + // Diagnose names. + Services.logger.debug(`Name ${name} is duplicate`); + items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); + diagnosedNames.set(name, undefined); }); // Properties clash with properties of same type. Loop through each and check against names. Object.entries(this.properties ?? {}).forEach(([_, properties]) => { - if (!properties) { - return; - } + properties?.forEach((items, name) => { + const identifier = name.split(' ')[1]; + const nameItems = names.get(identifier) ?? []; - properties.forEach((items, name) => { - // Check if we need to raise a diagnostic. - const combined = [items, names.get(name) ?? []].flat(); - if (combined.length === 1) { + // Base case no name clash. + if (items.length + nameItems.length === 1) { return; } - // Get or create diagnostic. - let startIndex = 0; - let diagnostic = diags.get(name); - if (!diagnostic) { - const item = items[0]; - diagnostic = this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, item.name); - if (!diagnostic) { - return; - } - startIndex = 1; - } + // Diagnose properties. + Services.logger.debug(`Property ${name} is duplicate`); + items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); - // Add references. - for (let i = startIndex; i < items.length; i++) { - this.addDiagnosticReference(diagnostic, items[i]); + // Don't diagnose names if we have already or don't need to. + if (diagnosedNames.has(identifier) || nameItems.length === 0) { + return; } - }) + + // Diagnose names and register. + Services.logger.debug(`Name ${identifier} is duplicate (property)`); + nameItems.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); + diagnosedNames.set(identifier, undefined); + }); }) } From 4eb40d51e569ba828d8a8cd55c3d8e5105f7e0e6 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:20:56 +0800 Subject: [PATCH 08/29] Better ifentification accounts for properties --- server/src/capabilities/capabilities.ts | 103 ++++++++++++------------ 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index b0bd7d0..23b7b74 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -212,14 +212,6 @@ export class ScopeItemCapability { public parent?: ScopeItemCapability, ) { } - /** - * Called on a module after it is re-parsed. - * TODO: Implement logic to self-clean before build. - */ - rebuild(): void { - this.build(); - } - /** * Recursively build from this node down. */ @@ -281,6 +273,7 @@ export class ScopeItemCapability { combineNames(names, this.functions); combineNames(names, this.subroutines); + const logger = Services.logger; names.forEach((items, name) => { // Base case no name clash. if (items.length <= 1) { @@ -288,7 +281,7 @@ export class ScopeItemCapability { } // Diagnose names. - Services.logger.debug(`Name ${name} is duplicate`); + logger.debug(`Name ${name} is duplicate`); items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); diagnosedNames.set(name, undefined); }); @@ -296,8 +289,7 @@ export class ScopeItemCapability { // Properties clash with properties of same type. Loop through each and check against names. Object.entries(this.properties ?? {}).forEach(([_, properties]) => { properties?.forEach((items, name) => { - const identifier = name.split(' ')[1]; - const nameItems = names.get(identifier) ?? []; + const nameItems = names.get(this.identifier) ?? []; // Base case no name clash. if (items.length + nameItems.length === 1) { @@ -305,18 +297,18 @@ export class ScopeItemCapability { } // Diagnose properties. - Services.logger.debug(`Property ${name} is duplicate`); - items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); + logger.debug(`Property ${items[0].name} is duplicate`); + items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, item.name)); // Don't diagnose names if we have already or don't need to. - if (diagnosedNames.has(identifier) || nameItems.length === 0) { + if (diagnosedNames.has(this.identifier) || nameItems.length === 0) { return; } // Diagnose names and register. - Services.logger.debug(`Name ${identifier} is duplicate (property)`); + logger.debug(`Name ${this.identifier} is duplicate (property)`); nameItems.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); - diagnosedNames.set(identifier, undefined); + diagnosedNames.set(this.identifier, undefined); }); }) } @@ -351,10 +343,10 @@ export class ScopeItemCapability { } // All declaration types check for modules. - this.resolveShadowedDeclaration(parent.findType(this.name)); - this.resolveShadowedDeclaration(parent.findModule(this.name)); - this.resolveShadowedDeclaration(parent.findFunction(this.name)); - this.resolveShadowedDeclaration(parent.findSubroutine(this.name)); + this.resolveShadowedDeclaration(parent.findType(this.identifier)); + this.resolveShadowedDeclaration(parent.findModule(this.identifier)); + this.resolveShadowedDeclaration(parent.findFunction(this.identifier)); + this.resolveShadowedDeclaration(parent.findSubroutine(this.identifier)); // Properties care about everything except properties that // aren't the same type. Everything else cares about everything. @@ -365,17 +357,17 @@ export class ScopeItemCapability { // Check get properties. if (!!(this.assignmentType & AssignmentType.GET)) { - this.resolveShadowedDeclaration(parent.findPropertyGetter(this.name)); + this.resolveShadowedDeclaration(parent.findPropertyGetter(this.identifier)); } // Check let properties. if (!!(this.assignmentType & AssignmentType.LET)) { - this.resolveShadowedDeclaration(parent.findPropertyLetter(this.name)); + this.resolveShadowedDeclaration(parent.findPropertyLetter(this.identifier)); } // Check set properties. if (!!(this.assignmentType & AssignmentType.SET)) { - this.resolveShadowedDeclaration(parent.findPropertySetter(this.name)); + this.resolveShadowedDeclaration(parent.findPropertySetter(this.identifier)); } } @@ -396,25 +388,25 @@ export class ScopeItemCapability { // Handle calls that aren't assignments. if (this.assignmentType === AssignmentType.CALL) { - this.linkThisToItem(this.findFunction(this.name)); - this.linkThisToItem(this.findSubroutine(this.name)); + this.linkThisToItem(this.findFunction(this.identifier)); + this.linkThisToItem(this.findSubroutine(this.identifier)); return; } // Handle get/set/let relationships. if (this.assignmentType & AssignmentType.GET) { - this.linkThisToItem(this.findFunction(this.name)); - this.linkThisToItem(this.findPropertyGetter(this.name)); + this.linkThisToItem(this.findFunction(this.identifier)); + this.linkThisToItem(this.findPropertyGetter(this.identifier)); return; } if (this.assignmentType & AssignmentType.LET) { - this.linkThisToItem(this.findPropertyLetter(this.name)); + this.linkThisToItem(this.findPropertyLetter(this.identifier)); return; } if (this.assignmentType & AssignmentType.SET) { - this.linkThisToItem(this.findPropertySetter(this.name)); + this.linkThisToItem(this.findPropertySetter(this.identifier)); return; } } @@ -454,45 +446,52 @@ export class ScopeItemCapability { return this.parent?.project; } + get identifier(): string { + if (this.type === ItemType.PROPERTY) { + return this.name.split(' ')[1]; + } + return this.name; + } + get name(): string { return this.explicitSetName ?? this.element?.identifierCapability?.name ?? 'Unknown'; } - findType(name: string): ScopeItemCapability | undefined { - return this.types?.get(name)?.[0] - ?? this.parent?.findType(name); + findType(identifier: string): ScopeItemCapability | undefined { + return this.types?.get(identifier)?.[0] + ?? this.parent?.findType(identifier); } - findModule(name: string): ScopeItemCapability | undefined { - return this.modules?.get(name)?.[0] - ?? this.parent?.findModule(name); + findModule(identifier: string): ScopeItemCapability | undefined { + return this.modules?.get(identifier)?.[0] + ?? this.parent?.findModule(identifier); } - findFunction(name: string): ScopeItemCapability | undefined { - return this.functions?.get(name)?.[0] - ?? this.parent?.findFunction(name); + findFunction(identifier: string): ScopeItemCapability | undefined { + return this.functions?.get(identifier)?.[0] + ?? this.parent?.findFunction(identifier); } - findSubroutine(name: string): ScopeItemCapability | undefined { - return this.subroutines?.get(name)?.[0] - ?? this.parent?.findSubroutine(name); + findSubroutine(identifier: string): ScopeItemCapability | undefined { + return this.subroutines?.get(identifier)?.[0] + ?? this.parent?.findSubroutine(identifier); } - findPropertyGetter(name: string): ScopeItemCapability | undefined { - return this.properties?.getters?.get(name)?.[0] - ?? this.parent?.findPropertyGetter(name); + findPropertyGetter(identifier: string): ScopeItemCapability | undefined { + return this.properties?.getters?.get(identifier)?.[0] + ?? this.parent?.findPropertyGetter(identifier); } - findPropertyLetter(name: string): ScopeItemCapability | undefined { - return this.properties?.letters?.get(name)?.[0] - ?? this.parent?.findPropertyLetter(name); + findPropertyLetter(identifier: string): ScopeItemCapability | undefined { + return this.properties?.letters?.get(identifier)?.[0] + ?? this.parent?.findPropertyLetter(identifier); } - findPropertySetter(name: string): ScopeItemCapability | undefined { - return this.properties?.setters?.get(name)?.[0] - ?? this.parent?.findPropertySetter(name); + findPropertySetter(identifier: string): ScopeItemCapability | undefined { + return this.properties?.setters?.get(identifier)?.[0] + ?? this.parent?.findPropertySetter(identifier); } /** @@ -629,9 +628,9 @@ export class ScopeItemCapability { } private addItem(target: Map, item: ScopeItemCapability): void { - const items = target.get(item.name) ?? []; + const items = target.get(item.identifier) ?? []; items.push(item); - target.set(item.name, items); + target.set(item.identifier, items); } /** From f71f03f054786aff254a51db92ed66d7607dce7c Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 09:18:52 +0800 Subject: [PATCH 09/29] Update eslint --- .eslintignore | 6 -- .eslintrc.js | 20 ---- eslint.config.mjs | 61 +++++++++++ package-lock.json | 264 ++++++++++++++-------------------------------- package.json | 9 +- 5 files changed, 148 insertions(+), 212 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js create mode 100644 eslint.config.mjs diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 98126ea..0000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules/** -client/node_modules/** -client/out/** -server/node_modules/** -server/out/** -server/src/antlr/** \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index f660e39..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,20 +0,0 @@ -/**@type {import('eslint').Linter.Config} */ -// eslint-disable-next-line no-undef -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', - ], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - ], - rules: { - 'semi': [2, "always"], - '@typescript-eslint/no-unused-vars': 0, - '@typescript-eslint/no-explicit-any': 0, - '@typescript-eslint/explicit-module-boundary-types': 0, - '@typescript-eslint/no-non-null-assertion': 0, - } -}; \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..98aefbe --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,61 @@ +// // eslint.config.js +// import js from '@eslint/js'; +// import ts from '@typescript-eslint/eslint-plugin'; +// import parser from '@typescript-eslint/parser'; + +// export default [ +// js.configs.recommended, +// { +// files: ['**/*.ts', '**/*.tsx'], +// languageOptions: { +// parser, +// }, +// plugins: { +// '@typescript-eslint': ts, +// }, +// rules: { +// semi: ['error', 'always'], +// '@typescript-eslint/no-unused-vars': 'off', +// '@typescript-eslint/no-explicit-any': 'off', +// '@typescript-eslint/explicit-module-boundary-types': 'off', +// '@typescript-eslint/no-non-null-assertion': 'off', +// }, +// }, +// ]; + +// eslint.config.js +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import parser from '@typescript-eslint/parser'; + +export default [ + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + rules: { + semi: ['error', 'always'], + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + }, + }, + { + ignores: [ + 'node_modules/', + 'client/node_modules/', + 'client/out/', + 'server/node_modules/', + 'server/out/', + 'server/src/antlr/', + ], + }, +]; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 010c12e..4eb8367 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "dependencies": { "antlr4ng": "^3.0.16", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.9.1" + "tsyringe": "^4.9.1", + "typescript-eslint": "^8.29.1" }, "devDependencies": { "@types/mocha": "^10.0.10", @@ -23,11 +24,11 @@ "@vscode/test-electron": "^2.4.1", "antlr4ng-cli": "^2.0.0", "esbuild": "^0.25.2", - "eslint": "^9.23.0", + "eslint": "^9.24.0", "js-yaml": "^4.1.0", "mocha": "^11.1.0", "npm-run-all": "^4.1.5", - "typescript": "^5.8.2", + "typescript": "^5.8.3", "vscode-tmgrammar-test": "^0.1.3" }, "engines": { @@ -470,7 +471,6 @@ "version": "4.5.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", - "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -489,17 +489,15 @@ "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": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", @@ -514,7 +512,6 @@ "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", @@ -525,7 +522,6 @@ "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" @@ -538,7 +534,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -548,7 +543,6 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -561,7 +555,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -585,7 +578,6 @@ "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", @@ -596,7 +588,6 @@ "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" @@ -606,10 +597,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", - "dev": true, + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -619,7 +609,6 @@ "version": "2.1.6", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -629,7 +618,6 @@ "version": "0.2.8", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.13.0", @@ -643,7 +631,6 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -656,7 +643,6 @@ "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": { "node": ">=18.18.0" @@ -666,7 +652,6 @@ "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.1", @@ -680,7 +665,6 @@ "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" @@ -694,7 +678,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" @@ -708,7 +691,6 @@ "version": "0.4.2", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -778,7 +760,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -792,7 +773,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -802,7 +782,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -827,7 +806,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { @@ -841,7 +819,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/mocha": { @@ -862,17 +839,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz", - "integrity": "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", + "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/type-utils": "8.29.0", - "@typescript-eslint/utils": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/type-utils": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -892,16 +868,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz", - "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", + "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", "debug": "^4.3.4" }, "engines": { @@ -917,14 +892,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz", - "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", + "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0" + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -935,14 +909,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz", - "integrity": "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", + "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.0", - "@typescript-eslint/utils": "8.29.0", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/utils": "8.29.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -959,10 +932,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz", - "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", + "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -973,14 +945,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz", - "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", + "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/visitor-keys": "8.29.0", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1000,16 +971,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz", - "integrity": "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.0", - "@typescript-eslint/types": "8.29.0", - "@typescript-eslint/typescript-estree": "8.29.0" + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1024,13 +994,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz", - "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==", - "dev": true, + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", + "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.0", + "@typescript-eslint/types": "8.29.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1045,7 +1014,6 @@ "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" @@ -1289,7 +1257,6 @@ "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -1302,7 +1269,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -1322,7 +1288,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -1362,7 +1327,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1409,7 +1373,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { @@ -1481,7 +1444,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -1556,7 +1518,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1566,7 +1527,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1687,7 +1647,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1710,7 +1669,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1727,7 +1685,6 @@ "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" @@ -1872,7 +1829,6 @@ "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" @@ -1885,7 +1841,6 @@ "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": { @@ -1902,7 +1857,6 @@ "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": { @@ -1923,7 +1877,6 @@ "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": { "path-key": "^3.1.0", @@ -1992,7 +1945,6 @@ "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": { "ms": "^2.1.3" @@ -2023,7 +1975,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, "license": "MIT" }, "node_modules/define-data-property": { @@ -2313,7 +2264,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -2323,19 +2273,18 @@ } }, "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", - "dev": true, + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", + "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", + "@eslint/js": "9.24.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2387,7 +2336,6 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -2404,7 +2352,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2417,7 +2364,6 @@ "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", @@ -2428,7 +2374,6 @@ "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" @@ -2441,7 +2386,6 @@ "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" @@ -2454,7 +2398,6 @@ "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" @@ -2467,7 +2410,6 @@ "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.14.0", @@ -2485,7 +2427,6 @@ "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" @@ -2498,7 +2439,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -2511,7 +2451,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -2524,7 +2463,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -2534,7 +2472,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -2544,14 +2481,12 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { "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": { "@nodelib/fs.stat": "^2.0.2", @@ -2568,21 +2503,18 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, "license": "MIT" }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -2592,7 +2524,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" @@ -2605,7 +2536,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2618,7 +2548,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -2645,7 +2574,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", @@ -2659,7 +2587,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, "license": "ISC" }, "node_modules/for-each": { @@ -2850,7 +2777,6 @@ "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" @@ -2863,7 +2789,6 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2913,7 +2838,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, "license": "MIT" }, "node_modules/has-bigints": { @@ -2933,7 +2857,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3087,7 +3010,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -3104,7 +3026,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -3121,7 +3042,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -3320,7 +3240,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3375,7 +3294,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -3414,7 +3332,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -3616,7 +3533,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -3691,7 +3607,6 @@ "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" @@ -3704,7 +3619,6 @@ "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-parse-better-errors": { @@ -3718,14 +3632,12 @@ "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", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, "license": "MIT" }, "node_modules/jszip": { @@ -3745,7 +3657,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -3755,7 +3666,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", @@ -3795,7 +3705,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -3811,7 +3720,6 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/log-symbols": { @@ -3877,7 +3785,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -3887,7 +3794,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -3911,7 +3817,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -4002,14 +3907,12 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, "license": "MIT" }, "node_modules/nice-try": { @@ -4327,7 +4230,6 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", @@ -4455,7 +4357,6 @@ "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" @@ -4471,7 +4372,6 @@ "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" @@ -4501,7 +4401,6 @@ "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" @@ -4528,7 +4427,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4548,7 +4446,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4595,7 +4492,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -4641,7 +4537,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" @@ -4658,7 +4553,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4668,7 +4562,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -4824,7 +4717,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -4858,7 +4750,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -4869,7 +4760,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -4969,7 +4859,6 @@ "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5048,7 +4937,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5061,7 +4949,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5427,7 +5314,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5537,7 +5423,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -5550,7 +5435,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18.12" @@ -5581,7 +5465,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" @@ -5669,10 +5552,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -5682,6 +5564,28 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", + "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "@typescript-eslint/utils": "8.29.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -5712,7 +5616,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -5923,7 +5826,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -6035,7 +5937,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6254,7 +6155,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/package.json b/package.json index 9a12247..c17eaf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vba-lsp", - "type": "commonjs", + "type": "module", "displayName": "VBA Pro", "description": "A VBA extension for VSCode with Language Server support", "icon": "images/vba-lsp-icon.png", @@ -204,7 +204,8 @@ "dependencies": { "antlr4ng": "^3.0.16", "reflect-metadata": "^0.2.2", - "tsyringe": "^4.9.1" + "tsyringe": "^4.9.1", + "typescript-eslint": "^8.29.1" }, "devDependencies": { "@types/mocha": "^10.0.10", @@ -215,11 +216,11 @@ "@vscode/test-electron": "^2.4.1", "antlr4ng-cli": "^2.0.0", "esbuild": "^0.25.2", - "eslint": "^9.23.0", + "eslint": "^9.24.0", "js-yaml": "^4.1.0", "mocha": "^11.1.0", "npm-run-all": "^4.1.5", - "typescript": "^5.8.2", + "typescript": "^5.8.3", "vscode-tmgrammar-test": "^0.1.3" } } From 046c911926b6513fad464e93254d3a7ddcb77661 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 09:21:46 +0800 Subject: [PATCH 10/29] Added handling for visibility modifiers. --- server/src/capabilities/capabilities.ts | 84 ++++++++++++++----------- server/src/project/document.ts | 4 +- server/src/project/elements/typing.ts | 9 ++- 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 23b7b74..63821fe 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -20,7 +20,7 @@ import { Services } from '../injection/services'; abstract class BaseCapability { - element: BaseContextSyntaxElement + element: BaseContextSyntaxElement; constructor(element: BaseContextSyntaxElement) { this.element = element; @@ -39,7 +39,7 @@ export class FoldingRangeCapability extends BaseCapability { const end = { line: this.element.context.range.end.line - trailingLineCount, character: this.element.context.range.end.character - } + }; const range = Range.create(start, end); return new FoldingRange(range, this.foldingRangeKind, this.openWord, this.closeWord); } @@ -53,7 +53,7 @@ export class FoldingRangeCapability extends BaseCapability { export class DiagnosticCapability extends BaseCapability { diagnostics: Diagnostic[] = []; - evaluate: (...args: any[]) => Diagnostic[] + evaluate: (...args: any[]) => Diagnostic[]; constructor(element: BaseContextSyntaxElement, evaluate?: (...args: any[]) => Diagnostic[]) { super(element); @@ -70,7 +70,7 @@ export class SemanticTokenCapability extends BaseCapability { get semanticToken(): SemanticToken { const element = this.element as BaseContextSyntaxElement & HasSemanticTokenCapability; - const context = !!element.identifierCapability + const context = element.identifierCapability ? new Context(element.identifierCapability.nameContext, element.context.document) : element.context; @@ -100,7 +100,7 @@ export class SemanticTokenCapability extends BaseCapability { export class SymbolInformationCapability extends BaseCapability { - private symbolKind: SymbolKind + private symbolKind: SymbolKind; get SymbolInformation(): SymbolInformation { const element = this.element as BaseIdentifyableSyntaxElement; @@ -109,7 +109,7 @@ export class SymbolInformationCapability extends BaseCapability { this.symbolKind, element.context.range, element.context.document.uri - ) + ); } constructor(element: BaseIdentifyableSyntaxElement, symbolKind: SymbolKind) { @@ -147,7 +147,7 @@ export class IdentifierCapability extends BaseCapability { // 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; + this.range = args.defaultRange ? args.defaultRange() : args.element.context.range; } } } @@ -201,8 +201,12 @@ export class ScopeItemCapability { link?: ScopeItemCapability; backLinks?: ScopeItemCapability[]; - // Properties + // Technical + isDirty: boolean = true; + + // Item Properties explicitSetName?: string; + isPublicScope: boolean = false; constructor( @@ -247,6 +251,8 @@ export class ScopeItemCapability { this.properties?.getters?.forEach(items => items.forEach(item => item.build())); this.properties?.letters?.forEach(items => items.forEach(item => item.build())); this.properties?.setters?.forEach(items => items.forEach(item => item.build())); + + this.isDirty = false; } /** Resolves for the current scope, i.e., children of the current item. */ @@ -265,7 +271,7 @@ export class ScopeItemCapability { const combineNames = (a: Map, b: Map | undefined) => { b?.forEach((bItems, name) => a.set(name, [a.get(name) ?? [], bItems].flat())); return a; - } + }; const names = new Map(); combineNames(names, this.types); @@ -310,7 +316,7 @@ export class ScopeItemCapability { nameItems.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); diagnosedNames.set(this.identifier, undefined); }); - }) + }); } private addDiagnosticReference(diagnostic: BaseDiagnostic | undefined, item: ScopeItemCapability): void { @@ -325,13 +331,13 @@ export class ScopeItemCapability { uri: context.document.uri, range: context.range } - }) + }); } private resolveShadowedDeclaration(item: ScopeItemCapability | undefined): void { if (item) { const diagnostic = this.pushDiagnostic(ShadowDeclarationDiagnostic); - this.addDiagnosticReference(diagnostic, item) + this.addDiagnosticReference(diagnostic, item); } } @@ -356,17 +362,17 @@ export class ScopeItemCapability { // get/set/let diagnostics into one single diagnostic. // Check get properties. - if (!!(this.assignmentType & AssignmentType.GET)) { + if (this.assignmentType & AssignmentType.GET) { this.resolveShadowedDeclaration(parent.findPropertyGetter(this.identifier)); } // Check let properties. - if (!!(this.assignmentType & AssignmentType.LET)) { + if (this.assignmentType & AssignmentType.LET) { this.resolveShadowedDeclaration(parent.findPropertyLetter(this.identifier)); } // Check set properties. - if (!!(this.assignmentType & AssignmentType.SET)) { + if (this.assignmentType & AssignmentType.SET) { this.resolveShadowedDeclaration(parent.findPropertySetter(this.identifier)); } } @@ -504,12 +510,18 @@ export class ScopeItemCapability { // Public scoped elements should get the project. // Check pub/priv declares in same document treated as duplicate instead of shadowed. + /** + * !! Declaring public things is working :) + * So far: just Enum (need to set up the logic for everything). + */ + // Set the parent for the item. - item.parent = this; + item.parent = this.isPublicScope ? (this.project ?? this) : this; + item.parent.isDirty = true; let ancestor: ScopeItemCapability | undefined = this; let ancestorLevel = 0; - while (!!ancestor) { + while (ancestor) { ancestorLevel += 1; ancestor = ancestor.parent; } @@ -517,54 +529,54 @@ export class ScopeItemCapability { // Reference types are not declarations. if (item.type === ItemType.REFERENCE) { - this.references ??= new Map(); - this.addItem(this.references, item); + item.parent.references ??= new Map(); + item.parent.addItem(item.parent.references, item); return this; } // Register functions. if (item.type === ItemType.FUNCTION) { - this.functions ??= new Map(); - this.addItem(this.functions, item); + item.parent.functions ??= new Map(); + item.parent.addItem(item.parent.functions, item); return item; } // Register subroutine. if (item.type === ItemType.SUBROUTINE) { - this.subroutines ??= new Map(); - this.addItem(this.subroutines, item); + item.parent.subroutines ??= new Map(); + item.parent.addItem(item.parent.subroutines, item); return item; } // Register enum or type. if (item.type === ItemType.TYPE) { - this.types ??= new Map(); - this.addItem(this.types, item); + item.parent.types ??= new Map(); + item.parent.addItem(item.parent.types, item); return item; } // Register properties and variables. if (item.type === ItemType.PROPERTY || item.type === ItemType.VARIABLE) { - this.properties ??= {}; + item.parent.properties ??= {}; if (item.assignmentType & AssignmentType.GET) { - this.properties.getters ??= new Map(); - this.addItem(this.properties.getters, item); + item.parent.properties.getters ??= new Map(); + item.parent.addItem(item.parent.properties.getters, item); } if (item.assignmentType & AssignmentType.LET) { - this.properties.letters ??= new Map(); - this.addItem(this.properties.letters, item); + item.parent.properties.letters ??= new Map(); + item.parent.addItem(item.parent.properties.letters, item); } if (item.assignmentType & AssignmentType.SET) { - this.properties.setters ??= new Map(); - this.addItem(this.properties.setters, item); + item.parent.properties.setters ??= new Map(); + item.parent.addItem(item.parent.properties.setters, item); } return item.type === ItemType.PROPERTY ? item : this; } // Handle module registration if (item.type === ItemType.MODULE) { - this.modules ??= new Map(); - this.addItem(this.modules, item); + item.parent.modules ??= new Map(); + item.parent.addItem(item.parent.modules, item); return item; } @@ -580,7 +592,7 @@ export class ScopeItemCapability { item.link?.removeBacklink(item); // Remove link from any backlinked items. item.backLinks?.forEach(node => node.link = undefined); - } + }; const scan = (map: Map | undefined, uri: string) => { if (map === undefined) { @@ -589,7 +601,7 @@ export class ScopeItemCapability { const keys = Array.from(map.keys()); keys.forEach(key => { - const items = map.get(key)! + const items = map.get(key)!; const keep = items.filter(item => item.element?.context.document.uri !== uri); const remove = items.filter(item => item.element?.context.document.uri === uri); diff --git a/server/src/project/document.ts b/server/src/project/document.ts index 897337e..d97816d 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -191,7 +191,9 @@ export abstract class BaseProjectDocument { // Parse the document. await (new SyntaxParser(Services.logger)).parseAsync(token, this); - this.currentScope.build(); + const projectScope = this.currentScope.project; + const buildScope = projectScope?.isDirty ? projectScope : this.currentScope; + buildScope.build(); // Evaluate the diagnostics. this.diagnostics = this.hasDiagnosticElements diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts index 925bae7..f6ff248 100644 --- a/server/src/project/elements/typing.ts +++ b/server/src/project/elements/typing.ts @@ -4,11 +4,13 @@ import { SemanticTokenModifiers, SemanticTokenTypes, SymbolKind } from 'vscode-l // Antlr import { ParserRuleContext } from 'antlr4ng'; -import { ConstItemContext, +import { + ConstItemContext, EnumDeclarationContext, EnumMemberContext, GlobalVariableDeclarationContext, PrivateConstDeclarationContext, + PrivateEnumDeclarationContext, PrivateTypeDeclarationContext, PrivateVariableDeclarationContext, PublicConstDeclarationContext, @@ -38,7 +40,10 @@ abstract class BaseTypeDeclarationElement extends B this.diagnosticCapability = new DiagnosticCapability(this); this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); this.semanticTokenCapability = new SemanticTokenCapability(this, tokenType, tokenModifiers ?? []); + + // An enum is public unless explicitly set to private. this.scopeItemCapability = new ScopeItemCapability(this, ItemType.TYPE); + this.scopeItemCapability.isPublicScope = !(ctx.parent instanceof PrivateEnumDeclarationContext); } } @@ -142,7 +147,7 @@ export class VariableDeclarationElement extends BaseContextSyntaxElement ctx.ambiguousIdentifier()}); + this.identifierCapability = new IdentifierCapability({ element: this, getNameContext: () => ctx.ambiguousIdentifier() }); this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE); } } From 2d253b757c9f95bec91b9e46d647f973c48a2d65 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 09:40:26 +0800 Subject: [PATCH 11/29] Appeasing the linter --- server/src/capabilities/capabilities.ts | 10 +- server/src/capabilities/diagnostics.ts | 2 +- server/src/capabilities/folding.ts | 4 +- server/src/capabilities/semanticTokens.ts | 4 +- server/src/extensions/numberExtensions.ts | 2 +- server/src/extensions/parserExtensions.ts | 116 ++++++++++----------- server/src/project/document.ts | 24 ++--- server/src/project/elements/base.ts | 4 +- server/src/project/elements/module.ts | 14 +-- server/src/project/elements/precompiled.ts | 22 ++-- server/src/project/elements/typing.ts | 4 +- server/src/project/formatter.ts | 13 +-- server/src/project/parser/vbaAntlr.ts | 52 ++++----- server/src/project/parser/vbaListener.ts | 66 ++++++------ server/src/project/parser/vbaParser.ts | 4 +- server/src/project/workspace.ts | 25 +++-- server/src/server.ts | 8 +- server/src/utils/helpers.ts | 8 +- server/src/utils/logger.ts | 20 ++-- server/src/utils/wrappers.ts | 36 +++---- 20 files changed, 220 insertions(+), 218 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 63821fe..6139e8c 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -519,12 +519,10 @@ export class ScopeItemCapability { item.parent = this.isPublicScope ? (this.project ?? this) : this; item.parent.isDirty = true; - let ancestor: ScopeItemCapability | undefined = this; - let ancestorLevel = 0; - while (ancestor) { - ancestorLevel += 1; - ancestor = ancestor.parent; - } + // Get the scope level for logging. + const getAncestorLevel = (item: ScopeItemCapability, level: number): number => + item.parent ? getAncestorLevel(item.parent, level + 1) : level; + const ancestorLevel = getAncestorLevel(this, 0); Services.logger.debug(`Registering [${ItemType[item.type]}] ${item.name}`, ancestorLevel); // Reference types are not declarations. diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 3b393b8..579b328 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -8,7 +8,7 @@ export type DiagnosticConstructor = export abstract class BaseDiagnostic implements Diagnostic { range: Range; - message: string + message: string; severity?: DiagnosticSeverity | undefined; code?: string | number | undefined; codeDescription?: CodeDescription | undefined; diff --git a/server/src/capabilities/folding.ts b/server/src/capabilities/folding.ts index 5cb5a9c..2456032 100644 --- a/server/src/capabilities/folding.ts +++ b/server/src/capabilities/folding.ts @@ -41,7 +41,7 @@ export class FoldingRange implements VscFoldingRange { * [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds. */ get kind(): string | undefined { - return this._foldingRangeKind + return this._foldingRangeKind; } get openWord(): string { @@ -56,7 +56,7 @@ export class FoldingRange implements VscFoldingRange { return { startLine: this.startLine, endLine: this.endLine - } + }; } constructor( diff --git a/server/src/capabilities/semanticTokens.ts b/server/src/capabilities/semanticTokens.ts index 0377afb..38f5de0 100644 --- a/server/src/capabilities/semanticTokens.ts +++ b/server/src/capabilities/semanticTokens.ts @@ -15,7 +15,7 @@ import { ParserRuleContext } from 'antlr4ng/dist/ParserRuleContext'; 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]))); +const registeredTokenModifiers = new Map((Object.keys(SemanticTokenModifiers) as (keyof typeof SemanticTokenModifiers)[]).map((k, i) => ([k, 2 ** i]))); export function activateSemanticTokenProvider(result: InitializeResult) { @@ -82,7 +82,7 @@ export class SemanticTokensManager { const sortedTokens = this.sortSemanticTokens(filteredTokens); if (sortedTokens.length === 0) return null; - + // Get an array of SemanticTokens relative to previous token. const relativeResult: uinteger[] = sortedTokens.map((token, i) => token.toUintegerArray(i === 0 ? undefined : sortedTokens[i - 1])).flat(); diff --git a/server/src/extensions/numberExtensions.ts b/server/src/extensions/numberExtensions.ts index e611389..77b324a 100644 --- a/server/src/extensions/numberExtensions.ts +++ b/server/src/extensions/numberExtensions.ts @@ -1,4 +1,4 @@ -export {}; // ensures this is treated as a module +export { }; // ensures this is treated as a module declare global { interface Number { diff --git a/server/src/extensions/parserExtensions.ts b/server/src/extensions/parserExtensions.ts index 041dc6e..63bc027 100644 --- a/server/src/extensions/parserExtensions.ts +++ b/server/src/extensions/parserExtensions.ts @@ -9,35 +9,35 @@ import { ParserRuleContext, TerminalNode } from 'antlr4ng'; import { CompilerConditionalStatementContext } from '../antlr/out/vbapreParser'; import { AmbiguousIdentifierContext, - BuiltinTypeContext, - ClassTypeNameContext, - ConstItemContext, - EndOfStatementContext, - EndOfStatementNoWsContext, - GlobalVariableDeclarationContext, - PrivateConstDeclarationContext, - PrivateVariableDeclarationContext, - ProcedureTailContext, - PublicConstDeclarationContext, - PublicVariableDeclarationContext, - TypeSpecContext, - TypeSuffixContext, - VariableDclContext, - WitheventsVariableDclContext + BuiltinTypeContext, + ClassTypeNameContext, + ConstItemContext, + EndOfStatementContext, + EndOfStatementNoWsContext, + GlobalVariableDeclarationContext, + PrivateConstDeclarationContext, + PrivateVariableDeclarationContext, + ProcedureTailContext, + PublicConstDeclarationContext, + PublicVariableDeclarationContext, + TypeSpecContext, + TypeSuffixContext, + VariableDclContext, + WitheventsVariableDclContext } from '../antlr/out/vbaParser'; import { LineEndingContext } from '../antlr/out/vbafmtParser'; declare module 'antlr4ng' { - interface ParserRuleContext { + interface ParserRuleContext { /** Convert the node to a range. */ - toRange(doc: TextDocument): Range; + toRange(doc: TextDocument): Range; startIndex(): number; stopIndex(): number; hasPositionOf(ctx: ParserRuleContext): boolean; endsWithLineEnding: boolean; countTrailingLineEndings(): number; - } + } interface TerminalNode { /** Convert the node to a range. */ @@ -111,25 +111,25 @@ declare module '../antlr/out/vbaParser' { 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) - ); + const startIndex = this.start?.start ?? 0; + const stopIndex = this.stop?.stop ?? startIndex; + return Range.create( + doc.positionAt(startIndex), + doc.positionAt(stopIndex + 1) + ); }; ParserRuleContext.prototype.startIndex = function (): number { return this.start?.start ?? 0; -} +}; ParserRuleContext.prototype.stopIndex = function (): number { return this.stop?.stop ?? this.startIndex(); -} +}; ParserRuleContext.prototype.hasPositionOf = function (ctx: ParserRuleContext): boolean { return this.startIndex() === ctx.startIndex() && this.stopIndex() === ctx.stopIndex(); -} +}; Object.defineProperty(ParserRuleContext.prototype, 'endsWithLineEnding', { get: function endsWithLineEnding() { @@ -159,7 +159,7 @@ Object.defineProperty(ParserRuleContext.prototype, 'endsWithLineEnding', { // Not a line ending and no more children. return false; } -}) +}); interface LineEndingParserRuleContext { NEWLINE(): TerminalNode | null; @@ -167,8 +167,8 @@ interface LineEndingParserRuleContext { function isLineEndingParserRuleContext(ctx: unknown): ctx is LineEndingParserRuleContext { return typeof ctx === 'object' - && ctx !== null - && typeof (ctx as any).NEWLINE === 'function'; + && ctx !== null + && typeof (ctx as any).NEWLINE === 'function'; } function countTrailingLineEndings(ctx: ParserRuleContext): number { @@ -186,7 +186,7 @@ function countTrailingLineEndings(ctx: ParserRuleContext): number { let result = 0; while (i < lines.length) { const char = lines[i]; - + if (char === '\r') { result++; i += lines[i + 1] === '\n' ? 2 : 1; @@ -201,7 +201,7 @@ function countTrailingLineEndings(ctx: ParserRuleContext): number { // Recursive call on last child. const lastChild = ctx.children.at(-1); - if (!!(lastChild instanceof ParserRuleContext)) { + if (lastChild instanceof ParserRuleContext) { return countTrailingLineEndings(lastChild); } @@ -211,27 +211,27 @@ function countTrailingLineEndings(ctx: ParserRuleContext): number { ParserRuleContext.prototype.countTrailingLineEndings = function (): number { return countTrailingLineEndings(this); -} +}; TerminalNode.prototype.toRange = function (doc: TextDocument): Range { return Range.create( - doc.positionAt(this.startIndex()), - doc.positionAt(this.stopIndex() + 1) - ); + doc.positionAt(this.startIndex()), + doc.positionAt(this.stopIndex() + 1) + ); }; TerminalNode.prototype.startIndex = function (): number { return this.getPayload()?.start ?? 0; -} +}; TerminalNode.prototype.stopIndex = function (): number { return this.getPayload()?.stop ?? this.startIndex(); -} +}; CompilerConditionalStatementContext.prototype.vbaExpression = function (): string { - return (this.compilerIfStatement() ?? this.compilerElseIfStatement())! + return (this.compilerIfStatement() ?? this.compilerElseIfStatement())! .booleanExpression() .getText() .toLowerCase(); @@ -264,7 +264,7 @@ ConstItemContext.prototype.ambiguousIdentifier = function (): AmbiguousIdentifie ConstItemContext.prototype.typeContext = function (): BuiltinTypeContext | TypeSuffixContext | undefined { return this.typedNameConstItem()?.typedName().typeSuffix() - ?? this.untypedNameConstItem()?.constAsClause()?.builtinType() + ?? this.untypedNameConstItem()?.constAsClause()?.builtinType(); }; @@ -320,33 +320,33 @@ VariableDclContext.prototype.toSymbolKind = function (): SymbolKind { function toSymbolKind(context: BuiltinTypeContext | TypeSuffixContext | TypeSpecContext | ClassTypeNameContext | undefined): SymbolKind { - switch (context?.getText().toLocaleLowerCase()) { + switch (context?.getText().toLocaleLowerCase()) { case undefined: - return SymbolKind.Class - case 'boolean': - return SymbolKind.Boolean; + return SymbolKind.Class; + case 'boolean': + return SymbolKind.Boolean; case '$': // string - case 'byte': - case 'string': - return SymbolKind.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 'double': + case 'currency': case 'integer': - case 'long': - case 'longPtr': - case 'longLong': - return SymbolKind.Number; - case 'object': - return SymbolKind.Object; - default: - return SymbolKind.Class; - } + case 'long': + case 'longPtr': + case 'longLong': + return SymbolKind.Number; + case 'object': + return SymbolKind.Object; + default: + return SymbolKind.Class; + } } /** diff --git a/server/src/project/document.ts b/server/src/project/document.ts index d97816d..12b691f 100644 --- a/server/src/project/document.ts +++ b/server/src/project/document.ts @@ -59,7 +59,7 @@ export abstract class BaseProjectDocument { protected symbolInformations: SymbolInformation[] = []; protected unhandledNamedElements: [] = []; protected isClosed = false; - protected currentScope: ScopeItemCapability + protected currentScope: ScopeItemCapability; abstract symbolKind: SymbolKind @@ -176,7 +176,7 @@ export abstract class BaseProjectDocument { return await (new SyntaxParser(Services.logger)).formatParseAsync(token, this); } catch (e) { Services.logger.debug('caught doc'); - throw e + throw e; } } @@ -220,11 +220,11 @@ export abstract class BaseProjectDocument { * @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); - if (!!element.scopeItemCapability) this.registerScopeItem(element as HasScopeItemCapability); + 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); + if (element.scopeItemCapability) this.registerScopeItem(element as HasScopeItemCapability); return this; } @@ -253,12 +253,12 @@ export abstract class BaseProjectDocument { registerSemanticToken = (element: HasSemanticTokenCapability) => { this.semanticTokens.add(element); return this; - } + }; registerSubtractElement = (element: BaseContextSyntaxElement) => { this.redactedElements.push(element); return this; - } + }; /** * Registers a SymbolInformation. @@ -268,7 +268,7 @@ export abstract class BaseProjectDocument { registerSymbolInformation = (element: HasSymbolInformationCapability) => { this.symbolInformations.push(element.symbolInformationCapability.SymbolInformation); return this; - } + }; registerScopeItem = (element: HasScopeItemCapability) => this.currentScope = this.currentScope.registerScopeItem(element.scopeItemCapability); @@ -278,7 +278,7 @@ export abstract class BaseProjectDocument { const parent = this.currentScope.parent; this.currentScope = parent ?? this.currentScope; } - } + }; private subtractTextFromRanges(ranges: Range[]): string { const text = this.textDocument.getText(); @@ -313,7 +313,7 @@ export abstract class BaseProjectDocument { const result = docLines.map((line, i) => i >= y && i < x ? ' '.repeat(line.length) : line ); - return result.join('\r\n') + return result.join('\r\n'); } } } diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index 28d2afb..5d74596 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -18,7 +18,7 @@ import { export abstract class BaseSyntaxElement { // Base Properties - context?: Context + context?: Context; identifierCapability?: IdentifierCapability; // Capabilities @@ -99,7 +99,7 @@ export class Context { rule: T; document: TextDocument; range: Range; - + get text(): string { return this.rule.getText(); } diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts index c48c46a..548df4a 100644 --- a/server/src/project/elements/module.ts +++ b/server/src/project/elements/module.ts @@ -30,7 +30,7 @@ abstract class BaseModuleElement extends BaseIdenti abstract attrubutes: ParserRuleContext[]; abstract diagnosticCapability: DiagnosticCapability; abstract hasOptionExplicit: boolean; - + settings: DocumentSettings; // foldingRangeCapability: FoldingRangeCapability; symbolInformationCapability: SymbolInformationCapability; @@ -82,7 +82,7 @@ abstract class BaseModuleElement extends BaseIdenti .commonModuleCodeElement() ?.commonOptionDirective() ?.optionExplicitDirective(); - if (!!isOptionExplicitDirective) return true; + if (isOptionExplicitDirective) return true; } return false; } @@ -93,11 +93,11 @@ abstract class BaseModuleElement extends BaseIdenti attrs.forEach(attr => { const attrName = getAttributeName(attr); if (catalogue.has(attrName)) { - result.push(attr) + result.push(attr); } else { catalogue.set(attrName, null); } - }) + }); return result; } } @@ -167,7 +167,7 @@ export class ClassElement extends BaseModuleElement { nameContextGetter = () => ctx .classModuleHeader() .nameAttr()[0] - .STRINGLITERAL() + .STRINGLITERAL(); } this.identifierCapability = new IdentifierCapability({ element: this, @@ -195,12 +195,12 @@ export class ModuleIgnoredAttributeElement extends BaseContextSyntaxElement { if(x) this.conditionalBlocks.push(new CompilerConditionBlock(x, doc, env)) }); + blocks.map(x => { if (x) this.conditionalBlocks.push(new CompilerConditionBlock(x, doc, env)); }); // Create the comment elements. let resolved = false; @@ -39,9 +39,9 @@ export class CompilerLogicalBlock extends BaseContextSyntaxElement { - readonly documentSettings: {environment: { os: string, version: string }}; + readonly documentSettings: { environment: { os: string, version: string } }; - constructor(ctx: CompilerConditionalBlockContext | CompilerDefaultBlockContext, doc: TextDocument, env: {environment: { os: string, version: string }}) { + constructor(ctx: CompilerConditionalBlockContext | CompilerDefaultBlockContext, doc: TextDocument, env: { environment: { os: string, version: string } }) { super(ctx, doc); this.documentSettings = env; } @@ -53,7 +53,7 @@ class CompilerConditionBlock extends BaseContextSyntaxElement 'compilerElseStatement' in o)(ctx)) return true; + if (((o: any): o is CompilerDefaultBlockContext => 'compilerElseStatement' in o)(ctx)) return true; const vbaExpression = ctx.compilerConditionalStatement().vbaExpression(); const tsExpression = this.transpileVbaToTypescript(vbaExpression); @@ -70,7 +70,7 @@ class CompilerConditionBlock extends BaseContextSyntaxElement [x, envToBooleanText(x)])); replacements.set('or', '||'); replacements.set('and', '&&'); @@ -92,7 +92,7 @@ class CompilerConditionBlock extends BaseContextSyntaxElement { - const regexp = RegExp(`${k}`, 'i') + const regexp = RegExp(`${k}`, 'i'); if (regexp.test(result)) { result = result.replace(regexp, v); } diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts index f6ff248..2b6a984 100644 --- a/server/src/project/elements/typing.ts +++ b/server/src/project/elements/typing.ts @@ -85,7 +85,7 @@ export class EnumMemberDeclarationElement extends BaseContextSyntaxElement { identifierCapability: IdentifierCapability; - private _isPublic: boolean + private _isPublic: boolean; get isPublic(): boolean { return this._isPublic; } constructor(ctx: PublicTypeDeclarationContext | PrivateTypeDeclarationContext, doc: TextDocument, isPublic: boolean) { @@ -174,5 +174,5 @@ export class TypeSuffixElement extends BaseContextSyntaxElement= MAXRECURSION; i--) { if (i <= -20) { throw new InputMismatchException(recognizer); @@ -157,7 +157,7 @@ export class VbaErrorHandler extends DefaultErrorStrategy { currentToken.stop, currentToken.line, currentToken.column - ) + ); } private isTokenPositionMatch(a: Token | null, b: Token | null): boolean { diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index fc85c03..20b2aae 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -114,7 +114,7 @@ export class VbaListener extends vbaListener { enterAnyOperator = (ctx: AnyOperatorContext) => { const element = new DuplicateOperatorElement(ctx, this.document.textDocument); this.document.registerElement(element); - } + }; enterEnumDeclaration = (ctx: EnumDeclarationContext) => { const element = new EnumDeclarationElement(ctx, this.document.textDocument, this.isAfterMethodDeclaration); @@ -124,7 +124,7 @@ export class VbaListener extends vbaListener { enterEnumMember = (ctx: EnumMemberContext) => { const element = new EnumMemberDeclarationElement(ctx, this.document.textDocument); this.document.registerElement(element); - } + }; enterClassModule = (ctx: ClassModuleContext) => { const element = new ClassElement(ctx, this.document.textDocument, this.documentSettings ?? { doWarnOptionExplicitMissing: true }); @@ -137,7 +137,7 @@ export class VbaListener extends vbaListener { enterIgnoredClassAttr = (ctx: IgnoredClassAttrContext) => this.registerIgnoredAttribute(ctx); enterIgnoredProceduralAttr = (ctx: IgnoredProceduralAttrContext) => this.registerIgnoredAttribute(ctx); private registerIgnoredAttribute(ctx: IgnoredClassAttrContext | IgnoredProceduralAttrContext) { - this.document.registerElement(new ModuleIgnoredAttributeElement(ctx, this.document.textDocument)) + this.document.registerElement(new ModuleIgnoredAttributeElement(ctx, this.document.textDocument)); } enterProceduralModule = (ctx: ProceduralModuleContext) => { @@ -156,7 +156,7 @@ export class VbaListener extends vbaListener { }; enterPropertySetDeclaration = (ctx: PropertySetDeclarationContext) => { - const element = !!ctx.LET() + const element = ctx.LET() ? new PropertyLetDeclarationElement(ctx, this.document.textDocument) : new PropertySetDeclarationElement(ctx, this.document.textDocument); this.document.registerElement(element); @@ -165,19 +165,19 @@ export class VbaListener extends vbaListener { enterSubroutineDeclaration = (ctx: SubroutineDeclarationContext) => { const element = new SubDeclarationElement(ctx, this.document.textDocument); this.document.registerElement(element); - } + }; enterFunctionDeclaration = (ctx: FunctionDeclarationContext) => { const element = new FunctionDeclarationElement(ctx, this.document.textDocument); this.document.registerElement(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); - } + }; enterTypeSuffix = (ctx: TypeSuffixContext) => this.document.registerElement(new TypeSuffixElement(ctx, this.document.textDocument)); @@ -191,15 +191,15 @@ export class VbaListener extends vbaListener { private enterVariableDeclaration = (ctx: PublicConstDeclarationContext | PrivateConstDeclarationContext | PublicVariableDeclarationContext | GlobalVariableDeclarationContext | PrivateVariableDeclarationContext) => { const element = DeclarationStatementElement.create(ctx, this.document.textDocument); element.declarations.forEach(x => this.document.registerElement(x)); - } + }; enterUnexpectedEndOfLine = (ctx: UnexpectedEndOfLineContext) => { const element = new UnexpectedEndOfLineElement(ctx, this.document.textDocument); this.document.registerElement(element); - } + }; enterWhileStatement = (ctx: WhileStatementContext) => { - const element = new WhileLoopElement(ctx, this.document.textDocument) + const element = new WhileLoopElement(ctx, this.document.textDocument); this.document.registerElement(element); }; @@ -235,12 +235,12 @@ export class VbaPreListener extends vbapreListener { b => doc.registerElement(b) .registerSubtractElement(b) ); - } + }; } class PreIfElseBlockElement { - ctx: PreIfElseBlockContext - blocks: { block: PreBlockContext, levels: number[] }[] = [] + ctx: PreIfElseBlockContext; + blocks: { block: PreBlockContext, levels: number[] }[] = []; readonly levelBeforeEnter: number; get levelOnExit(): number { @@ -299,7 +299,7 @@ export class VbaFmtListener extends vbafmtListener { visitErrorNode(node: ErrorNode): void { const doc = this.common.document.textDocument; - Services.logger.error(`Couldn't parse ${node.toRange(doc)}\n${node.getText()}`) + Services.logger.error(`Couldn't parse ${node.toRange(doc)}\n${node.getText()}`); } /** @@ -319,7 +319,7 @@ export class VbaFmtListener extends vbafmtListener { // Attributes are always zero indented. enterAttributeStatement = (ctx: AttributeStatementContext) => { const range = this.getCtxRange(ctx); - const offset = ctx.endsWithLineEnding ? 0 : 1 + const offset = ctx.endsWithLineEnding ? 0 : 1; // Set the line after the end to what is current and then set current to zero. this.setIndentAt({ @@ -334,7 +334,7 @@ export class VbaFmtListener extends vbafmtListener { text: `${this.rangeText(ctx)} Attribute` }); this.activeElements.push(ctx); - } + }; // Line ending treatments. enterContinuation = (ctx: ContinuationContext) => { @@ -356,7 +356,7 @@ export class VbaFmtListener extends vbafmtListener { text: `${this.rangeText(ctx)} cont` }); } - } + }; // Handle anything that has been continued. exitEveryRule(node: ParserRuleContext): void { @@ -371,7 +371,7 @@ export class VbaFmtListener extends vbafmtListener { // Remove from continued and outdent next line. this.continuedElements.pop(); const doc = this.common.document.textDocument; - const offset = node.endsWithLineEnding ? 0 : 1 + const offset = node.endsWithLineEnding ? 0 : 1; const line = node.toRange(doc).end.line + offset; this.modifyIndentAt({ line: line, @@ -392,12 +392,12 @@ export class VbaFmtListener extends vbafmtListener { enterCaseDefaultStatement = (ctx: CaseDefaultStatementContext) => { this.outdentCaseStatementBlock(ctx); this.indentCaseStatementBlock(ctx); - } + }; enterCaseStatement = (ctx: CaseStatementContext) => { this.outdentCaseStatementBlock(ctx); this.indentCaseStatementBlock(ctx); - } + }; enterClassHeaderBlock = (ctx: ClassHeaderBlockContext) => this.indentOnEnter({ context: ctx, offset: 1 }); @@ -412,14 +412,14 @@ export class VbaFmtListener extends vbafmtListener { this.activeElements.push(ctx); exitIndentAfterElement = (ctx: IndentAfterElementContext) => { - const offset = ctx.endsWithLineEnding ? 0 : 1 + const offset = ctx.endsWithLineEnding ? 0 : 1; const line = this.getCtxRange(ctx).end.line + offset; this.modifyIndentAt({ line: line, offset: 2, text: `${this.rangeText(ctx)}` }); - } + }; enterLabelStatement? = (ctx: LabelStatementContext) => { // A label is a special case that will always be 0 indent @@ -427,7 +427,7 @@ export class VbaFmtListener extends vbafmtListener { const line = this.getCtxRange(ctx).start.line; this.indentationKeys[line + 1] = this.getIndent(line); this.indentationKeys[line] = 0; - } + }; enterMethodParameters = (ctx: MethodParametersContext) => this.activeElements.push(ctx); @@ -439,7 +439,7 @@ export class VbaFmtListener extends vbafmtListener { offset: -2, text: `${this.rangeText(ctx)}` }); - } + }; // Enter outdent on enter / indent after exit element. enterOutdentOnIndentAfterElement = (ctx: OutdentOnIndentAfterElementContext) => { @@ -450,19 +450,19 @@ export class VbaFmtListener extends vbafmtListener { offset: -2, text: `${this.rangeText(ctx)}` }); - } + }; // Exit outdent on enter / indent after exit element. exitOutdentOnIndentAfterElement = (ctx: OutdentOnIndentAfterElementContext) => { // Offset the line to indent based on whether the element ends with a new line character. - const offset = ctx.endsWithLineEnding ? 0 : 1 + const offset = ctx.endsWithLineEnding ? 0 : 1; const line = this.getCtxRange(ctx).end.line + offset; this.modifyIndentAt({ line: line, offset: 2, text: `${this.rangeText(ctx)}` }); - } + }; enterPreBlock = (ctx: PreBlockContext) => { // Get and ensure we have a preCompilerElement @@ -486,7 +486,7 @@ export class VbaFmtListener extends vbafmtListener { block: ctx, levels: [preCompilerElement.levelBeforeEnter + 1] }); - } + }; exitPreBlock = (ctx: PreBlockContext) => this.setIndentAt({ @@ -498,7 +498,7 @@ export class VbaFmtListener extends vbafmtListener { enterPreIfElseBlock = (ctx: PreIfElseBlockContext) => { const indentBeforeEnter = this.getIndent(this.getCtxRange(ctx).start.line); this.preCompilerElements.push(new PreIfElseBlockElement(ctx, indentBeforeEnter)); - } + }; exitPreIfElseBlock = (ctx: PreIfElseBlockContext) => this.setIndentAt({ @@ -520,7 +520,7 @@ export class VbaFmtListener extends vbafmtListener { if (!!caseElement && caseElement.endsWithLineEnding) { this.outdentAfterExit({ context: ctx }); } - } + }; /** * Special outdent handler for case statements where the previous case was a block type. @@ -654,7 +654,7 @@ export class VbaFmtListener extends vbafmtListener { // Log the outcome. const num = (params.line + 1).toString().padStart(3, '0'); const arrows = newIndent > 0 ? '>'.repeat(newIndent) : '<'.repeat(Math.abs(newIndent)); - Services.logger.debug(`${num}: ${arrows} ${params.text}`) + Services.logger.debug(`${num}: ${arrows} ${params.text}`); return newIndentSafe; } @@ -691,7 +691,7 @@ export class VbaFmtListener extends vbafmtListener { this.indentationKeys[params.line] = params.offset; const num = (params.line + 1).toString().padStart(3, '0'); const arrows = '>'.repeat(params.offset); - Services.logger.debug(`${num}: ${arrows} ${params.text}`) + Services.logger.debug(`${num}: ${arrows} ${params.text}`); if (params.trackMinimumIndent) { this.minumumIndent = params.offset; } @@ -702,7 +702,7 @@ export class VbaFmtListener extends vbafmtListener { */ private rangeText(ctx: ParserRuleContext): string { const r = this.getCtxRange(ctx); - return `[${r.start.line + 1}, ${r.start.character}, ${r.end.line + 1}, ${r.end.character}]` + return `[${r.start.line + 1}, ${r.start.character}, ${r.end.line + 1}, ${r.end.character}]`; } private getCtxRange(ctx: ParserRuleContext): Range { diff --git a/server/src/project/parser/vbaParser.ts b/server/src/project/parser/vbaParser.ts index beed4fb..9c02671 100644 --- a/server/src/project/parser/vbaParser.ts +++ b/server/src/project/parser/vbaParser.ts @@ -14,7 +14,7 @@ export class SyntaxParser { async parseAsync(token: CancellationToken, document: VbaClassDocument | VbaModuleDocument): Promise { // Preparse the document if we find a precompiler statement. - const regexp = new RegExp(/^\s*#If/gmi) + const regexp = new RegExp(/^\s*#If/gmi); let docText = document.textDocument.getText(); if (regexp.test(docText)) { this.logger.debug(`Beginning pre-parse.`); @@ -40,7 +40,7 @@ export class SyntaxParser { const parser = VbaFmtParser.create(document.textDocument.getText(range)); await this.parseDocumentAsync(token, listener, parser); this.logger.debug(`Completed format parse.`); - + return listener; } diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index bf0eeeb..3877655 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -120,10 +120,13 @@ export class Workspace implements IWorkspace { this.logger.info(`Parsed ${this.activeDocument.name}`); this.connection.sendDiagnostics(this.activeDocument.languageServerDiagnostics()); } catch (e) { - // Swallow cancellation exceptions. They're good. We like these. - if (e instanceof ParseCancellationException) { } - else if (e instanceof Error) { this.logger.stack(e); } - else { this.logger.error('Something went wrong.') } + if (e instanceof ParseCancellationException) { + // Swallow cancellation exceptions. They're good. We like these. + } else if (e instanceof Error) { + this.logger.stack(e); + } else { + this.logger.error('Something went wrong.'); + } } this.parseCancellationTokenSource = undefined; @@ -141,11 +144,11 @@ export class Workspace implements IWorkspace { } catch (e) { if (e instanceof ParseCancellationException) { - this.logger.debug('Parse cancelled successfully.') + this.logger.debug('Parse cancelled successfully.'); } else if (e instanceof Error) { this.logger.stack(e); } else { - this.logger.error(`Parse failed: ${e}`) + this.logger.error(`Parse failed: ${e}`); } } @@ -164,7 +167,7 @@ export class Workspace implements IWorkspace { closeDocument(document: TextDocument): void { const projectDocument = this.projectDocuments.get(document.uri); if (!projectDocument) { - Services.logger.warn(`Failed to get document to close: ${document.uri}`) + Services.logger.warn(`Failed to get document to close: ${document.uri}`); return; } @@ -204,12 +207,12 @@ export class Workspace implements IWorkspace { // immediately anyway so no point in it being lazy. May not // even be working as diagnostics will already have been resolved. this.connection.languages.diagnostics.refresh(); - } + }; private getConfiguration = async () => { // Logging here will cause a cyclical crash of the server. return await this.connection.workspace.getConfiguration('vbaLanguageServer'); - } + }; } @@ -336,13 +339,13 @@ class WorkspaceEvents { private async onFoldingRangesAsync(params: FoldingRangeParams, token: CancellationToken): Promise { const logger = Services.logger; - logger.debug('[Event] onFoldingRanges') + logger.debug('[Event] onFoldingRanges'); let document: BaseProjectDocument | undefined; try { document = await this.getParsedProjectDocument(params.textDocument.uri, 0, token); } catch (error) { // Swallow parser cancellations and rethrow anything else. - if (!!(error instanceof ParseCancellationException)) { + if (error instanceof ParseCancellationException) { throw error; } } diff --git a/server/src/server.ts b/server/src/server.ts index 85b79b3..d666330 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -11,7 +11,7 @@ import { } from 'vscode-languageserver/node'; // Dependency Injection -import 'reflect-metadata' +import 'reflect-metadata'; import { Services } from './injection/services'; // Ensures globally available type extensions. @@ -37,7 +37,7 @@ export class LanguageServer implements ILanguageServer { this.configuration = new LanguageServerConfiguration(params); Services.registerWorkspace(Workspace); this.workspace = Services.workspace; - + // Set up the connection result. // Update this to make use of the LSCapabilities data class. const result = new ConnectionInitializeResult(this.configuration.capabilities); @@ -59,7 +59,7 @@ export class LanguageServer implements ILanguageServer { // Ensure we have configuration by getting it if we don't. const ensureConfig = async (): Promise => { if (!this._clientConfiguration) this._clientConfiguration = await getConfig(); - } + }; return (async (): Promise => { await ensureConfig(); @@ -80,7 +80,7 @@ export class LanguageServerConfiguration { // interFileDependencies: false, // workspaceDiagnostics: false // }, - + // Implement soon. codeActionProvider: false, completionProvider: undefined, diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index dc98daf..cea83e3 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,4 +1,4 @@ -export class Dictionary extends Map { +export class Dictionary extends Map { private defaultFactory: (...args: any) => V; constructor(defaultFactory: (...args: any) => V) { @@ -19,13 +19,13 @@ export class Dictionary extends Map { export function isOfType(obj: unknown, property: keyof T, nullable: boolean = true): obj is T { - if (nullable) return (obj as T)[property] !== undefined; + if (nullable) return (obj as T)[property] !== undefined; return ( typeof obj === 'object' && obj !== null && property in obj && (!!(obj as T)[property]) - ) + ); } export function ioEvents(): Promise { @@ -33,5 +33,5 @@ export function ioEvents(): Promise { } export function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms) ); + return new Promise(resolve => setTimeout(resolve, ms)); } \ No newline at end of file diff --git a/server/src/utils/logger.ts b/server/src/utils/logger.ts index 92d3ae7..2a79dd9 100644 --- a/server/src/utils/logger.ts +++ b/server/src/utils/logger.ts @@ -23,21 +23,21 @@ export class LspLogger implements Logger { @inject("_Connection") public connection: _Connection, @inject("ILanguageServer") private server: ILanguageServer) { } - error = (msg: string, lvl?: number) => this.emit(LogLevel.error, msg, lvl) - warn = (msg: string, lvl?: number) => this.emit(LogLevel.warn, msg, lvl) - info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl) - log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl) - debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl) - stack = (e: Error) => this.emit(LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`) + error = (msg: string, lvl?: number) => this.emit(LogLevel.error, msg, lvl); + warn = (msg: string, lvl?: number) => this.emit(LogLevel.warn, msg, lvl); + info = (msg: string, lvl?: number) => this.emit(LogLevel.info, msg, lvl); + log = (msg: string, lvl?: number) => this.emit(LogLevel.log, msg, lvl); + debug = (msg: string, lvl?: number) => this.emit(LogLevel.debug, msg, lvl); + stack = (e: Error) => this.emit(LogLevel.error, `${e.name}: ${e.message}\n${e.stack}`); private emit(logLevel: LogLevel, msgText: string, msgLevel?: number): void { // Async get the configuration and then emit. this.server.clientConfiguration.then(config => { try { // Get the configured log level or default to debug. - const configLevel = !!config + const configLevel = config ? LogLevel[config.logLevel.outputChannel.toLocaleLowerCase() as keyof typeof LogLevel] - : LogLevel.debug + : LogLevel.debug; // Return without sending if log level is too high. if (logLevel > configLevel) @@ -54,7 +54,7 @@ export class LspLogger implements Logger { message: msgText, level: msgLevel ?? 0 }); - }) + }); } private sendMessage = (message: LogMessage) => @@ -67,5 +67,5 @@ export class LspLogger implements Logger { type: LogLevel.error, message: `Unable to convert '${level}' to LogLevel`, level: 0 - }) + }); } \ No newline at end of file diff --git a/server/src/utils/wrappers.ts b/server/src/utils/wrappers.ts index e242203..2c04a32 100644 --- a/server/src/utils/wrappers.ts +++ b/server/src/utils/wrappers.ts @@ -3,28 +3,28 @@ import { LspLogger } from './logger'; import { Logger } from '../injection/interface'; import { Services } from '../injection/services'; -type signature = (token: CancellationToken, ...args: A) => Promise; -type paramsSignature = (params: P, token: CancellationToken) => Promise; +type signature = (token: CancellationToken, ...args: A) => Promise; +type paramsSignature = (params: P, token: CancellationToken) => Promise; /** * A wrapper to give cancellation token handling to an async function. * @param fn An async function that requires cancellation token handling. * @param defaultValue The value to return when cancelled. */ -function returnDefaultOnCancel(fn: signature, logger?: Logger, name?: string, defaultValue?: T, cancelError?: Error): signature { - return async (token: CancellationToken, ...args: A): Promise => { +function returnDefaultOnCancel(fn: signature, logger?: Logger, name?: string, defaultValue?: T, cancelError?: Error): signature { + return async (token: CancellationToken, ...args: A): Promise => { if (token.isCancellationRequested) { - if (logger) logger.debug(`Cancellation requested before start for ${name ?? 'unknown'}. Returning default.`); - if (cancelError) throw cancelError; + if (logger) logger.debug(`Cancellation requested before start for ${name ?? 'unknown'}. Returning default.`); + if (cancelError) throw cancelError; return defaultValue; } - return new Promise((resolve) => { - const onCancel = () =>{ - if (logger) logger.debug(`Cancellation requested during processing for ${name ?? 'unknown'}. Returning default.`); - if (cancelError) throw cancelError; - resolve(defaultValue); - } + return new Promise((resolve) => { + const onCancel = () => { + if (logger) logger.debug(`Cancellation requested during processing for ${name ?? 'unknown'}. Returning default.`); + if (cancelError) throw cancelError; + resolve(defaultValue); + }; token.onCancellationRequested(onCancel); fn(token, ...args).then(resolve).catch(() => resolve(defaultValue)); }); @@ -36,8 +36,8 @@ function returnDefaultOnCancel(fn: signature, logger?: * @param fn An async function that requires cancellation token handling. * @param defaultValue The value to return when cancelled. */ -export function returnDefaultOnCancelClientRequest(fn: paramsSignature, defaultValue: T, name: string): paramsSignature { - return async (params: P, token: CancellationToken): Promise => { +export function returnDefaultOnCancelClientRequest(fn: paramsSignature, defaultValue: T, name: string): paramsSignature { + return async (params: P, token: CancellationToken): Promise => { if (token.isCancellationRequested) { const msg = `Cancellation requested before start for ${name}. Returning default.`; Services.logger.debug(msg); @@ -45,11 +45,11 @@ export function returnDefaultOnCancelClientRequest(fn: paramsSignature((resolve) => { - const onCancel = () =>{ - const msg = `Cancellation requested during processing for ${name}. Returning default.`; + const onCancel = () => { + const msg = `Cancellation requested during processing for ${name}. Returning default.`; Services.logger.debug(msg); - resolve(defaultValue); - } + resolve(defaultValue); + }; token.onCancellationRequested(onCancel); fn(params, token).then(resolve).catch(() => resolve(defaultValue)); token.onCancellationRequested(() => undefined); From 60915794066653d0e17896e4bbf062b6a5e27016 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 09:49:04 +0800 Subject: [PATCH 12/29] Whoopsey! --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c17eaf6..485599f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vba-lsp", - "type": "module", + "type": "commonjs", "displayName": "VBA Pro", "description": "A VBA extension for VSCode with Language Server support", "icon": "images/vba-lsp-icon.png", @@ -223,4 +223,4 @@ "typescript": "^5.8.3", "vscode-tmgrammar-test": "^0.1.3" } -} +} \ No newline at end of file From 2945ade87b2472a7fcc44a7e13c5b8b158996d60 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:07:38 +0800 Subject: [PATCH 13/29] Visibility added to UdtDeclaration --- server/src/project/elements/typing.ts | 26 ++++++++++++++++-------- server/src/project/parser/vbaListener.ts | 8 ++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts index 2b6a984..f2cffc3 100644 --- a/server/src/project/elements/typing.ts +++ b/server/src/project/elements/typing.ts @@ -10,13 +10,13 @@ import { EnumMemberContext, GlobalVariableDeclarationContext, PrivateConstDeclarationContext, - PrivateEnumDeclarationContext, - PrivateTypeDeclarationContext, PrivateVariableDeclarationContext, PublicConstDeclarationContext, + PublicEnumDeclarationContext, PublicTypeDeclarationContext, PublicVariableDeclarationContext, TypeSuffixContext, + UdtDeclarationContext, VariableDclContext, WitheventsVariableDclContext } from '../../antlr/out/vbaParser'; @@ -43,7 +43,11 @@ abstract class BaseTypeDeclarationElement extends B // An enum is public unless explicitly set to private. this.scopeItemCapability = new ScopeItemCapability(this, ItemType.TYPE); - this.scopeItemCapability.isPublicScope = !(ctx.parent instanceof PrivateEnumDeclarationContext); + this.scopeItemCapability.isPublicScope = this.isPublicScope; + } + + protected get isPublicScope(): boolean { + throw new Error('Not implemented'); } } @@ -51,6 +55,10 @@ abstract class BaseTypeDeclarationElement extends B export class EnumDeclarationElement extends BaseTypeDeclarationElement { identifierCapability: IdentifierCapability; + protected override get isPublicScope(): boolean { + return this.context.rule.parent instanceof PublicEnumDeclarationContext; + } + constructor(ctx: EnumDeclarationContext, doc: TextDocument, isAfterProcedure: boolean) { super(ctx, doc, SymbolKind.Enum, SemanticTokenTypes.enum); this.identifierCapability = new IdentifierCapability({ @@ -82,18 +90,18 @@ export class EnumMemberDeclarationElement extends BaseContextSyntaxElement { +export class TypeDeclarationElement extends BaseTypeDeclarationElement { identifierCapability: IdentifierCapability; - private _isPublic: boolean; - get isPublic(): boolean { return this._isPublic; } + protected override get isPublicScope(): boolean { + return this.context.rule.parent instanceof PublicTypeDeclarationContext; + } - constructor(ctx: PublicTypeDeclarationContext | PrivateTypeDeclarationContext, doc: TextDocument, isPublic: boolean) { + constructor(ctx: UdtDeclarationContext, doc: TextDocument) { super(ctx, doc, SymbolKind.Struct, SemanticTokenTypes.struct); - this._isPublic = isPublic; this.identifierCapability = new IdentifierCapability({ element: this, - getNameContext: () => ctx.udtDeclaration().untypedName() + getNameContext: () => ctx.untypedName() }); } } diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index 20b2aae..ae22d20 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -18,14 +18,12 @@ import { IgnoredClassAttrContext, IgnoredProceduralAttrContext, PrivateConstDeclarationContext, - PrivateTypeDeclarationContext, PrivateVariableDeclarationContext, ProceduralModuleContext, ProcedureDeclarationContext, PropertyGetDeclarationContext, PropertySetDeclarationContext, PublicConstDeclarationContext, - PublicTypeDeclarationContext, PublicVariableDeclarationContext, SubroutineDeclarationContext, TypeSuffixContext, @@ -172,10 +170,8 @@ export class VbaListener extends vbaListener { this.document.registerElement(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); + enterUdtDeclaration = (ctx: UdtDeclarationContext) => { + const element = new TypeDeclarationElement(ctx, this.document.textDocument); this.document.registerElement(element); }; From 9f2b6762f82fc315ca485ffef388f5bdf1f61891 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:44:04 +0800 Subject: [PATCH 14/29] Update logging messages --- server/src/capabilities/capabilities.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 6139e8c..618290e 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -279,7 +279,6 @@ export class ScopeItemCapability { combineNames(names, this.functions); combineNames(names, this.subroutines); - const logger = Services.logger; names.forEach((items, name) => { // Base case no name clash. if (items.length <= 1) { @@ -287,7 +286,6 @@ export class ScopeItemCapability { } // Diagnose names. - logger.debug(`Name ${name} is duplicate`); items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); diagnosedNames.set(name, undefined); }); @@ -303,7 +301,6 @@ export class ScopeItemCapability { } // Diagnose properties. - logger.debug(`Property ${items[0].name} is duplicate`); items.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, item.name)); // Don't diagnose names if we have already or don't need to. @@ -312,7 +309,6 @@ export class ScopeItemCapability { } // Diagnose names and register. - logger.debug(`Name ${this.identifier} is duplicate (property)`); nameItems.forEach(item => this.pushDiagnostic(DuplicateDeclarationDiagnostic, item, name)); diagnosedNames.set(this.identifier, undefined); }); @@ -523,7 +519,7 @@ export class ScopeItemCapability { const getAncestorLevel = (item: ScopeItemCapability, level: number): number => item.parent ? getAncestorLevel(item.parent, level + 1) : level; const ancestorLevel = getAncestorLevel(this, 0); - Services.logger.debug(`Registering [${ItemType[item.type]}] ${item.name}`, ancestorLevel); + Services.logger.debug(`Registering [${item.isPublicScope ? 'public' : 'private'} ${ItemType[item.type]}] ${item.name}`, ancestorLevel); // Reference types are not declarations. if (item.type === ItemType.REFERENCE) { From 4d438bec2e34d222b17ef9ab6aa5cf89ac220b92 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:44:25 +0800 Subject: [PATCH 15/29] Method visibility support --- server/src/capabilities/capabilities.ts | 4 ++-- server/src/project/elements/procedure.ts | 28 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 618290e..7e7bab9 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -211,8 +211,8 @@ export class ScopeItemCapability { constructor( readonly element?: BaseContextSyntaxElement, - readonly type: ItemType = ItemType.REFERENCE, - readonly assignmentType: AssignmentType = AssignmentType.NONE, + public type: ItemType = ItemType.REFERENCE, + public assignmentType: AssignmentType = AssignmentType.NONE, public parent?: ScopeItemCapability, ) { } diff --git a/server/src/project/elements/procedure.ts b/server/src/project/elements/procedure.ts index 108a0d5..d5b30e2 100644 --- a/server/src/project/elements/procedure.ts +++ b/server/src/project/elements/procedure.ts @@ -7,6 +7,7 @@ import { ParserRuleContext } from 'antlr4ng'; import { AmbiguousIdentifierContext, FunctionDeclarationContext, + ProcedureScopeContext, PropertyGetDeclarationContext, PropertySetDeclarationContext, SubroutineDeclarationContext @@ -16,11 +17,15 @@ import { import { BaseContextSyntaxElement, HasDiagnosticCapability, HasSymbolInformationCapability } from './base'; import { AssignmentType, DiagnosticCapability, FoldingRangeCapability, IdentifierCapability, ItemType, ScopeItemCapability, SymbolInformationCapability } from '../../capabilities/capabilities'; +interface HasProcedureScope { + procedureScope(): ProcedureScopeContext | null +} -abstract class BaseProcedureElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability { +abstract class BaseProcedureElement extends BaseContextSyntaxElement implements HasDiagnosticCapability, HasSymbolInformationCapability { diagnosticCapability: DiagnosticCapability; foldingRangeCapability: FoldingRangeCapability; symbolInformationCapability: SymbolInformationCapability; + scopeItemCapability: ScopeItemCapability; abstract identifierCapability: IdentifierCapability; constructor(ctx: T, doc: TextDocument, symbolKind: SymbolKind) { @@ -28,6 +33,12 @@ abstract class BaseProcedureElement extends BaseCon this.diagnosticCapability = new DiagnosticCapability(this); this.foldingRangeCapability = new FoldingRangeCapability(this); this.symbolInformationCapability = new SymbolInformationCapability(this, symbolKind); + this.scopeItemCapability = new ScopeItemCapability(this); + this.scopeItemCapability.isPublicScope = this.isPublicScope; + } + + protected get isPublicScope(): boolean { + return !this.context.rule.procedureScope()?.PRIVATE(); } // ToDo: @@ -49,7 +60,7 @@ export class SubDeclarationElement extends BaseProcedureElement extends BaseProcedureElement { +abstract class BasePropertyDeclarationElement extends BaseProcedureElement { identifierCapability: IdentifierCapability; private propertyType: string; @@ -116,7 +127,8 @@ export class PropertyGetDeclarationElement extends BasePropertyDeclarationElemen super(ctx, doc, 'Get', ctx.functionName()?.ambiguousIdentifier() ?? undefined); this.foldingRangeCapability.openWord = `Get Property ${this.identifierCapability.name}`; this.foldingRangeCapability.closeWord = 'End Property'; - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.PROPERTY, AssignmentType.GET); + this.scopeItemCapability.type = ItemType.PROPERTY; + this.scopeItemCapability.assignmentType = AssignmentType.GET; } } @@ -126,7 +138,8 @@ export class PropertySetDeclarationElement extends BasePropertyDeclarationElemen super(ctx, doc, 'Set', ctx.subroutineName()?.ambiguousIdentifier() ?? undefined); this.foldingRangeCapability.openWord = `Set Property ${this.identifierCapability.name}`; this.foldingRangeCapability.closeWord = 'End Property'; - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.PROPERTY, AssignmentType.SET); + this.scopeItemCapability.type = ItemType.PROPERTY; + this.scopeItemCapability.assignmentType = AssignmentType.SET; } } @@ -136,6 +149,7 @@ export class PropertyLetDeclarationElement extends BasePropertyDeclarationElemen super(ctx, doc, 'Let', ctx.subroutineName()?.ambiguousIdentifier() ?? undefined); this.foldingRangeCapability.openWord = `Let Property ${this.identifierCapability.name}`; this.foldingRangeCapability.closeWord = 'End Property'; - this.scopeItemCapability = new ScopeItemCapability(this, ItemType.PROPERTY, AssignmentType.LET); + this.scopeItemCapability.type = ItemType.PROPERTY; + this.scopeItemCapability.assignmentType = AssignmentType.LET; } } \ No newline at end of file From 832e5e95916a471389a9f53d4bcac6654f1ca3b2 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:14:08 +0800 Subject: [PATCH 16/29] Fixes #61 --- server/src/antlr/vba.g4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index 1000bac..a8a7e92 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -2742,8 +2742,8 @@ INTEGERLITERAL ; FLOATLITERAL - : FLOATINGPOINTLITERAL [!#@]? - | DECIMALLITERAL [!#@] + : MINUS? FLOATINGPOINTLITERAL [!#@]? + | MINUS? DECIMALLITERAL [!#@] ; fragment FLOATINGPOINTLITERAL From 70b7b6a87df383c22b7a64ce5971b60b1c0225f6 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Thu, 10 Apr 2025 23:23:17 +0800 Subject: [PATCH 17/29] Fixes #60 --- client/syntaxes/vba.tmLanguage.json | 6 +- client/syntaxes/vba.tmLanguage.yaml | 6 +- test/textmate/unit/logicFlowBlock.vba | 10 +++- test/textmate/unit/methodSignatures.vba | 76 ++++++++++++------------- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/client/syntaxes/vba.tmLanguage.json b/client/syntaxes/vba.tmLanguage.json index 4c8877f..214a9ca 100644 --- a/client/syntaxes/vba.tmLanguage.json +++ b/client/syntaxes/vba.tmLanguage.json @@ -498,7 +498,7 @@ "repository": { "onErrorStatement": { "name": "meta.onErrorStatement.vba", - "match": "(?i)\\b(on\\s+error)\\s+(?:(resume\\s+next)|(goto)\\s+([a-z0-9]+))", + "match": "(?i)\\b(on\\s+error)\\s+(?:(resume\\s+next)|(goto)\\s+([a-z0-9_]+))", "captures": { "1": { "name": "keyword.control.flow.jump.vba" @@ -549,7 +549,7 @@ "include": "#literals" }, { - "match": "(?i)([a-z][a-z0-9]*)", + "match": "(?i)([a-z][a-z0-9_]*)", "name": "variable.other.constant.label.vba" } ] @@ -819,7 +819,7 @@ }, "argumentSignatureFromParam": { "name": "meta.argument-signature.param.vba", - "begin": "(?i)[a-z][a-z0-9]*(\\(\\))?", + "begin": "(?i)[a-z][a-z0-9_]*(\\(\\))?", "end": "(?=[,):'\\n])", "beginCaptures": { "0": { diff --git a/client/syntaxes/vba.tmLanguage.yaml b/client/syntaxes/vba.tmLanguage.yaml index d138d34..2ba07ae 100644 --- a/client/syntaxes/vba.tmLanguage.yaml +++ b/client/syntaxes/vba.tmLanguage.yaml @@ -324,7 +324,7 @@ repository: repository: onErrorStatement: name: meta.onErrorStatement.vba - match: (?i)\b(on\s+error)\s+(?:(resume\s+next)|(goto)\s+([a-z0-9]+)) + match: (?i)\b(on\s+error)\s+(?:(resume\s+next)|(goto)\s+([a-z0-9_]+)) captures: 1: name: keyword.control.flow.jump.vba @@ -353,7 +353,7 @@ repository: patterns: - include: "#separator" - include: "#literals" - - match: (?i)([a-z][a-z0-9]*) + - match: (?i)([a-z][a-z0-9_]*) name: variable.other.constant.label.vba goToGoSubReturnStatement: name: meta.goToGoSubReturnStatement.vba @@ -512,7 +512,7 @@ repository: argumentSignatureFromParam: name: meta.argument-signature.param.vba - begin: (?i)[a-z][a-z0-9]*(\(\))? + begin: (?i)[a-z][a-z0-9_]*(\(\))? end: (?=[,):'\n]) beginCaptures: 0: diff --git a/test/textmate/unit/logicFlowBlock.vba b/test/textmate/unit/logicFlowBlock.vba index c443626..f3405c2 100644 --- a/test/textmate/unit/logicFlowBlock.vba +++ b/test/textmate/unit/logicFlowBlock.vba @@ -111,10 +111,18 @@ Public Sub Foo() End If ' ^^^ ^^ meta.block-if-else.vba keyword.control.block-if.close.vba + On Error GoTo Some_Label +' ^^^^^^^^^^^^^^^^^^^^^^^^ meta.onErrorStatement.vba +' ^^ ^^^^^ ^^^^ keyword.control.flow.jump.vba +' ^^^^^^^^^^ variable.other.constant.label.vba +100: +'<--- variable.other.constant.label.vba +Some_Label: +'<---------- variable.other.constant.label.vba + Select Case True ' ^^^^^^ ^^^^ meta.flow.switch.vba keyword.control.flow.switch.vba ' ^^^^ meta.flow.switch.vba meta.expression.vba -100: Case Is = x = y: ' ^^^^ keyword.control.flow.switch.vba ' ^^^^^^^^^^ meta.flow.switch.vba meta.expression.vba diff --git a/test/textmate/unit/methodSignatures.vba b/test/textmate/unit/methodSignatures.vba index 4ae44f1..d3fbf14 100644 --- a/test/textmate/unit/methodSignatures.vba +++ b/test/textmate/unit/methodSignatures.vba @@ -8,24 +8,24 @@ Sub Foo(): Debug.Print "Hello, World!": End Sub ' ^^^^^^^ storage.type.method.close.vba ' Standard sub -Public Sub Foo(ByVal x As Long, ByRef y As Long, ParamArray vars() As Variant) -' <------------------------------------------------------------------------------ source.method.signature.vba -' <---------- storage.type.method.vba -' ^^^ entity.name.function.vba -' ^^^^^ storage.modifier.ByVal.vba -' ^ variable.parameter.vba -' ^^ keyword.control.as.vba -' ^^^^ support.type.primitive.Long.vba -' ^ punctuation.separator.vba -' ^^^^^ storage.modifier.ByRef.vba -' ^ variable.parameter.vba -' ^^ keyword.control.as.vba -' ^^^^ support.type.primitive.Long.vba -' ^ punctuation.separator.vba -' ^^^^^^^^^^ storage.type.modifier.vba -' ^^^^ variable.parameter.vba -' ^^ keyword.control.as.vba -' ^^^^^^^ support.type.primitive.Variant.vba +Public Sub Foo(ByVal x As Long, ByRef underscore_param As Long, ParamArray vars() As Variant) +' <--------------------------------------------------------------------------------------------- source.method.signature.vba +' <---------- storage.type.method.vba +' ^^^ entity.name.function.vba +' ^^^^^ storage.modifier.ByVal.vba +' ^ variable.parameter.vba +' ^^ keyword.control.as.vba +' ^^^^ support.type.primitive.Long.vba +' ^ punctuation.separator.vba +' ^^^^^ storage.modifier.ByRef.vba +' ^^^^^^^^^^^^^^^^ variable.parameter.vba +' ^^ keyword.control.as.vba +' ^^^^ support.type.primitive.Long.vba +' ^ punctuation.separator.vba +' ^^^^^^^^^^ storage.type.modifier.vba +' ^^^^ variable.parameter.vba +' ^^ keyword.control.as.vba +' ^^^^^^^ support.type.primitive.Variant.vba End Sub @@ -39,26 +39,26 @@ Function Foo() As String: Foo = "Hello, World!": End Function ' ^^^^^^^^^^^^ storage.type.method.close.vba ' Standard function -Public Function Foo(ByVal x As Long, ByRef y As Long, ParamArray vars() As Variant) As Object -' <--------------------------------------------------------------------------------------------- source.method.signature.vba -' <--------------- storage.type.method.vba -' ^^^ entity.name.function.vba -' ^^^^^ storage.modifier.ByVal.vba -' ^ variable.parameter.vba -' ^^ keyword.control.as.vba -' ^^^^ support.type.primitive.Long.vba -' ^ punctuation.separator.vba -' ^^^^^ storage.modifier.ByRef.vba -' ^ variable.parameter.vba -' ^^ keyword.control.as.vba -' ^^^^ support.type.primitive.Long.vba -' ^ punctuation.separator.vba -' ^^^^^^^^^^ storage.type.modifier.vba -' ^^^^ variable.parameter.vba -' ^^ keyword.control.as.vba -' ^^^^^^^ support.type.primitive.Variant.vba -' ^^ keyword.control.as.vba -' ^^^^^^ support.type.object.Object.vba +Public Function Foo(ByVal x As Long, ByRef underscore_param As Long, ParamArray vars() As Variant) As Object +' <-----------------------------------------^^^^^^^^^^^^^^^---------------------------------------------------- source.method.signature.vba +' <--------------- storage.type.method.vba +' ^^^ entity.name.function.vba +' ^^^^^ storage.modifier.ByVal.vba +' ^ variable.parameter.vba +' ^^ keyword.control.as.vba +' ^^^^ support.type.primitive.Long.vba +' ^ punctuation.separator.vba +' ^^^^^ storage.modifier.ByRef.vba +' ^^^^^^^^^^^^^^^^ variable.parameter.vba +' ^^ keyword.control.as.vba +' ^^^^ support.type.primitive.Long.vba +' ^ punctuation.separator.vba +' ^^^^^^^^^^ storage.type.modifier.vba +' ^^^^ variable.parameter.vba +' ^^ keyword.control.as.vba +' ^^^^^^^ support.type.primitive.Variant.vba +' ^^ keyword.control.as.vba +' ^^^^^^ support.type.object.Object.vba End Function '<------------- storage.type.method.close.vba From 53e621bf76e5bbf61e0689a9a2a9601c3dcf85d2 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 19:16:31 +0800 Subject: [PATCH 18/29] Fixes #61 --- server/src/antlr/vba.g4 | 51 +++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index a8a7e92..de634c0 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -42,7 +42,7 @@ classBeginBlock ; beginBlockConfigElement - : endOfLine+ (OBJECT '.')? '_'? ambiguousIdentifier WS? eqOperator WS? (('-'? literalExpression) | FILEOFFSET) + : endOfLine+ (OBJECT '.')? '_'? ambiguousIdentifier WS? eqOperator WS? (expression | FILEOFFSET) | formBeginBlock | beginPropertyBlock ; @@ -1584,86 +1584,85 @@ anyOperator ; powOperator - : POW (wsc? anyOperator)* + : POW (anyOperator)* ; divOperator - : DIV (wsc? anyOperator)* + : DIV (anyOperator)* ; multOperator - : MULT (wsc? anyOperator)* + : MULT (anyOperator)* ; modOperator - : MOD (wsc? anyOperator)* + : MOD (anyOperator)* ; plusOperator - : PLUS (wsc? anyOperator)* + : PLUS (anyOperator)* ; minusOperator - : MINUS (wsc? anyOperator)* + : MINUS (anyOperator)* ; ampOperator - : AMPERSAND (wsc? anyOperator)* + : AMPERSAND (anyOperator)* ; isOperator - : IS (wsc? anyOperator)* + : IS (anyOperator)* ; likeOperator - : LIKE (wsc? anyOperator)* + : LIKE (anyOperator)* ; geqOperator - : GEQ (wsc? anyOperator)* + : GEQ (anyOperator)* ; leqOperator - : LEQ (wsc? anyOperator)* + : LEQ (anyOperator)* ; gtOperator - : GT (wsc? anyOperator)* + : GT (anyOperator)* ; ltOperator - : LT (wsc? anyOperator)* + : LT (anyOperator)* ; neqOperator - : NEQ (wsc? anyOperator)* + : NEQ (anyOperator)* ; eqOperator - : EQ (wsc? anyOperator)* + : EQ (anyOperator)* ; andOperator - : AND (wsc? anyOperator)* + : AND (anyOperator)* ; orOperator - : OR (wsc? anyOperator)* + : OR (anyOperator)* ; xorOperator - : XOR (wsc? anyOperator)* + : XOR (anyOperator)* ; eqvOperator - : EQV (wsc? anyOperator)* + : EQV (anyOperator)* ; impOperator - : IMP (wsc? anyOperator)* + : IMP (anyOperator)* ; - // keywords ABS : 'ABS' @@ -2736,14 +2735,12 @@ STRINGLITERAL ; INTEGERLITERAL - : MINUS? (DIGIT DIGIT* - | '&H' [0-9A-F]+ - | '&' [O]? [0-7]+) [%&^]? + : (DIGIT DIGIT* | '&H' [0-9A-F]+ | '&' [O]? [0-7]+) [%&^]? ; FLOATLITERAL - : MINUS? FLOATINGPOINTLITERAL [!#@]? - | MINUS? DECIMALLITERAL [!#@] + : FLOATINGPOINTLITERAL [!#@]? + | DECIMALLITERAL [!#@] ; fragment FLOATINGPOINTLITERAL From 97c59f5e8e04f1b753c1f7064c786cc8e07a8ea3 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 21:44:29 +0800 Subject: [PATCH 19/29] Decimal in standard vba --- server/src/antlr/vba.g4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index de634c0..4137fc8 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -1389,6 +1389,7 @@ reservedTypeIdentifier | BYTE | CURRENCY | DATE + | DECIMAL | DOUBLE | INTEGER | LONG @@ -1466,7 +1467,6 @@ reservedForImplementationUse ; futureReserved : CDECL - | DECIMAL | DEFDEC ; From 00686221f74ba210ec81473940a1c3a45b72f4bc Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:22:17 +0800 Subject: [PATCH 20/29] ToDo: Validate attributes --- server/src/project/elements/procedure.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/project/elements/procedure.ts b/server/src/project/elements/procedure.ts index d5b30e2..b91d97c 100644 --- a/server/src/project/elements/procedure.ts +++ b/server/src/project/elements/procedure.ts @@ -6,6 +6,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument'; import { ParserRuleContext } from 'antlr4ng'; import { AmbiguousIdentifierContext, + AttributeStatementContext, FunctionDeclarationContext, ProcedureScopeContext, PropertyGetDeclarationContext, @@ -26,6 +27,7 @@ abstract class BaseProcedureElement { identifierCapability: IdentifierCapability; From c89698fcfea7b1da409a9c8382b79d8eea657183 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:52:51 +0800 Subject: [PATCH 21/29] Test requires unique names --- test/fixtures/Diagnostics.cls | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/fixtures/Diagnostics.cls b/test/fixtures/Diagnostics.cls index 70ec3a7..0c5f3c3 100644 --- a/test/fixtures/Diagnostics.cls +++ b/test/fixtures/Diagnostics.cls @@ -2,7 +2,7 @@ VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END -Attribute VB_Name = "Diagnostics" +Attribute VB_Name = "DiagnosticsClassTest" Attribute VB_Description = "Class description goes here" Attribute VB_GlobalNameSpace = False Attribute VB_GlobalNameSpace = False @@ -14,7 +14,7 @@ Attribute VB_Exxposed = False Option Explicit -Public Sub Foo() +Public Sub SmkfeiondFoo() Dim i As Long While True i = i ++ 1 @@ -22,9 +22,9 @@ Public Sub Foo() Wend End Sub -Sub Identifier() -Attribute Identifier.VB_Description = "Dosctring." -Attribute Identifier.VB_Description = "Dosctring." +Sub GkiofseiFoo() +Attribute GkiofseiFoo.VB_Description = "Dosctring." +Attribute GkiofseiFoo.VB_Description = "Dosctring." ' Dosctring. ' ' Args: @@ -34,7 +34,7 @@ Attribute Identifier.VB_Description = "Dosctring." ' End Sub -Public Enum EnumFoo +Public Enum PewmfoiawFoo Enum1 Enum2 Enum3 From 44457f0bdf37a3e448f5cb72c03993fcf8941e5e Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:53:11 +0800 Subject: [PATCH 22/29] Auto format --- server/src/antlr/vba.g4 | 1307 +++++++++++++++++++++++++++++---------- 1 file changed, 980 insertions(+), 327 deletions(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index 4137fc8..d015d38 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -1,9 +1,9 @@ /* -* Visual Basic 7.1 Grammar for ANTLR4 -* -* Derived from the Visual Basic 7.1 language reference -* https://msopenspecs.azureedge.net/files/MS-VBAL/%5bMS-VBAL%5d.pdf -*/ + * Visual Basic 7.1 Grammar for ANTLR4 + + Derived from the Visual Basic 7.1 language reference + * https://msopenspecs.azureedge.net/files/MS-VBAL/%5bMS-VBAL%5d.pdf + */ // $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false // $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging @@ -23,10 +23,10 @@ startRule // Added form file entry module : (endOfLine | endOfLineNoWs)* ( - proceduralModule + proceduralModule | classFileHeader classModule | formFileHeader classModule - ) endOfLine* WS? + ) endOfLine* WS? ; classFileHeader @@ -55,12 +55,15 @@ formFileHeader formVersionIdentification : VERSION WS FLOATLITERAL ; + formObjectAssign : endOfLine+ OBJECT WS? eqOperator WS? STRINGLITERAL (';' WS? STRINGLITERAL)? ; + formBeginBlock : endOfLine+ BEGIN WS (GUID | (ambiguousIdentifier '.' ambiguousIdentifier)) WS ambiguousIdentifier beginBlockConfigElement+ endOfLine+ END ; + beginPropertyBlock : endOfLine+ BEGINPROPERTY WS ambiguousIdentifier (WS GUID WS?)? beginBlockConfigElement+ endOfLine+ ENDPROPERTY ; @@ -70,6 +73,7 @@ beginPropertyBlock proceduralModule : proceduralModuleHeader (endOfLine | endOfLineNoWs)* proceduralModuleBody ; + classModule : classModuleHeader endOfLine* classModuleBody ; @@ -89,7 +93,9 @@ ignoredProceduralAttr | ignoredAttr ; -classModuleHeader: (endOfLine+ (classAttr | nameAttr | ignoredClassAttr))* WS?; +classModuleHeader + : (endOfLine+ (classAttr | nameAttr | ignoredClassAttr))* WS? + ; // VBA Library Projects are allowed to have GoobalNamespace and creatable as true. classAttr @@ -111,13 +117,19 @@ ignoredAttr nameAttr : ATTRIBUTE WS? VB_NAME WS? eqOperator WS? STRINGLITERAL - ; + ; //--------------------------------------------------------------------------------------- // 5.1 Module Body Structure // Everything from here down is user generated code. -proceduralModuleBody: proceduralModuleCode; -classModuleBody: classModuleCode; +proceduralModuleBody + : proceduralModuleCode + ; + +classModuleBody + : classModuleCode + ; + unrestrictedName : reservedIdentifier | name @@ -129,6 +141,7 @@ name | typedName | markedFileNumber ; + untypedName : ambiguousIdentifier | FOREIGN_NAME @@ -148,18 +161,21 @@ proceduralModuleDirectiveElement : optionPrivateDirective | defDirective ; + proceduralModuleDeclarationElement : commonModuleDeclarationElement - | globalVariableDeclaration - | publicConstDeclaration + // | publicConstDeclaration // ToDo: relax this to common and have the diagnostic handled by LSP if we're not in a module. + | constDeclaration | publicExternalProcedureDeclaration | globalEnumDeclaration | optionPrivateDirective ; + classModuleDirectiveElement : defDirective | implementsDirective ; + classModuleDeclarationElement : commonModuleDeclarationElement | eventDeclaration @@ -175,32 +191,65 @@ commonOptionDirective ; // 5.2.1.1 Option Compare Directive -optionCompareDirective: OPTION wsc COMPARE wsc (BINARY | TEXT); +optionCompareDirective + : OPTION wsc COMPARE wsc (BINARY | TEXT) + ; // 5.2.1.2 Option Base Directive // INTEGER or SHORT? -optionBaseDirective: OPTION wsc BASE wsc INTEGERLITERAL; +optionBaseDirective + : OPTION wsc BASE wsc INTEGERLITERAL + ; // 5.2.1.3 Option Explicit Directive -optionExplicitDirective: OPTION wsc EXPLICIT; +optionExplicitDirective + : OPTION wsc EXPLICIT + ; // 5.2.1.4 Option Private Directive -optionPrivateDirective: OPTION wsc PRIVATE wsc MODULE; +optionPrivateDirective + : OPTION wsc PRIVATE wsc MODULE + ; // 5.2.2 Implicit Definition Directives -defDirective: defType WS letterSpec (WS ',' WS letterSpec)*; +defDirective + : defType WS letterSpec (WS ',' WS letterSpec)* + ; + letterSpec : singleLetter | universalLetterRange | letterRange ; -singleLetter: ambiguousIdentifier; -universalLetterRange: upperCaseA WS '-' WS upperCaseZ; -upperCaseA: ambiguousIdentifier; -upperCaseZ: ambiguousIdentifier; -letterRange: firstLetter WS '-' WS lastLetter; -firstLetter: ambiguousIdentifier; -lastLetter: ambiguousIdentifier; + +singleLetter + : ambiguousIdentifier + ; + +universalLetterRange + : upperCaseA WS '-' WS upperCaseZ + ; + +upperCaseA + : ambiguousIdentifier + ; + +upperCaseZ + : ambiguousIdentifier + ; + +letterRange + : firstLetter WS '-' WS lastLetter + ; + +firstLetter + : ambiguousIdentifier + ; + +lastLetter + : ambiguousIdentifier + ; + defType : DEFBOOL | DEFBYTE @@ -221,8 +270,9 @@ defType // 5.2.3 Module Declarations // added public-type to fix bug commonModuleDeclarationElement - : moduleVariableDeclaration - | privateConstDeclaration + : variableDeclaration + | variableHelpAttribute + | constDeclaration | privateTypeDeclaration | publicTypeDeclaration | privateEnumDeclaration @@ -231,98 +281,216 @@ commonModuleDeclarationElement | privateExternalProcedureDeclaration ; -// 5.2.3.1 Module Variable Declaration Lists -// Added variableHelpAttribute, not in MS-VBAL -moduleVariableDeclaration - : publicVariableDeclaration - | privateVariableDeclaration - | variableHelpAttribute +// ---------------------------- +// 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. +// ---------------------------- + +variableDeclaration + : (DIM | variableModifier) variableSharedModifier? WS ( + variableDeclarationList + | moduleVariableDeclarationList + ) variableHelpAttribute? ; 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. -// ---------------------------- +variableModifier + : GLOBAL + | PUBLIC + | PRIVATE + | STATIC + ; + +variableSharedModifier + : WS SHARED + ; + +// globalVariableDeclaration +// : GLOBAL WS variableDeclarationList +// ; + +// publicVariableDeclaration +// : PUBLIC (WS SHARED)? WS moduleVariableDeclarationList +// ; + +// privateVariableDeclaration +// : ((PRIVATE | DIM) wsc) (SHARED wsc)? moduleVariableDeclarationList +// ; -globalVariableDeclaration: GLOBAL WS variableDeclarationList; -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)*; +moduleVariableDeclarationList + : (witheventsVariableDcl | variableDcl) (wsc? ',' wsc? (witheventsVariableDcl | variableDcl))* + ; + +variableDeclarationList + : variableDcl (wsc? ',' wsc? variableDcl)* + ; // 5.2.3.1.1 Variable Declarations variableDcl : typedVariableDcl | untypedVariableDcl ; -typedVariableDcl: typedName wsc? arrayDim?; -untypedVariableDcl: ambiguousIdentifier wsc? (arrayClause | asClause)?; -arrayClause: arrayDim (wsc asClause)?; + +typedVariableDcl + : typedName wsc? arrayDim? + ; + +untypedVariableDcl + : ambiguousIdentifier wsc? (arrayClause | asClause)? + ; + +arrayClause + : arrayDim (wsc asClause)? + ; + asClause : asAutoObject | asType ; // 5.2.3.1.2 WithEvents Variable Declarations -witheventsVariableDcl: WITHEVENTS wsc ambiguousIdentifier wsc AS wsc? classTypeName; -classTypeName: definedTypeExpression; +witheventsVariableDcl + : WITHEVENTS wsc ambiguousIdentifier wsc AS wsc? classTypeName + ; + +classTypeName + : definedTypeExpression + ; // 5.2.3.1.3 Array Dimensions and Bounds -arrayDim: '(' wsc? boundsList? wsc? ')'; -boundsList: dimSpec (wsc? ',' wsc? dimSpec)*; -dimSpec: lowerBound? wsc? upperBound; -lowerBound: constantExpression wsc TO wsc; -upperBound: constantExpression; +arrayDim + : '(' wsc? boundsList? wsc? ')' + ; + +boundsList + : dimSpec (wsc? ',' wsc? dimSpec)* + ; + +dimSpec + : lowerBound? wsc? upperBound + ; + +lowerBound + : constantExpression wsc TO wsc + ; + +upperBound + : constantExpression + ; // 5.2.3.1.4 Variable Type Declarations -asAutoObject: AS WS NEW WS classTypeName; -asType: AS WS typeSpec; +asAutoObject + : AS WS NEW WS classTypeName + ; + +asType + : AS WS typeSpec + ; + typeSpec : fixedLengthStringSpec | typeExpression ; -fixedLengthStringSpec: STRING WS '*' WS stringLength; + +fixedLengthStringSpec + : STRING WS '*' WS stringLength + ; + stringLength : INTEGERLITERAL | constantName ; -constantName: simpleNameExpression; + +constantName + : simpleNameExpression + ; // 5.2.3.2 Const Declarations -publicConstDeclaration: (GLOBAL | PUBLIC) wsc moduleConstDeclaration; -privateConstDeclaration: (PRIVATE wsc)? moduleConstDeclaration; -moduleConstDeclaration: constDeclaration; -constDeclaration: CONST wsc constItemList; -constItemList: constItem (wsc? ',' wsc? constItem)*; +// publicConstDeclaration +// : (GLOBAL | PUBLIC) wsc moduleConstDeclaration +// ; + +// privateConstDeclaration +// : (PRIVATE wsc)? moduleConstDeclaration +// ; + +// moduleConstDeclaration +// : constDeclaration +// ; + +// constDeclaration +// : CONST wsc constItemList +// ; + +constDeclaration + : variableModifier? CONST wsc constItemList + ; + +constItemList + : constItem (wsc? ',' wsc? constItem)* + ; + constItem - : typedNameConstItem - | untypedNameConstItem + : ambiguousIdentifier typeSuffix? (wsc constAsClause)? wsc? eqOperator wsc? constantExpression + // | typedNameConstItem + // | untypedNameConstItem + ; + +// typedNameConstItem +// : typedName wsc? eqOperator wsc? constantExpression +// ; + +// untypedNameConstItem +// : ambiguousIdentifier (wsc constAsClause)? wsc? eqOperator wsc? constantExpression +// ; + +constAsClause + : AS wsc builtinType ; -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 -publicTypeDeclaration: ((GLOBAL | PUBLIC) wsc)? udtDeclaration; -privateTypeDeclaration: PRIVATE wsc udtDeclaration; -udtDeclaration: TYPE wsc untypedName endOfStatement+ udtMemberList endOfStatement+ END wsc TYPE; -udtMemberList: udtElement (endOfStatement udtElement)*; +publicTypeDeclaration + : ((GLOBAL | PUBLIC) wsc)? udtDeclaration + ; + +privateTypeDeclaration + : PRIVATE wsc udtDeclaration + ; + +udtDeclaration + : TYPE wsc untypedName endOfStatement+ udtMemberList endOfStatement+ END wsc TYPE + ; + +udtMemberList + : udtElement (endOfStatement udtElement)* + ; + udtElement : remStatement | udtMember ; + udtMember : reservedNameMemberDcl | untypedNameMemberDcl ; -untypedNameMemberDcl: ambiguousIdentifier optionalArrayClause; -reservedNameMemberDcl: reservedMemberName wsc asClause; -optionalArrayClause: arrayDim? wsc asClause; + +untypedNameMemberDcl + : ambiguousIdentifier optionalArrayClause + ; + +reservedNameMemberDcl + : reservedMemberName wsc asClause + ; + +optionalArrayClause + : arrayDim? wsc asClause + ; + reservedMemberName : statementKeyword | markerKeyword @@ -335,43 +503,102 @@ reservedMemberName ; // 5.2.3.4 Enum Declarations -globalEnumDeclaration: GLOBAL wsc enumDeclaration; -publicEnumDeclaration: (PUBLIC wsc)? enumDeclaration; -privateEnumDeclaration: PRIVATE wsc enumDeclaration; -enumDeclaration: ENUM wsc untypedName endOfStatement+ enumMemberList endOfStatement+ END wsc ENUM; -enumLongptrDeclaration: PRIVATE wsc ENUM wsc LONGPTR endOfStatement+ enumMemberList endOfStatement+ END wsc ENUM; -enumMemberList: enumElement (endOfStatement enumElement)*; +globalEnumDeclaration + : GLOBAL wsc enumDeclaration + ; + +publicEnumDeclaration + : (PUBLIC wsc)? enumDeclaration + ; + +privateEnumDeclaration + : PRIVATE wsc enumDeclaration + ; + +enumDeclaration + : ENUM wsc untypedName endOfStatement+ enumMemberList endOfStatement+ END wsc ENUM + ; + +enumLongptrDeclaration + : PRIVATE wsc ENUM wsc LONGPTR endOfStatement+ enumMemberList endOfStatement+ END wsc ENUM + ; + +enumMemberList + : enumElement (endOfStatement enumElement)* + ; + enumElement : remStatement | enumMember ; -enumMember: untypedName (wsc? eqOperator wsc? constantExpression)?; + +enumMember + : untypedName (wsc? eqOperator wsc? constantExpression)? + ; // 5.2.3.5 External Procedure Declaration -publicExternalProcedureDeclaration: (PUBLIC wsc)? externalProcDcl; -privateExternalProcedureDeclaration: PRIVATE wsc externalProcDcl; -externalProcDcl: DECLARE wsc (PTRSAFE wsc)? (externalSub | externalFunction); -externalSub: SUB wsc subroutineName wsc libInfo (wsc procedureParameters)?; -externalFunction: FUNCTION wsc functionName wsc libInfo (wsc procedureParameters)? (wsc functionType)?; -libInfo: libClause (wsc aliasClause)?; -libClause: LIB wsc STRINGLITERAL; -aliasClause: ALIAS wsc STRINGLITERAL; +publicExternalProcedureDeclaration + : (PUBLIC wsc)? externalProcDcl + ; + +privateExternalProcedureDeclaration + : PRIVATE wsc externalProcDcl + ; + +externalProcDcl + : DECLARE wsc (PTRSAFE wsc)? (externalSub | externalFunction) + ; + +externalSub + : SUB wsc subroutineName wsc libInfo (wsc procedureParameters)? + ; + +externalFunction + : FUNCTION wsc functionName wsc libInfo (wsc procedureParameters)? (wsc functionType)? + ; + +libInfo + : libClause (wsc aliasClause)? + ; + +libClause + : LIB wsc STRINGLITERAL + ; + +aliasClause + : ALIAS wsc STRINGLITERAL + ; // 5.2.4 Class Module Declarations // 5.2.4.2 Implements Directive -implementsDirective: IMPLEMENTS WS classTypeName; +implementsDirective + : IMPLEMENTS WS classTypeName + ; // 5.2.4.3 Event Declaration -eventDeclaration: PUBLIC? wsc EVENT wsc ambiguousIdentifier eventParameterList?; -eventParameterList: '(' wsc? positionalParameters? wsc? ')'; +eventDeclaration + : PUBLIC? wsc EVENT wsc ambiguousIdentifier eventParameterList? + ; +eventParameterList + : '(' wsc? positionalParameters? wsc? ')' + ; //--------------------------------------------------------------------------------------- // 5.3 Module Code Section Structure // removed an EOS -proceduralModuleCode: (proceduralModuleCodeElement endOfLine*)*; -classModuleCode: (classModuleCodeElement endOfLine*)*; -proceduralModuleCodeElement: commonModuleCodeElement; +proceduralModuleCode + : (proceduralModuleCodeElement endOfLine*)* + ; + +classModuleCode + : (classModuleCodeElement endOfLine*)* + ; + +proceduralModuleCodeElement + : commonModuleCodeElement + ; + classModuleCodeElement : commonModuleCodeElement | implementsDirective @@ -388,6 +615,7 @@ commonModuleCodeElement | classModuleDirectiveElement | classModuleDeclarationElement ; + procedureDeclaration : subroutineDeclaration | functionDeclaration @@ -399,32 +627,40 @@ procedureDeclaration // Allow a static keyword before or after, but not both subroutineDeclaration : (procedureScope wsc)? ( - ((initialStatic wsc)? SUB wsc subroutineName (wsc? procedureParameters?)) - | (SUB wsc subroutineName (wsc? procedureParameters)? wsc? trailingStatic) - ) - procedureBody? - endLabel? endOfStatement+ END wsc SUB procedureTail?; + ((initialStatic wsc)? SUB wsc subroutineName (wsc? procedureParameters?)) + | (SUB wsc subroutineName (wsc? procedureParameters)? wsc? trailingStatic) + ) procedureBody? endLabel? endOfStatement+ END wsc SUB procedureTail? + ; + functionDeclaration : (procedureScope wsc)? ( - (initialStatic wsc)? FUNCTION wsc functionName (wsc? procedureParameters)? (wsc? functionType)? - | FUNCTION wsc functionName (wsc? procedureParameters)? (wsc? functionType)? wsc? trailingStatic) - procedureBody? - endLabel? endOfStatement+ END wsc FUNCTION procedureTail?; - + (initialStatic wsc)? FUNCTION wsc functionName (wsc? procedureParameters)? ( + wsc? functionType + )? + | FUNCTION wsc functionName (wsc? procedureParameters)? (wsc? functionType)? wsc? trailingStatic + ) procedureBody? endLabel? endOfStatement+ END wsc FUNCTION procedureTail? + ; + propertyGetDeclaration : (procedureScope wsc)? ( - (initialStatic wsc)? PROPERTY wsc GET wsc functionName (wsc? procedureParameters)? (wsc? functionType)? - | PROPERTY wsc GET wsc functionName procedureParameters? (wsc? functionType)? wsc? trailingStatic) - procedureBody? - endLabel? endOfStatement+ END wsc PROPERTY procedureTail?; - + (initialStatic wsc)? PROPERTY wsc GET wsc functionName (wsc? procedureParameters)? ( + wsc? functionType + )? + | PROPERTY wsc GET wsc functionName procedureParameters? (wsc? functionType)? wsc? trailingStatic + ) procedureBody? endLabel? endOfStatement+ END wsc PROPERTY procedureTail? + ; + propertySetDeclaration : (procedureScope wsc)? ( - (initialStatic wsc)? PROPERTY wsc (LET | SET) wsc subroutineName wsc? propertyParameters - | PROPERTY wsc (LET | SET) wsc subroutineName propertyParameters wsc? trailingStatic) - procedureBody? - endLabel? endOfStatement+ END wsc PROPERTY procedureTail?; -endLabel: endOfStatement* endOfLineNoWs statementLabelDefinition; + (initialStatic wsc)? PROPERTY wsc (LET | SET) wsc subroutineName wsc? propertyParameters + | PROPERTY wsc (LET | SET) wsc subroutineName propertyParameters wsc? trailingStatic + ) procedureBody? endLabel? endOfStatement+ END wsc PROPERTY procedureTail? + ; + +endLabel + : endOfStatement* endOfLineNoWs statementLabelDefinition + ; + procedureTail : wsc? NEWLINE | wsc? commentBody @@ -440,19 +676,26 @@ procedureScope ; // 5.3.1.2 Static Procedures -initialStatic: STATIC; -trailingStatic: STATIC; +initialStatic + : STATIC + ; + +trailingStatic + : STATIC + ; // 5.3.1.3 Procedure Names subroutineName : ambiguousIdentifier | prefixedName ; + functionName : typedName | ambiguousIdentifier - | prefixedName + | prefixedName ; + prefixedName : eventHandlerName | implementedName @@ -460,60 +703,108 @@ prefixedName ; // 5.3.1.4 Function Type Declarations -functionType: AS wsc typeExpression wsc? arrayDesignator?; -arrayDesignator: '(' wsc? ')'; +functionType + : AS wsc typeExpression wsc? arrayDesignator? + ; + +arrayDesignator + : '(' wsc? ')' + ; // 5.3.1.5 Parameter Lists -procedureParameters: '(' wsc? parameterList? wscu? ')'; -propertyParameters: '(' wsc? (parameterList wsc? ',' wsc?)? valueParam wsc? ')'; +procedureParameters + : '(' wsc? parameterList? wscu? ')' + ; + +propertyParameters + : '(' wsc? (parameterList wsc? ',' wsc?)? valueParam wsc? ')' + ; + validParameterList : (positionalParameters wsc? ',' wsc? optionalParameters) - | (positionalParameters (wsc? ',' wsc? paramArray)?) + | (positionalParameters (wsc? ',' wsc? paramArray)?) | optionalParameters | paramArray ; + invalidParameterList : anyParam (wsc? ',' wsc? anyParam)* ; -parameterList: (validParameterList | invalidParameterList); +parameterList + : (validParameterList | invalidParameterList) + ; anyParam : positionalParam | optionalParam | paramArray ; - -positionalParameters: positionalParam (wsc? ',' wsc? positionalParam)*; -optionalParameters: optionalParam (wsc? ',' wsc? optionalParam)*; -valueParam: positionalParam; -positionalParam: (parameterMechanism wsc)? paramDcl; + +positionalParameters + : positionalParam (wsc? ',' wsc? positionalParam)* + ; + +optionalParameters + : optionalParam (wsc? ',' wsc? optionalParam)* + ; + +valueParam + : positionalParam + ; + +positionalParam + : (parameterMechanism wsc)? paramDcl + ; + optionalParam - : optionalPrefix wsc paramDcl wsc? defaultValue?; + : optionalPrefix wsc paramDcl wsc? defaultValue? + ; + paramArray - : PARAMARRAY wsc ambiguousIdentifier '(' wsc? ')' (wsc AS wsc (VARIANT | '[' VARIANT ']'))?; + : PARAMARRAY wsc ambiguousIdentifier '(' wsc? ')' (wsc AS wsc (VARIANT | '[' VARIANT ']'))? + ; + paramDcl : untypedNameParamDcl | typedNameParamDcl ; -untypedNameParamDcl: ambiguousIdentifier parameterType?; -typedNameParamDcl: typedName arrayDesignator?; + +untypedNameParamDcl + : ambiguousIdentifier parameterType? + ; + +typedNameParamDcl + : typedName arrayDesignator? + ; + optionalPrefix : OPTIONAL (wsc parameterMechanism)? | parameterMechanism wsc OPTIONAL ; + parameterMechanism : BYVAL | BYREF ; -parameterType: arrayDesignator? wsc AS wsc (typeExpression | ANY); -defaultValue: '=' wsc? constantExpression; + +parameterType + : arrayDesignator? wsc AS wsc (typeExpression | ANY) + ; + +defaultValue + : '=' wsc? constantExpression + ; // 5.3.1.8 Event Handler Declarations -eventHandlerName: ambiguousIdentifier; +eventHandlerName + : ambiguousIdentifier + ; // 5.3.1.9 Implemented Name Declarations -implementedName: ambiguousIdentifier; +implementedName + : ambiguousIdentifier + ; // 5.3.1.10 Lifecycle Handler Declarations lifecycleHandlerName @@ -523,7 +814,9 @@ lifecycleHandlerName //--------------------------------------------------------------------------------------- // 5.4 Procedure Bodies and Statements -procedureBody: statementBlock; +procedureBody + : statementBlock + ; // 5.4.1 Statement Blocks // spec used *, changed to + changed all parent to call with ? to avoid empty context. @@ -531,41 +824,56 @@ procedureBody: statementBlock; statementBlock : blockStatement+ ; + blockStatement : endOfStatement* (endOfLineNoWs | endOfLine) statementLabelDefinition | endOfStatement+ remStatement | statement | endOfStatement* (endOfLineNoWs | endOfLine) attributeStatement ; + statement : controlStatement | endOfStatement+ dataManipulationStatement | endOfStatement+ errorHandlingStatement | endOfStatement+ fileStatement ; - + // 5.4.1.1 Statement Labels statementLabelDefinition : identifierStatementLabel ':' | lineNumberLabel ':'? ; + statementLabel : identifierStatementLabel | lineNumberLabel - ; -statementLabelList: statementLabel (wsc? ',' wsc? statementLabel)?; -identifierStatementLabel: ambiguousIdentifier; -lineNumberLabel: INTEGERLITERAL; + ; + +statementLabelList + : statementLabel (wsc? ',' wsc? statementLabel)? + ; + +identifierStatementLabel + : ambiguousIdentifier + ; + +lineNumberLabel + : INTEGERLITERAL + ; // 5.4.1.2 Rem Statement // We have a token for this -remStatement: REMCOMMENT; +remStatement + : REMCOMMENT + ; // 5.4.2 Control Statements controlStatement : endOfStatement* endOfLine+ ifStatement | endOfStatement+ controlStatementExceptMultilineIf ; + controlStatementExceptMultilineIf : callStatement | whileStatement @@ -593,100 +901,148 @@ controlStatementExceptMultilineIf // 5.4.2.1 Call Statement callStatement - : CALL wsc (simpleNameExpression - | memberAccessExpression - | indexExpression - | withExpression) - | (simpleNameExpression - | memberAccessExpression - | withExpression) (wsc argumentList)? + : CALL wsc (simpleNameExpression | memberAccessExpression | indexExpression | withExpression) + | (simpleNameExpression | memberAccessExpression | withExpression) (wsc argumentList)? ; // 5.4.2.2 While Statement whileStatement - : WHILE wsc booleanExpression - statementBlock? endOfStatement+ WEND; + : WHILE wsc booleanExpression statementBlock? endOfStatement+ WEND + ; // 5.4.2.3 For Statement forStatement : simpleForStatement | explicitForStatement ; -simpleForStatement: forClause statementBlock? endOfStatement+ NEXT; + +simpleForStatement + : forClause statementBlock? endOfStatement+ NEXT + ; + explicitForStatement - : forClause statementBlock? endOfStatement+ (NEXT | (nestedForStatement wsc? ',')) wsc boundVariableExpression; + : forClause statementBlock? endOfStatement+ (NEXT | (nestedForStatement wsc? ',')) wsc boundVariableExpression + ; + nestedForStatement : explicitForStatement | explicitForEachStatement ; + forClause - : FOR wsc boundVariableExpression wsc? eqOperator wsc? startValue wsc TO wsc endValue (wsc stepClause)?; -startValue: expression; -endValue: expression; -stepClause: STEP wsc stepIncrement; -stepIncrement: expression; + : FOR wsc boundVariableExpression wsc? eqOperator wsc? startValue wsc TO wsc endValue ( + wsc stepClause + )? + ; + +startValue + : expression + ; + +endValue + : expression + ; + +stepClause + : STEP wsc stepIncrement + ; + +stepIncrement + : expression + ; // 5.4.2.4 For Each Statement forEachStatement : simpleForEachStatement | explicitForEachStatement ; + simpleForEachStatement - : forEachClause statementBlock? endOfStatement+ NEXT; - + : forEachClause statementBlock? endOfStatement+ NEXT + ; + explicitForEachStatement - : forEachClause statementBlock? - endOfStatement (NEXT | (nestedForStatement wsc? ',')) wsc boundVariableExpression; - forEachClause: FOR wsc EACH wsc? boundVariableExpression wsc? IN wsc? collection; - collection: expression; + : forEachClause statementBlock? endOfStatement (NEXT | (nestedForStatement wsc? ',')) wsc boundVariableExpression + ; + +forEachClause + : FOR wsc EACH wsc? boundVariableExpression wsc? IN wsc? collection + ; + +collection + : expression + ; // 5.4.2.5 Exit For Statement -exitForStatement: EXIT wsc FOR; +exitForStatement + : EXIT wsc FOR + ; // 5.4.2.6 Do Statement doStatement - : DO (wsc? conditionClause)? statementBlock? endOfStatement+ - LOOP (wsc? conditionClause)?; + : DO (wsc? conditionClause)? statementBlock? endOfStatement+ LOOP (wsc? conditionClause)? + ; + conditionClause : whileClause | untilClause ; -whileClause: WHILE wsc? booleanExpression; -untilClause: UNTIL wsc? booleanExpression; + +whileClause + : WHILE wsc? booleanExpression + ; + +untilClause + : UNTIL wsc? booleanExpression + ; // 5.4.2.7 Exit Do Statement -exitDoStatement: EXIT wsc DO; +exitDoStatement + : EXIT wsc DO + ; // 5.4.2.8 If Statement // why is a LINE-START required before this? ifStatement - : IF wsc? booleanExpression wsc? THEN - statementBlock? - elseIfBlock* - elseBlock? endOfStatement+ - ((END wsc IF) | ENDIF); + : IF wsc? booleanExpression wsc? THEN statementBlock? elseIfBlock* elseBlock? endOfStatement+ ( + (END wsc IF) + | ENDIF + ) + ; + // Need to verify why some of the end-of-line / line-start things are set the way they are. elseIfBlock - : endOfStatement* endOfLine ELSEIF wsc? booleanExpression wsc? THEN endOfLine? - statementBlock? + : endOfStatement* endOfLine ELSEIF wsc? booleanExpression wsc? THEN endOfLine? statementBlock? | endOfStatement* ELSEIF wsc? booleanExpression wsc? THEN statementBlock? ; -elseBlock: endOfLine+ ELSE endOfLine? wsc? statementBlock?; + +elseBlock + : endOfLine+ ELSE endOfLine? wsc? statementBlock? + ; // 5.4.2.9 Single-line If Statement singleLineIfStatement : ifWithNonEmptyThen | ifWithEmptyThen ; + ifWithNonEmptyThen - : IF wsc booleanExpression wsc THEN wsc? listOrLabel (wsc singleLineElseClause)?; + : IF wsc booleanExpression wsc THEN wsc? listOrLabel (wsc singleLineElseClause)? + ; + ifWithEmptyThen - : IF wsc booleanExpression wsc THEN wsc singleLineElseClause; -singleLineElseClause: ELSE wsc? listOrLabel?; + : IF wsc booleanExpression wsc THEN wsc singleLineElseClause + ; + +singleLineElseClause + : ELSE wsc? listOrLabel? + ; + listOrLabel : (statementLabel (':' wsc? sameLineStatement?)*) | ':'? wsc? sameLineStatement (wsc? ':' wsc? sameLineStatement?)* ; + sameLineStatement : fileStatement | errorHandlingStatement @@ -696,17 +1052,27 @@ sameLineStatement // 5.4.2.10 Select Case Statement selectCaseStatement - : SELECT wsc CASE wsc selectExpression - caseClause* - caseElseClause? - endOfStatement+ END wsc SELECT; -caseClause: endOfStatement+ CASE wsc? rangeClause (wsc? ',' wsc? rangeClause)* statementBlock?; -caseElseClause: endOfStatement+ CASE wsc ELSE statementBlock?; + : SELECT wsc CASE wsc selectExpression caseClause* caseElseClause? endOfStatement+ END wsc SELECT + ; + +caseClause + : endOfStatement+ CASE wsc? rangeClause (wsc? ',' wsc? rangeClause)* statementBlock? + ; + +caseElseClause + : endOfStatement+ CASE wsc ELSE statementBlock? + ; + rangeClause : expression | startValue wsc? TO wsc? endValue - | IS? wsc comparisonOperator wsc? expression; -selectExpression: expression; + | IS? wsc comparisonOperator wsc? expression + ; + +selectExpression + : expression + ; + comparisonOperator : eqOperator | NEW @@ -717,40 +1083,67 @@ comparisonOperator ; // 5.4.2.11 Stop Statement -stopStatement: STOP; +stopStatement + : STOP + ; // 5.4.2.12 GoTo Statement -gotoStatement: (GO wsc TO | GOTO) wsc statementLabel; +gotoStatement + : (GO wsc TO | GOTO) wsc statementLabel + ; // 5.4.2.13 On…GoTo Statement -onGotoStatement: ON wsc? expression GOTO wsc statementLabelList; +onGotoStatement + : ON wsc? expression GOTO wsc statementLabelList + ; // 5.4.2.14 GoSub Statement -gosubStatement: ((GO wsc SUB) | GOSUB) wsc statementLabel; +gosubStatement + : ((GO wsc SUB) | GOSUB) wsc statementLabel + ; // 5.4.2.15 Return Statement -returnStatement: RETURN; +returnStatement + : RETURN + ; // 5.4.2.16 On…GoSub Statement -onGosubStatement: ON wsc? expression wsc? GOSUB wsc statementLabelList; +onGosubStatement + : ON wsc? expression wsc? GOSUB wsc statementLabelList + ; // 5.4.2.17 Exit Sub Statement -exitSubStatement: EXIT wsc SUB; +exitSubStatement + : EXIT wsc SUB + ; // 5.4.2.18 Exit Function Statement -exitFunctionStatement: EXIT wsc FUNCTION; +exitFunctionStatement + : EXIT wsc FUNCTION + ; // 5.4.2.19 Exit Property Statement -exitPropertyStatement: EXIT wsc PROPERTY; +exitPropertyStatement + : EXIT wsc PROPERTY + ; // 5.4.2.20 RaiseEvent Statement raiseeventStatement - : RAISEEVENT wsc? ambiguousIdentifier wsc? ('(' wsc? eventArgumentList wsc? ')')?; -eventArgumentList: (eventArgument (wsc? ',' wsc? eventArgument)*)?; -eventArgument: expression; + : RAISEEVENT wsc? ambiguousIdentifier wsc? ('(' wsc? eventArgumentList wsc? ')')? + ; + +eventArgumentList + : (eventArgument (wsc? ',' wsc? eventArgument)*)? + ; + +eventArgument + : expression + ; // 5.4.2.21 With Statement -withStatement: WITH wsc? expression statementBlock? endOfStatement+ END wsc WITH; +withStatement + : WITH wsc? expression statementBlock? endOfStatement+ END wsc WITH + ; // Missing from documentation endStatement @@ -760,9 +1153,8 @@ endStatement // 5.4.3 Data Manipulation Statements // Added eraseStatement. It is missing from the list in MsS-VBAL 1.7 dataManipulationStatement - : localVariableDeclaration - | staticVariableDeclaration - | localConstDeclaration + : variableDeclaration + | constDeclaration | redimStatement | eraseStatement | midStatement @@ -773,16 +1165,24 @@ 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; +staticVariableDeclaration + : STATIC wsc variableDeclarationList + ; // 5.4.3.2 Local Constant Declarations -localConstDeclaration: constDeclaration; +// localConstDeclaration +// : constDeclaration +// ; // 5.4.3.3 ReDim Statement -redimStatement: REDIM (wsc PRESERVE)? wsc? redimDeclarationList; -redimDeclarationList: redimVariableDcl (wsc? ',' wsc? redimVariableDcl)*; +redimStatement + : REDIM (wsc PRESERVE)? wsc? redimDeclarationList + ; + +redimDeclarationList + : redimVariableDcl (wsc? ',' wsc? redimVariableDcl)* + ; + // Had to add withExpression and memberAccess // to match callStatement. redimVariableDcl @@ -791,46 +1191,104 @@ redimVariableDcl | withExpressionDcl | memberAccessExpressionDcl ; -redimTypedVariableDcl: typedName wsc? dynamicArrayDim; -redimUntypedDcl: untypedName wsc? dynamicArrayClause; -withExpressionDcl: withExpression wsc? dynamicArrayDim; -memberAccessExpressionDcl: memberAccessExpression wsc? dynamicArrayDim; -dynamicArrayDim: '(' wsc? dynamicBoundsList wsc? ')'; -dynamicBoundsList: dynamicDimSpec (wsc? ',' wsc? dynamicDimSpec)*; -dynamicDimSpec: (dynamicLowerBound wsc)? dynamicUpperBound; -dynamicLowerBound: integerExpression wsc? TO; -dynamicUpperBound: integerExpression; -dynamicArrayClause: dynamicArrayDim wsc? asClause?; + +redimTypedVariableDcl + : typedName wsc? dynamicArrayDim + ; + +redimUntypedDcl + : untypedName wsc? dynamicArrayClause + ; + +withExpressionDcl + : withExpression wsc? dynamicArrayDim + ; + +memberAccessExpressionDcl + : memberAccessExpression wsc? dynamicArrayDim + ; + +dynamicArrayDim + : '(' wsc? dynamicBoundsList wsc? ')' + ; + +dynamicBoundsList + : dynamicDimSpec (wsc? ',' wsc? dynamicDimSpec)* + ; + +dynamicDimSpec + : (dynamicLowerBound wsc)? dynamicUpperBound + ; + +dynamicLowerBound + : integerExpression wsc? TO + ; + +dynamicUpperBound + : integerExpression + ; + +dynamicArrayClause + : dynamicArrayDim wsc? asClause? + ; // 5.4.3.4 Erase Statement -eraseStatement: ERASE wsc? eraseList; -eraseList: eraseElement (wsc? ',' wsc? eraseElement)*; -eraseElement: lExpression; +eraseStatement + : ERASE wsc? eraseList + ; + +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? eqOperator wsc? expression; +midStatement + : modeSpecifier wsc? '(' wsc? stringArgument wsc? ',' wsc? startMid wsc? (',' wsc? length)? ')' wsc? eqOperator wsc? expression + ; + modeSpecifier : MID | MIDB | MID_D | MIDB_D ; -stringArgument: boundVariableExpression; + +stringArgument + : boundVariableExpression + ; + // Changed name from start to startMid due to a problem with the Dart compilation. -startMid: integerExpression; -length: integerExpression; +startMid + : integerExpression + ; + +length + : integerExpression + ; // 5.4.3.6 LSet Statement -lsetStatement: LSET wsc? boundVariableExpression wsc? eqOperator wsc? expression; +lsetStatement + : LSET wsc? boundVariableExpression wsc? eqOperator wsc? expression + ; // 5.4.3.7 RSet Statement -rsetStatement: RSET wsc? boundVariableExpression wsc? eqOperator wsc? expression; +rsetStatement + : RSET wsc? boundVariableExpression wsc? eqOperator wsc? expression + ; // 5.4.3.8 Let Statement -letStatement: (LET wsc)? lExpression wsc? eqOperator wsc? expression; +letStatement + : (LET wsc)? lExpression wsc? eqOperator wsc? expression + ; // 5.4.3.9 Set Statement -setStatement: SET wsc lExpression wsc? eqOperator wsc? expression; +setStatement + : SET wsc lExpression wsc? eqOperator wsc? expression + ; // 5.4.4 Error Handling Statements errorHandlingStatement @@ -840,18 +1298,28 @@ errorHandlingStatement ; // 5.4.4.1 On Error Statement -onErrorStatement: ON wsc ERROR wsc? errorBehavior; +onErrorStatement + : ON wsc ERROR wsc? errorBehavior + ; + errorBehavior : RESUME wsc NEXT | GOTO wsc? statementLabel ; // 5.4.4.2 Resume Statement -resumeStatement: RESUME wsc? (NEXT| statementLabel)?; +resumeStatement + : RESUME wsc? (NEXT | statementLabel)? + ; // 5.4.4.3 Error Statement -errorStatement: ERROR wsc errorNumber; -errorNumber: integerExpression; +errorStatement + : ERROR wsc errorNumber + ; + +errorNumber + : integerExpression + ; // 5.4.5 File Statements fileStatement @@ -873,8 +1341,15 @@ fileStatement openStatement : OPEN wsc? pathName wsc? modeClause? wsc accessClause? wsc? lock? wsc? AS wsc? fileNumber wsc? lenClause? ; -pathName: expression; -modeClause: FOR wsc modeOpt; + +pathName + : expression + ; + +modeClause + : FOR wsc modeOpt + ; + modeOpt : APPEND | BINARY @@ -882,92 +1357,191 @@ modeOpt | OUTPUT | RANDOM ; -accessClause: ACCESS wsc access; + +accessClause + : ACCESS wsc access + ; + access : READ | WRITE | READ wsc WRITE ; + lock : SHARED | LOCK wsc READ | LOCK wsc WRITE | LOCK wsc READ wsc WRITE ; -lenClause: LEN wsc eqOperator wsc recLength; -recLength: expression; + +lenClause + : LEN wsc eqOperator wsc recLength + ; + +recLength + : expression + ; // 5.4.5.1.1 File Numbers fileNumber : markedFileNumber | unmarkedFileNumber ; -markedFileNumber: '#' expression; -unmarkedFileNumber: expression; + +markedFileNumber + : '#' expression + ; + +unmarkedFileNumber + : expression + ; // 5.4.5.2 Close and Reset Statements closeStatement : RESET | CLOSE wsc? fileNumberList? ; -fileNumberList: fileNumber (wsc? ',' wsc? fileNumber)*; + +fileNumberList + : fileNumber (wsc? ',' wsc? fileNumber)* + ; // 5.4.5.3 Seek Statement -seekStatement: SEEK wsc fileNumber wsc? ',' wsc? position; -position: expression; +seekStatement + : SEEK wsc fileNumber wsc? ',' wsc? position + ; + +position + : expression + ; // 5.4.5.4 Lock Statement -lockStatement: LOCK wsc fileNumber (wsc? ',' wsc? recordRange); +lockStatement + : LOCK wsc fileNumber (wsc? ',' wsc? recordRange) + ; + recordRange : startRecordNumber | startRecordNumber? wsc TO wsc endRecordNumber ; -startRecordNumber: expression; -endRecordNumber: expression; + +startRecordNumber + : expression + ; + +endRecordNumber + : expression + ; // 5.4.5.5 Unlock Statement -unlockStatement: UNLOCK wsc fileNumber (wsc? ',' wsc? recordRange)?; +unlockStatement + : UNLOCK wsc fileNumber (wsc? ',' wsc? recordRange)? + ; // 5.4.5.6 Line Input Statement -lineInputStatement: LINE wsc INPUT wsc markedFileNumber wsc? ',' wsc? variableName; -variableName: variableExpression; +lineInputStatement + : LINE wsc INPUT wsc markedFileNumber wsc? ',' wsc? variableName + ; + +variableName + : variableExpression + ; // 5.4.5.7 Width Statement -widthStatement: WIDTH wsc markedFileNumber wsc? ',' wsc? lineWidth; -lineWidth: expression; +widthStatement + : WIDTH wsc markedFileNumber wsc? ',' wsc? lineWidth + ; + +lineWidth + : expression + ; // 5.4.5.8 Print Statement -printStatement: PRINT wsc markedFileNumber wsc? ',' wsc? outputList?; +printStatement + : PRINT wsc markedFileNumber wsc? ',' wsc? outputList? + ; // 5.4.5.8.1 Output Lists -outputList: outputItem+; +outputList + : outputItem+ + ; + outputItem : outputClause charPosition? - | charPosition; -outputClause: spcClause | tabClause| outputExpression; -charPosition: ';' | ','; -outputExpression: expression; -spcClause: SPC wsc '(' wsc? spcNumber wsc? ')'; -spcNumber: expression; -tabClause: TAB wsc '(' wsc? tabNumber wsc? ')'; -tabNumber: expression; + | charPosition + ; + +outputClause + : spcClause + | tabClause + | outputExpression + ; + +charPosition + : ';' + | ',' + ; + +outputExpression + : expression + ; + +spcClause + : SPC wsc '(' wsc? spcNumber wsc? ')' + ; + +spcNumber + : expression + ; + +tabClause + : TAB wsc '(' wsc? tabNumber wsc? ')' + ; + +tabNumber + : expression + ; // 5.4.5.9 Write Statement -writeStatement: WRITE wsc markedFileNumber wsc? ',' wsc? outputList?; +writeStatement + : WRITE wsc markedFileNumber wsc? ',' wsc? outputList? + ; // 5.4.5.10 Input Statement -inputStatement: INPUT wsc markedFileNumber wsc? ',' wsc? inputList; -inputList: inputVariable (wsc? ',' wsc? inputVariable)*; -inputVariable: boundVariableExpression; +inputStatement + : INPUT wsc markedFileNumber wsc? ',' wsc? inputList + ; + +inputList + : inputVariable (wsc? ',' wsc? inputVariable)* + ; + +inputVariable + : boundVariableExpression + ; // 5.4.5.11 Put Statement -putStatement: PUT wsc fileNumber wsc? ',' wsc? recordNumber? wsc? ',' wsc? data; -recordNumber: expression; -data: expression; +putStatement + : PUT wsc fileNumber wsc? ',' wsc? recordNumber? wsc? ',' wsc? data + ; + +recordNumber + : expression + ; + +data + : expression + ; // 5.4.5.12 Get Statement -getStatement: GET wsc fileNumber wsc? ',' wsc? recordNumber? wsc? ',' wsc? variable; -variable: variableExpression; +getStatement + : GET wsc fileNumber wsc? ',' wsc? recordNumber? wsc? ',' wsc? variable + ; + +variable + : variableExpression + ; // Attribute Statement attributeStatement @@ -992,9 +1566,11 @@ attributeUsrName debugStatement : DEBUG '.' PRINT wsc debugArgs ; + debugArgs : expression (wsc? debugSep wsc? expression)* ; + debugSep : wsc | ';' @@ -1028,15 +1604,9 @@ expression | ltOperator | neqOperator | eqOperator - ) wsc? expression + ) wsc? expression | notOperatorExpression - | expression wsc? ( - andOperator - | orOperator - | xorOperator - | eqvOperator - | impOperator - ) wsc? expression + | expression wsc? (andOperator | orOperator | xorOperator | eqvOperator | impOperator) wsc? expression | lExpression ; @@ -1045,18 +1615,18 @@ expression lExpression : simpleNameExpression | instanceExpression -// memberAccessExpression + // memberAccessExpression | lExpression '.' wsc? unrestrictedName - | lExpression wsc? LINE_CONTINUATION wsc?'.' wsc? unrestrictedName -// indexExpression + | lExpression wsc? LINE_CONTINUATION wsc? '.' wsc? unrestrictedName + // indexExpression | lExpression wsc? '(' wsc? argumentList wsc? ')' -// dictionaryAccessExpression - | lExpression '!' unrestrictedName + // dictionaryAccessExpression + | lExpression '!' unrestrictedName | lExpression wsc? LINE_CONTINUATION wsc? '!' unrestrictedName | lExpression wsc? LINE_CONTINUATION wsc? '!' wsc? LINE_CONTINUATION wsc? unrestrictedName | withExpression ; - + // 5.6.5 Literal Expressions // check on hex and oct // check definition of integer and float @@ -1069,10 +1639,14 @@ literalExpression ; // 5.6.6 Parenthesized Expressions -parenthesizedExpression: LPAREN wsc? expression wsc? RPAREN; +parenthesizedExpression + : LPAREN wsc? expression wsc? RPAREN + ; // 5.6.7 TypeOf…Is Expressions -typeofIsExpression: TYPEOF wsc? expression wsc? IS wsc? typeExpression; +typeofIsExpression + : TYPEOF wsc? expression wsc? IS wsc? typeExpression + ; // 5.6.8 New Expressions // The name 'newExpression' fails under the Go language @@ -1081,10 +1655,14 @@ newExpress ; // 5.6.9.8.1 Not Operator -notOperatorExpression: NOT wsc? expression; +notOperatorExpression + : NOT wsc? expression + ; // 5.6.9.3.1 Unary - Operator -unaryMinusExpression: MINUS wsc? expression; +unaryMinusExpression + : MINUS wsc? expression + ; // 5.6.10 Simple Name Expressions // Had to add reservedName and specialForm to allow calls to Abs() Debug. and Lbound() @@ -1092,17 +1670,19 @@ simpleNameExpression : name | reservedName | specialForm -; + ; // 5.6.11 Instance Expressions -instanceExpression: ME; +instanceExpression + : ME + ; // 5.6.12 Member Access Expressions // This expression is also rolled into lExpression. // Changes here must be duplicated there memberAccessExpression : lExpression '.' wsc? unrestrictedName - | lExpression wsc? LINE_CONTINUATION wsc?'.' wsc? unrestrictedName + | lExpression wsc? LINE_CONTINUATION wsc? '.' wsc? unrestrictedName ; // 5.6.13 Index Expressions @@ -1113,15 +1693,31 @@ indexExpression ; // 5.6.13.1 Argument Lists -argumentList: positionalOrNamedArgumentList?; +argumentList + : positionalOrNamedArgumentList? + ; + positionalOrNamedArgumentList : (positionalArgument wsc? ',' wsc?)* requiredPositionalArgument | (positionalArgument wsc? ',' wsc?)* namedArgumentList ; -positionalArgument: argumentExpression?; -requiredPositionalArgument: argumentExpression; -namedArgumentList: namedArgument (wsc? ',' wsc? namedArgument)*; -namedArgument: unrestrictedName wsc? ASSIGN wsc? argumentExpression; + +positionalArgument + : argumentExpression? + ; + +requiredPositionalArgument + : argumentExpression + ; + +namedArgumentList + : namedArgument (wsc? ',' wsc? namedArgument)* + ; + +namedArgument + : unrestrictedName wsc? ASSIGN wsc? argumentExpression + ; + argumentExpression : (BYVAL wsc)? expression | addressofExpression @@ -1131,7 +1727,7 @@ argumentExpression // This expression is also rolled into lExpression. // Changes here must be duplicated there dictionaryAccessExpression - : lExpression '!' unrestrictedName + : lExpression '!' unrestrictedName | lExpression wsc? LINE_CONTINUATION wsc? '!' unrestrictedName | lExpression wsc? LINE_CONTINUATION wsc? '!' wsc? LINE_CONTINUATION wsc? unrestrictedName ; @@ -1141,35 +1737,54 @@ withExpression : withMemberAccessExpression | withDictionaryAccessExpression ; -withMemberAccessExpression: '.' unrestrictedName; -withDictionaryAccessExpression: '!' unrestrictedName; + +withMemberAccessExpression + : '.' unrestrictedName + ; + +withDictionaryAccessExpression + : '!' unrestrictedName + ; // 5.6.16 Constrained Expressions // The following Expressions have complex static requirements // 5.6.16.1 Constant Expressions -constantExpression: expression; +constantExpression + : expression + ; // 5.6.16.2 Conditional Compilation Expressions -ccExpression: expression; +ccExpression + : expression + ; // 5.6.16.3 Boolean Expressions -booleanExpression: expression; +booleanExpression + : expression + ; // 5.6.16.4 Integer Expressions -integerExpression: expression; +integerExpression + : expression + ; // 5.6.16.5 -variableExpression: lExpression; +variableExpression + : lExpression + ; // 5.6.16.6 -boundVariableExpression: lExpression; +boundVariableExpression + : lExpression + ; // 5.6.16.7 typeExpression : builtinType | definedTypeExpression ; + definedTypeExpression : simpleNameExpression | memberAccessExpression @@ -1179,6 +1794,7 @@ definedTypeExpression addressofExpression : ADDRESSOF wsc procedurePointerExpression ; + procedurePointerExpression : simpleNameExpression | memberAccessExpression @@ -1193,34 +1809,45 @@ procedurePointerExpression // If may make things simpler here to send all wsc to the hidden channel // and let a linting tool highlight the couple cases where whitespace // will cause an error. -wsc: (WS | LINE_CONTINUATION)+; +wsc + : (WS | LINE_CONTINUATION)+ + ; + // known as EOL in MS-VBAL endOfLine : wsc? (NEWLINE | commentBody | remStatement) wsc? ; + unexpectedEndOfLine : wsc? (NEWLINE | commentBody | remStatement) wsc? ; + // White space, continuation, unexpected ending. // TODO: Replace as many wsc with wscu and test. wscu : (wsc | unexpectedEndOfLine)+ ; + // We usually don't care if a line of code begins with whitespace, and the parser rules are // cleaner if we lump that in with the EOL or EOS "token". However, for those cases where // something MUST occur on the start of a line, use endOfLineNoWs. endOfLineNoWs : wsc? (NEWLINE | commentBody | remStatement) ; + // known as EOS in MS-VBAL endOfStatement : (endOfLine | wsc? COLON wsc?)+ ; + endOfStatementNoWs : (endOfLineNoWs | wsc? COLON)+ ; + // The COMMENT token includes the leading single quote -commentBody: COMMENT; +commentBody + : COMMENT + ; // 3.3.5.2 Reserved Identifiers and IDENTIFIER reservedIdentifier @@ -1235,11 +1862,13 @@ reservedIdentifier | reservedForImplementationUse | futureReserved ; + // Known as IDENTIFIER in MS-VBAL ambiguousIdentifier : IDENTIFIER | ambiguousKeyword ; + statementKeyword : CALL | CASE @@ -1309,16 +1938,20 @@ statementKeyword | WITH | WRITE ; -remKeyword: REM; + +remKeyword + : REM + ; + markerKeyword : ANY | AS | BYREF - | BYVAL + | BYVAL | CASE | EACH | ELSE - | IN + | IN | NEW | SHARED | UNTIL @@ -1332,6 +1965,7 @@ markerKeyword | THEN | TO ; + operatorIdentifier : ADDRESSOF | AND @@ -1346,6 +1980,7 @@ operatorIdentifier | TYPEOF | XOR ; + reservedName : ABS | CBOOL @@ -1375,6 +2010,7 @@ reservedName | SGN | STRING ; + specialForm : ARRAY | CIRCLE @@ -1384,6 +2020,7 @@ specialForm | SCALE | UBOUND ; + reservedTypeIdentifier : BOOLEAN | BYTE @@ -1421,22 +2058,27 @@ typeableReservedName : DATE | STRING ; + literalIdentifier : booleanLiteralIdentifier | objectLiteralIdentifier | variantLiteralIdentifier ; + booleanLiteralIdentifier : TRUE | FALSE ; + objectLiteralIdentifier : NOTHING ; + variantLiteralIdentifier : EMPTY_X | NULL_ ; + reservedForImplementationUse : ATTRIBUTE | LINEINPUT @@ -1446,11 +2088,11 @@ reservedForImplementationUse | VB_CUSTOMIZABLE | VB_DESCRIPTION | VB_EXPOSED - | VB_EXT_KEY + | VB_EXT_KEY | VB_GLOBALNAMESPACE | VB_HELPID | VB_INVOKE_FUNC - | VB_INVOKE_PROPERTY + | VB_INVOKE_PROPERTY | VB_INVOKE_PROPERTYPUT | VB_INVOKE_PROPERTYPUTREF | VB_MEMBERFLAGS @@ -1462,9 +2104,10 @@ reservedForImplementationUse | VB_VARDESCRIPTION | VB_VARHELPID | VB_VARMEMBERFLAGS - | VB_VARPROCDATA + | VB_VARPROCDATA | VB_VARUSERMEMID ; + futureReserved : CDECL | DEFDEC @@ -1486,6 +2129,7 @@ typedName : ambiguousIdentifier typeSuffix | typeableReservedName typeSuffix ; + typeSuffix : '&' | '%' @@ -1711,6 +2355,7 @@ BASE BEGIN : 'BEGIN' ; + BEGINPROPERTY : 'BEGINPROPERTY' ; @@ -2310,7 +2955,7 @@ RETURN RSET : 'RSET' ; - + SCALE : 'SCALE' ; @@ -2435,7 +3080,7 @@ VB_EXPOSED : 'VB_EXPOSED' ; -VB_EXT_KEY +VB_EXT_KEY : 'VB_EXT_KEY ' ; @@ -2451,7 +3096,7 @@ VB_INVOKE_FUNC : 'VB_INVOKE_FUNC' ; -VB_INVOKE_PROPERTY +VB_INVOKE_PROPERTY : 'VB_INVOKE_PROPERTY ' ; @@ -2499,7 +3144,7 @@ VB_VARMEMBERFLAGS : 'VB_VARMEMBERFLAGS' ; -VB_VARPROCDATA +VB_VARPROCDATA : 'VB_VARPROCDATA ' ; @@ -2560,7 +3205,7 @@ COLLECTION DATABASE : 'DATABASE' ; - + DELETESETTING : 'DELETESETTING' ; @@ -2634,15 +3279,19 @@ DIV : '\\' | '/' ; + Dollar : '$' ; + EQ : '=' ; + EXCLAM : '!' ; + GEQ : '>=' | '=>' @@ -2655,6 +3304,7 @@ GT HASH : '#' ; + LEQ : '<=' | '=<' @@ -2828,6 +3478,7 @@ fragment AMPM FILEOFFSET : '$'? STRINGLITERAL ':' HEXDIGIT+ ; + // whitespace, line breaks, comments, ... LINE_CONTINUATION : WS UNDERSCORE WS? '\r'? '\n' @@ -2862,10 +3513,12 @@ WS ; MACRO_LINE - : (WS? '#IF' ~[\r\n\u2028\u2029]* THEN COMMENT? - | WS? '#ELSEIF' ~[\r\n\u2028\u2029]* THEN COMMENT? - | WS? '#ELSE' COMMENT? - | WS? ('#END If'|'#endif') COMMENT?) -> channel(HIDDEN) + : ( + WS? '#IF' ~[\r\n\u2028\u2029]* THEN COMMENT? + | WS? '#ELSEIF' ~[\r\n\u2028\u2029]* THEN COMMENT? + | WS? '#ELSE' COMMENT? + | WS? ('#END If' | '#endif') COMMENT? + ) -> channel(HIDDEN) ; // identifier From 8382343021a87ba84aa815cf8920ef6c050ad55b Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:53:34 +0800 Subject: [PATCH 23/29] isPublic shouldn't be on base --- server/src/project/elements/base.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index 5d74596..5e3c31c 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -28,8 +28,6 @@ export abstract class BaseSyntaxElement { symbolInformationCapability?: SymbolInformationCapability; scopeItemCapability?: ScopeItemCapability; - get isPublic(): boolean { return false; } - constructor(); constructor(ctx: T, doc: TextDocument) constructor(ctx?: T, doc?: TextDocument) { From f1b84feb8094836e03ae10d6d3d1ac2c67d96f4b Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:54:55 +0800 Subject: [PATCH 24/29] Invalid variable declaration diagnostics --- server/src/capabilities/diagnostics.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts index 579b328..d63be3d 100644 --- a/server/src/capabilities/diagnostics.ts +++ b/server/src/capabilities/diagnostics.ts @@ -101,6 +101,23 @@ export class SubOrFunctionNotDefinedDiagnostic extends BaseDiagnostic { } } +// test +export class MethodVariableHasVisibilityModifierDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Information; + constructor(range: Range, message: string) { + super(range); + this.message = `Visibility ignored for ${message} scoped variables.`; + } +} + +// test +export class MethodVariableIsPublicDiagnostic extends BaseDiagnostic { + severity = DiagnosticSeverity.Warning; + constructor(range: Range, message: string) { + super(range); + this.message = `${message} scoped variables cannot be public.`; + } +} // test export class UnexpectedLineEndingDiagnostic extends BaseDiagnostic { From 8b234b9454fef9f7c3fc7322cdb09518be39e3c2 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:56:04 +0800 Subject: [PATCH 25/29] Simplified variable parsing no longer requries extensions --- server/src/extensions/parserExtensions.ts | 78 ----------------------- 1 file changed, 78 deletions(-) diff --git a/server/src/extensions/parserExtensions.ts b/server/src/extensions/parserExtensions.ts index 63bc027..482da9e 100644 --- a/server/src/extensions/parserExtensions.ts +++ b/server/src/extensions/parserExtensions.ts @@ -14,12 +14,7 @@ import { ConstItemContext, EndOfStatementContext, EndOfStatementNoWsContext, - GlobalVariableDeclarationContext, - PrivateConstDeclarationContext, - PrivateVariableDeclarationContext, ProcedureTailContext, - PublicConstDeclarationContext, - PublicVariableDeclarationContext, TypeSpecContext, TypeSuffixContext, VariableDclContext, @@ -56,15 +51,6 @@ declare module '../antlr/out/vbapreParser' { 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. */ @@ -75,21 +61,6 @@ declare module '../antlr/out/vbaParser' { 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; @@ -238,55 +209,6 @@ CompilerConditionalStatementContext.prototype.vbaExpression = function (): strin }; -// 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. From b362d5ce11755cec9e9271879e41083d02662675 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:57:32 +0800 Subject: [PATCH 26/29] Smarter registration and properties --- server/src/capabilities/capabilities.ts | 35 ++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/server/src/capabilities/capabilities.ts b/server/src/capabilities/capabilities.ts index 7e7bab9..85e006a 100644 --- a/server/src/capabilities/capabilities.ts +++ b/server/src/capabilities/capabilities.ts @@ -15,7 +15,7 @@ import { ParserRuleContext, TerminalNode } from 'antlr4ng'; import { SemanticToken } from '../capabilities/semanticTokens'; import { FoldingRange, FoldingRangeKind } from '../capabilities/folding'; import { BaseContextSyntaxElement, BaseIdentifyableSyntaxElement, Context, HasSemanticTokenCapability } from '../project/elements/base'; -import { BaseDiagnostic, DuplicateDeclarationDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; +import { BaseDiagnostic, DuplicateDeclarationDiagnostic, MethodVariableIsPublicDiagnostic, ShadowDeclarationDiagnostic, SubOrFunctionNotDefinedDiagnostic, VariableNotDefinedDiagnostic } from './diagnostics'; import { Services } from '../injection/services'; @@ -203,11 +203,18 @@ export class ScopeItemCapability { // Technical isDirty: boolean = true; + private get isMethodScope(): boolean { + return [ + ItemType.SUBROUTINE, + ItemType.FUNCTION, + ItemType.PROPERTY, + ].includes(this.type); + } // Item Properties explicitSetName?: string; - isPublicScope: boolean = false; - + isPublicScope = false; + visibilityModifierContext?: ParserRuleContext; constructor( readonly element?: BaseContextSyntaxElement, @@ -507,12 +514,28 @@ export class ScopeItemCapability { // Check pub/priv declares in same document treated as duplicate instead of shadowed. /** - * !! Declaring public things is working :) - * So far: just Enum (need to set up the logic for everything). + * Visibility on a method-scoped variable does nothing but isn't invalid. + * These should declare as if they're private and raise a warning. */ + const getParent = (item: ScopeItemCapability): ScopeItemCapability => + (item.isPublicScope ? this.project : this) ?? this; + + // Method-scoped variables are always private. + if (this.isMethodScope && item.type === ItemType.VARIABLE && item.isPublicScope) { + item.isPublicScope = false; + if (item.visibilityModifierContext && item.element) { + const ctx = item.visibilityModifierContext; + const diagnostic = new MethodVariableIsPublicDiagnostic( + ctx.toRange(item.element.context.document), + ItemType[this.type] + ); + item.element?.diagnosticCapability?.diagnostics.push(diagnostic); + } + } + // Set the parent for the item. - item.parent = this.isPublicScope ? (this.project ?? this) : this; + item.parent = getParent(item); item.parent.isDirty = true; // Get the scope level for logging. From 3ae9feefe6892afa0d1052e2a1336a573f00c3f9 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:58:32 +0800 Subject: [PATCH 27/29] New variable handling --- server/src/project/elements/typing.ts | 188 +++++++++++++++++++---- server/src/project/parser/vbaListener.ts | 20 +-- 2 files changed, 164 insertions(+), 44 deletions(-) diff --git a/server/src/project/elements/typing.ts b/server/src/project/elements/typing.ts index f2cffc3..af10f09 100644 --- a/server/src/project/elements/typing.ts +++ b/server/src/project/elements/typing.ts @@ -5,19 +5,19 @@ import { SemanticTokenModifiers, SemanticTokenTypes, SymbolKind } from 'vscode-l // Antlr import { ParserRuleContext } from 'antlr4ng'; import { + ArrayDimContext, + AsClauseContext, + ConstDeclarationContext, ConstItemContext, EnumDeclarationContext, EnumMemberContext, - GlobalVariableDeclarationContext, - PrivateConstDeclarationContext, - PrivateVariableDeclarationContext, - PublicConstDeclarationContext, PublicEnumDeclarationContext, PublicTypeDeclarationContext, - PublicVariableDeclarationContext, TypeSuffixContext, UdtDeclarationContext, + UnrestrictedNameContext, VariableDclContext, + VariableDeclarationContext, WitheventsVariableDclContext } from '../../antlr/out/vbaParser'; @@ -107,35 +107,48 @@ export class TypeDeclarationElement extends BaseTypeDeclarationElement extends BaseContextSyntaxElement { - private _isPublic: boolean; - private isConstant: boolean; +export class VariableDeclarationStatementElement extends BaseContextSyntaxElement { + + get isPublic(): boolean { + const modifierCtx = this.context.rule.variableModifier(); + return !!modifierCtx?.GLOBAL() || !!modifierCtx?.PUBLIC(); + } - 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 - )); + const doc = this.context.document; + const declarationList = this.context.rule.variableDeclarationList() + ?? this.context.rule.moduleVariableDeclarationList()!; + + return declarationList.variableDcl().map(ctx => new VariableDeclarationElement(ctx, doc, this.isPublic, false)); } - constructor(ctx: T, doc: TextDocument, isConstant: boolean, isPublic: boolean) { + constructor(ctx: VariableDeclarationContext, doc: TextDocument) { super(ctx, doc); - this._isPublic = isPublic; - this.isConstant = isConstant; this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE); } +} + +export class ConstDeclarationStatementElement extends BaseContextSyntaxElement { + + get isPublic(): boolean { + const modifierCtx = this.context.rule.variableModifier(); + return !!modifierCtx?.GLOBAL() || !!modifierCtx?.PUBLIC(); + } - 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)); + get declarations() { + const doc = this.context.document; + const declarationList = this.context.rule.constItemList().constItem(); + return declarationList.map(ctx => new VariableDeclarationElement(ctx, doc, this.isPublic, true)); + } + + constructor(ctx: ConstDeclarationContext, doc: TextDocument) { + super(ctx, doc); + this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE); } } @@ -146,17 +159,134 @@ export class VariableDeclarationElement extends BaseContextSyntaxElement ctx.ambiguousIdentifier() }); + + // VariableDcl > TypedVariableDcl > TypedName > TypeSuffix + // > UntypedVariableDcl > AsClause + + // Always going to be an object. + // WithEventsVariable > classTypeName > definedTypeExpression > simple/member + + // Always going to be a primative. + // ConstItemContext > TypeSuffix + // ConstItemContext > constAsClause > builtInType + if (ctx instanceof VariableDclContext) { + const typeCtx = ctx.typedVariableDcl()?.typedName().typeSuffix() + ?? ctx.untypedVariableDcl()?.asClause(); + const arrayCtx = ctx.typedVariableDcl()?.arrayDim() + ?? ctx.untypedVariableDcl()?.arrayClause()?.arrayDim(); + if (typeCtx) { + this.variableTypeInformation = new VariableTypeInformation(typeCtx, doc, arrayCtx); + } + } this.scopeItemCapability = new ScopeItemCapability(this, ItemType.VARIABLE); + this.scopeItemCapability.assignmentType = AssignmentType.GET + | (this.hasLetAccessor ? AssignmentType.LET : AssignmentType.NONE) + | (this.hasSetAccessor ? AssignmentType.SET : AssignmentType.NONE); + } + + get hasLetAccessor(): boolean { + if (this.context.rule instanceof WitheventsVariableDclContext) { + return false; + } + if (this.context.rule instanceof ConstItemContext) { + return false; + } + return this.variableTypeInformation?.isPrimativeType ?? true; + } + + get hasSetAccessor(): boolean { + if (this.context.rule instanceof WitheventsVariableDclContext) { + return true; + } + if (this.context.rule instanceof ConstItemContext) { + return false; + } + return this.variableTypeInformation?.isObjectType ?? true; + } + + getType() { + const ctx = this.context.rule; + if (ctx instanceof VariableDclContext) { + // If we're null here, we're implicitly a variant. + const typeCtx = ctx.typedVariableDcl()?.typedName().typeSuffix() + ?? ctx.untypedVariableDcl()!.asClause(); + + } + } +} + +// ToDo: Needs to handle ClassTypeNameContext +class VariableTypeInformation extends BaseContextSyntaxElement { + get isObjectType(): boolean { + // Type hints are never an object. + const ctx = this.context.rule; + if (ctx instanceof TypeSuffixContext) { + return false; + } + + // Check builtins for variant type. + const builtin = ctx.asType()?.typeSpec().typeExpression()?.builtinType(); + if (builtin?.reservedTypeIdentifier()?.VARIANT() || builtin?.reservedTypeIdentifierB()?.VARIANT_B()) { + return true; + } + + // Don't trust anything else. Just check not a primative. + return !this.isPrimativeType; + } + + get isPrimativeType(): boolean { + // Type hints are always primitive. + const ctx = this.context.rule; + if (ctx instanceof TypeSuffixContext) { + return true; + } + + // A newed object is always an object. + if (ctx.asAutoObject()) { + return false; + } + + // Fixed length strings are primative. + const typeSpec = ctx.asType()!.typeSpec(); + if (typeSpec.fixedLengthStringSpec()) { + return true; + } + + // Built ins are primative (or can be in Variant's case) unless object. + const builtin = typeSpec.typeExpression()?.builtinType(); + if (builtin?.reservedTypeIdentifier() || builtin?.reservedTypeIdentifierB()) { + return true; + } else if (builtin?.OBJECT() || builtin?.OBJECT_B()) { + return false; + } + + // Defined names can be all sorts of things but if we got here, we're an object. + const definedType = typeSpec.typeExpression()?.definedTypeExpression(); + if (definedType?.simpleNameExpression()) { + return false; + } + + // If we have a member accessed type, we need to do more digging... + const memberAccessed = definedType?.memberAccessExpression()?.unrestrictedName(); + const isPrimativeMember = (ctx: UnrestrictedNameContext | undefined): boolean => !!memberAccessed?.reservedIdentifier()?.reservedTypeIdentifier(); + const isTypeSuffixMember = (ctx: UnrestrictedNameContext | undefined): boolean => !!memberAccessed?.name()?.typedName(); + return isPrimativeMember(memberAccessed) || isTypeSuffixMember(memberAccessed); + } + + get isFixedArrayType(): boolean { + return !this.arrayCtx?.boundsList(); + } + + constructor(ctx: TypeSuffixContext | AsClauseContext, doc: TextDocument, private readonly arrayCtx?: ArrayDimContext) { + super(ctx, doc); } } diff --git a/server/src/project/parser/vbaListener.ts b/server/src/project/parser/vbaListener.ts index ae22d20..f33080f 100644 --- a/server/src/project/parser/vbaListener.ts +++ b/server/src/project/parser/vbaListener.ts @@ -13,22 +13,18 @@ import { EnumDeclarationContext, EnumMemberContext, FunctionDeclarationContext, - GlobalVariableDeclarationContext, IfStatementContext, IgnoredClassAttrContext, IgnoredProceduralAttrContext, - PrivateConstDeclarationContext, - PrivateVariableDeclarationContext, ProceduralModuleContext, ProcedureDeclarationContext, PropertyGetDeclarationContext, PropertySetDeclarationContext, - PublicConstDeclarationContext, - PublicVariableDeclarationContext, SubroutineDeclarationContext, TypeSuffixContext, UdtDeclarationContext, UnexpectedEndOfLineContext, + VariableDeclarationContext, WhileStatementContext } from '../../antlr/out/vbaParser'; import { @@ -57,7 +53,7 @@ import { UnexpectedEndOfLineElement } from '../elements/utils'; import { DuplicateOperatorElement, IfElseBlock as IfStatementElement, WhileLoopElement } from '../elements/flow'; import { VbaClassDocument, VbaModuleDocument } from '../document'; import { ClassElement, ModuleElement, ModuleIgnoredAttributeElement } from '../elements/module'; -import { DeclarationStatementElement, EnumDeclarationElement, EnumMemberDeclarationElement, TypeDeclarationElement, TypeSuffixElement } from '../elements/typing'; +import { VariableDeclarationStatementElement, EnumDeclarationElement, EnumMemberDeclarationElement, TypeDeclarationElement, TypeSuffixElement } from '../elements/typing'; import { FunctionDeclarationElement, PropertyGetDeclarationElement, PropertyLetDeclarationElement, PropertySetDeclarationElement, SubDeclarationElement } from '../elements/procedure'; import { ExtensionConfiguration } from '../workspace'; import { Services } from '../../injection/services'; @@ -178,15 +174,9 @@ export class VbaListener extends vbaListener { enterTypeSuffix = (ctx: TypeSuffixContext) => this.document.registerElement(new TypeSuffixElement(ctx, this.document.textDocument)); - // 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)); + enterVariableDeclaration = (ctx: VariableDeclarationContext) => { + const element = new VariableDeclarationStatementElement(ctx, this.document.textDocument); + element.declarations.forEach(declaration => this.document.registerElement(declaration)); }; enterUnexpectedEndOfLine = (ctx: UnexpectedEndOfLineContext) => { From a9d46ae9fccdf856cf196885a81f4238b93d69c0 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 23:02:39 +0800 Subject: [PATCH 28/29] Provide unique names to avoid duplicate triggering from other files --- test/fixtures/Diagnostics.bas | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/fixtures/Diagnostics.bas b/test/fixtures/Diagnostics.bas index eedef31..4a5f105 100644 --- a/test/fixtures/Diagnostics.bas +++ b/test/fixtures/Diagnostics.bas @@ -5,7 +5,7 @@ Attribute VB_Foo = False Option Explicit -Public Sub Foo() +Public Sub UniqueNameFoo() Dim i As Long While True i = i ++ 1 @@ -13,9 +13,9 @@ Public Sub Foo() Wend End Sub -Sub Identifier() -Attribute Identifier.VB_Description = "Dosctring." -Attribute Identifier.VB_Description = "Dosctring." +Sub UniqueNameIdentifier() +Attribute UniqueNameIdentifier.VB_Description = "Dosctring." +Attribute UniqueNameIdentifier.VB_Description = "Dosctring." ' Dosctring. ' ' Args: @@ -25,7 +25,7 @@ Attribute Identifier.VB_Description = "Dosctring." ' End Sub -Public Enum EnumFoo +Public Enum UniqueNameEnumFoo Enum1 Enum2 Enum3 From 1ce199e6c553dcfecc42a4974f065e2364fcbbe5 Mon Sep 17 00:00:00 2001 From: sslinky <39886505+SSlinky@users.noreply.github.com> Date: Fri, 11 Apr 2025 23:05:16 +0800 Subject: [PATCH 29/29] 1.5.8 --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4eb8367..339ecbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "1.5.7", + "version": "1.5.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.5.7", + "version": "1.5.8", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 485599f..0a44e5b 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "icon": "images/vba-lsp-icon.png", "author": "SSlinky", "license": "MIT", - "version": "1.5.7", + "version": "1.5.8", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" @@ -223,4 +223,4 @@ "typescript": "^5.8.3", "vscode-tmgrammar-test": "^0.1.3" } -} \ No newline at end of file +}