From 85f7e939a56736897c110ab1a050a5c53c62869c Mon Sep 17 00:00:00 2001 From: sslinky Date: Fri, 17 May 2024 20:09:33 +0800 Subject: [PATCH 01/29] Fixed bug causing : to break strings --- client/src/syntaxes/vba.tmLanguage.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/src/syntaxes/vba.tmLanguage.yaml b/client/src/syntaxes/vba.tmLanguage.yaml index 41a826d..4b4fe16 100644 --- a/client/src/syntaxes/vba.tmLanguage.yaml +++ b/client/src/syntaxes/vba.tmLanguage.yaml @@ -19,10 +19,10 @@ repository: - include: "#mthdSig" - include: "#variableDeclarations" - include: "#block" - - include: "#testing" + - include: "#testing" repository: moduleLines: - match: "(.*):" + match: '([^\n:]*)?:(?=([^"]*"[^"]*")*[^"]*$)' captures: 1: patterns: @@ -52,8 +52,7 @@ repository: repository: string: name: string.quoted.double.vba - begin: "\"" - end: "\"" + match: '"[^\r\n]*"' boolean: name: constant.language.boolean.vba match: "(?i)(true|false)" From c6afd608106d4151bec088c6e2b61d7a339ca3c3 Mon Sep 17 00:00:00 2001 From: sslinky Date: Sun, 19 May 2024 15:18:20 +0800 Subject: [PATCH 02/29] refactor server and capabilities --- ...vbaSemanticTokens.ts => semanticTokens.ts} | 0 server/src/capabilities/workspaceFolder.ts | 20 ++ server/src/project/workspace.ts | 143 ++++++++ server/src/server.ts | 319 +++--------------- server/src/utils/diagnositics.ts | 42 +++ 5 files changed, 260 insertions(+), 264 deletions(-) rename server/src/capabilities/{vbaSemanticTokens.ts => semanticTokens.ts} (100%) create mode 100644 server/src/capabilities/workspaceFolder.ts create mode 100644 server/src/project/workspace.ts create mode 100644 server/src/utils/diagnositics.ts diff --git a/server/src/capabilities/vbaSemanticTokens.ts b/server/src/capabilities/semanticTokens.ts similarity index 100% rename from server/src/capabilities/vbaSemanticTokens.ts rename to server/src/capabilities/semanticTokens.ts diff --git a/server/src/capabilities/workspaceFolder.ts b/server/src/capabilities/workspaceFolder.ts new file mode 100644 index 0000000..d42d09c --- /dev/null +++ b/server/src/capabilities/workspaceFolder.ts @@ -0,0 +1,20 @@ +import { ClientCapabilities, InitializeResult } from 'vscode-languageserver'; +import { LanguageServerConfiguration } from '../server'; + + +export function hasConfigurationCapability(languageServerConfiguration: LanguageServerConfiguration) { + const capabilities = languageServerConfiguration.params.capabilities; + return !!(capabilities.workspace && !!capabilities.workspace.configuration); +} + +function hasWorkspaceFolderCapability(x: ClientCapabilities) { + return !!(x.workspace && !!x.workspace.workspaceFolders); +} + +export function activateWorkspaceFolderCapability(capabilities: ClientCapabilities, result: InitializeResult) { + if (hasWorkspaceFolderCapability(capabilities)) { + result.capabilities.workspace = { + workspaceFolders: { supported: true } + }; + } +} diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts new file mode 100644 index 0000000..1974285 --- /dev/null +++ b/server/src/project/workspace.ts @@ -0,0 +1,143 @@ +import { CompletionItem, CompletionParams, DidChangeConfigurationNotification, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbolParams, FoldingRange, FoldingRangeParams, Hover, HoverParams, SymbolInformation, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver'; +import { IProjectDocument } from './document'; +import { LanguageServerConfiguration } from '../server'; +import { sleep } from '../utils/helpers'; +import { hasConfigurationCapability } from '../capabilities/workspaceFolder'; + + +/** + * Organises project documents and runs actions + * at a workspace level. + */ +export class Workspace { + private events: WorkspaceEvents; + private documents: IProjectDocument[] = []; + private publicScopeDeclarations: Map = new Map(); + + readonly connection: _Connection; + + // constructor(connection: _Connection, capabilities: LanguageServerCapabilities) { + constructor(params: {connection: _Connection, capabilities: LanguageServerConfiguration}) { + this.connection = params.connection; + this.events = new WorkspaceEvents({ + workspace: this, + connection: params.connection, + configuration: params.capabilities, + }); + } + + /** + * Registers a declaration or pushes an ambiguous name diagnostic. + */ + registerNamedElementDeclaration(element: any) { + // Check document names for existing entry. + // Check for public scope for existing entry. + + throw new Error("Not implemented"); + } + + /** + * Pushes an overriding name diagnostic if overriding public scope. + * @param element The element to check. + */ + checkNamedElementDeclaration(element: any) { + throw new Error("Not implemented"); + } + +} + +class WorkspaceEvents { + readonly workspace: Workspace; + readonly configuration: LanguageServerConfiguration; + + constructor(params: {connection: _Connection, workspace: Workspace, configuration: LanguageServerConfiguration}) { + this.workspace = params.workspace; + this.configuration = params.configuration; + this.initialiseConnectionEvents(params.connection); + } + + private initialiseConnectionEvents(connection: _Connection) { + connection.onInitialized(() => this.onInitialized()); + connection.onCompletion(params => this.onCompletion(params)); + connection.onCompletionResolve(item => this.onCompletionResolve(item)); + connection.onDidChangeConfiguration(params => this.onDidChangeConfiguration(params)); + connection.onDidChangeWatchedFiles(params => this.onDidChangeWatchedFiles(params)); + connection.onDocumentSymbol((params) => this.onDocumentSymbolAsync(params)); + connection.onHover(params => this.onHover(params)); + + if (hasConfigurationCapability(this.configuration)) { + connection.onFoldingRanges((params) => this.onFoldingRanges(params)); + } + + connection.onRequest((method: string, params: object | object[] | any) => { + switch (method) { + case 'textDocument/semanticTokens/full': { + // const stp = params as SemanticTokensParams; + // return docInfo.getSemanticTokens(stp); + break; + } + case 'textDocument/semanticTokens/range': + // return docInfo.getSemanticTokens(params); + break; + default: + // console.error(`Unresolved request path: ${method}`); + } + }); + } + + /** Connection event handlers */ + + private onCompletion(params: CompletionParams): never[] { + console.log('onCompletion: ' + params); + return []; + } + + private onCompletionResolve(item: CompletionItem): CompletionItem { + console.log('onCompletionResolve: ' + item.label); + return item; + } + + private onDidChangeConfiguration(params: DidChangeConfigurationParams): void { + console.log('onDidChangeConfiguration: ' + params); + } + + private onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) { + console.log('onDidChangeWatchedFiles: ' + params); + } + + // TODO: Should trigger a full workspace refresh. + private onDidChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent) { + console.log('onDidChangeWorkspaceFolders: ' + e); + this.workspace.connection.console.log('Workspace folder change event received.\n' + e); + } + + private async onDocumentSymbolAsync(params: DocumentSymbolParams): Promise { + console.log('onDocumentSymbolAsync: ' + params); + await sleep(1); + return []; + } + + private onFoldingRanges(params: FoldingRangeParams): FoldingRange[] { + console.log('onFoldingRanges: ' + params.textDocument.uri); + return []; + } + + private onHover(params: HoverParams): Hover { + console.log('onHover: ' + params.position.line + ',' + params.position.character); + return { contents: '' }; + } + + private onInitialized(): void { + console.log('onInitialized:---'); + const connection = this.workspace.connection; + // Register for client configuration notification changes. + connection.client.register(DidChangeConfigurationNotification.type, undefined); + + // This is how we can listen for changes to workspace folders. + if (hasConfigurationCapability(this.configuration)) { + connection.workspace.onDidChangeWorkspaceFolders(e => + this.onDidChangeWorkspaceFolders(e) + ); + } + } +} \ No newline at end of file diff --git a/server/src/server.ts b/server/src/server.ts index 39e27b2..eb0b719 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -9,280 +9,71 @@ import { DidChangeConfigurationNotification, TextDocumentSyncKind, InitializeResult, - SemanticTokensParams, + ServerCapabilities, } from 'vscode-languageserver/node'; -// import { LanguageTools } from './capabilities/ast'; -import { activateSemanticTokenProvider } from './capabilities/vbaSemanticTokens'; -import { ProjectInformation } from './docInfo'; - -// Create a connection for the server, using Node's IPC as a transport. -// Also include all preview / proposed LSP features. -const connection = createConnection(ProposedFeatures.all); - -// Create a simple text document manager. -// const documents: TextDocuments = new TextDocuments(TextDocument); - -// Create the AST tools. -// const service = new LanguageTools(); - -let hasConfigurationCapability = false; -let hasWorkspaceFolderCapability = false; -let hasDiagnosticRelatedInformationCapability = false; - -let docInfo: ProjectInformation; - -connection.onInitialize((params: InitializeParams) => { - const capabilities = params.capabilities; - - // Does the client support the `workspace/configuration` request? - // If not, we fall back using global settings. - hasConfigurationCapability = !!( - capabilities.workspace && !!capabilities.workspace.configuration - ); - hasWorkspaceFolderCapability = !!( - capabilities.workspace && !!capabilities.workspace.workspaceFolders - ); - hasDiagnosticRelatedInformationCapability = !!( - capabilities.textDocument && - capabilities.textDocument.publishDiagnostics && - capabilities.textDocument.publishDiagnostics.relatedInformation - ); +import { Workspace } from './project/workspace'; +import { activateSemanticTokenProvider } from './capabilities/semanticTokens'; +import { activateWorkspaceFolderCapability } from './capabilities/workspaceFolder'; + + +class LanguageServer { + workspace?: Workspace; + configuration?: LanguageServerConfiguration; + readonly connection; + + constructor() { + this.connection = createConnection(ProposedFeatures.all); + this.connection.onInitialize((params: InitializeParams) => { + // Set up the workspace. + this.configuration = new LanguageServerConfiguration(params); + const workspace = new Workspace({ + connection: this.connection, + capabilities: this.configuration + }); + this.workspace = workspace; + + // Set up the connection result. + // Update this to make use of the LSCapabilities data class. + const result = new ConnectionInitializeResult(this.configuration.capabilities); + activateWorkspaceFolderCapability(params.capabilities, result); + activateSemanticTokenProvider(result); + return result; + }); + this.connection.onInitialized(() => { + // Register for client configuration notification changes. + this.connection.client.register(DidChangeConfigurationNotification.type, undefined); + + }); - docInfo = new ProjectInformation( - connection, - hasConfigurationCapability, - hasWorkspaceFolderCapability, - hasDiagnosticRelatedInformationCapability); + this.connection.listen(); + } +} - const result: InitializeResult = { - capabilities: { - textDocumentSync: TextDocumentSyncKind.Incremental, - // Tell the client that this server supports code completion. - completionProvider: { - resolveProvider: false - }, - foldingRangeProvider: true, - hoverProvider: true, - documentSymbolProvider: true - } +export class LanguageServerConfiguration { + params: InitializeParams; + capabilities: ServerCapabilities = { + hoverProvider: false, + textDocumentSync: TextDocumentSyncKind.Incremental, + completionProvider: { resolveProvider: false }, + foldingRangeProvider: false, + documentSymbolProvider: false, }; - activateSemanticTokenProvider(result); - if (hasWorkspaceFolderCapability) { - result.capabilities.workspace = { - workspaceFolders: { - supported: true - } - }; - } - return result; -}); -connection.onInitialized(() => { - if (hasConfigurationCapability) { - // Register for all configuration changes. - connection.client.register(DidChangeConfigurationNotification.type, undefined); + constructor(params: InitializeParams) { + this.params = params; } - if (hasWorkspaceFolderCapability) { - connection.workspace.onDidChangeWorkspaceFolders(e => { - connection.console.log('Workspace folder change event received.'); - }); - } -}); - -// The example settings -interface ExampleSettings { - maxNumberOfProblems: number; } -// The global settings, used when the `workspace/configuration` request is not supported by the client. -// Please note that this is not the case when using this server with the client provided in this example -// but could happen with other clients. -// const defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 }; -// let globalSettings: ExampleSettings = defaultSettings; - -// Cache the settings of all open documents -// const documentSettings: Map> = new Map(); - -// connection.onDidChangeConfiguration(change => { -// if (hasConfigurationCapability) { -// // Reset all cached document settings -// documentSettings.clear(); -// } else { -// globalSettings = ( -// (change.settings.languageServerExample || defaultSettings) -// ); -// } - -// // Revalidate all open text documents -// docInfo.docs.all().forEach(validateTextDocument); -// }); - - +class ConnectionInitializeResult implements InitializeResult { + [custom: string]: any; + capabilities: ServerCapabilities; + serverInfo?: { name: string; version?: string | undefined; } | undefined; - -// // Only keep settings for open documents -// documents.onDidClose(e => { -// documentSettings.delete(e.document.uri); -// }); - -// documents.onDidOpen(e => { -// service.evaluate(e.document); -// }); - -// // The content of a text document has changed. This event is emitted -// // when the text document first opened or when its content has changed. -// documents.onDidChangeContent(change => { -// // validateTextDocument(change.document); -// service.evaluate(change.document); -// }); - -// async function validateTextDocument(textDocument: TextDocument): Promise { -// const settings = await getDocumentSettings(textDocument.uri); -// const v = new DocumentValidator(textDocument, settings.maxNumberOfProblems); - -// service.evaluate(textDocument); - -// // Validate use of .Select -// v.validate(/\.Select/g, "Don't use `Select`. Please.", DiagnosticSeverity.Warning); - - -// // Send the computed diagnostics to VSCode. -// const diagnostics: Diagnostic[] = v.diagnostics; -// connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); -// } - -// class DocumentValidator { -// doc: TextDocument; -// text: string; -// problemLimit: integer; -// diagnostics: Diagnostic[]; - -// constructor(textDocument: TextDocument, problemLimit: integer) { -// this.doc = textDocument; -// this.text = textDocument.getText(); -// this.problemLimit = problemLimit; -// this.diagnostics = []; -// } - -// validate(pattern: RegExp, description: string, severity: DiagnosticSeverity) { -// let m: RegExpExecArray | null; -// while ((m = pattern.exec(this.text)) && this.problemLimit > 0) { -// this.problemLimit--; -// const d: Diagnostic = { -// severity: severity, -// range: { -// start: this.doc.positionAt(m.index + 1), -// end: this.doc.positionAt(m.index + m[0].length) -// }, -// message: description, -// source: 'sslinky-vba', -// code: 666 -// }; -// if (hasDiagnosticRelatedInformationCapability) { -// d.relatedInformation = [ -// { -// location: { -// uri: this.doc.uri, -// range: Object.assign({}, d.range) -// }, -// message: 'Seriously though, don\'t use it.' -// }, -// { -// location: { -// uri: this.doc.uri, -// range: Object.assign({}, d.range) -// }, -// message: 'Ever.' -// } -// ]; -// } -// this.diagnostics.push(d); -// } -// } -// } - - -// This handler provides the initial list of the completion items. -// connection.onCompletion( -// (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { -// // The pass parameter contains the position of the text document in -// // which code complete got requested. For the example we ignore this -// // info and always provide the same completion items. -// return [ -// { -// label: 'TypeScript', -// kind: CompletionItemKind.Text, -// data: 1 -// }, -// { -// label: 'JavaScript', -// kind: CompletionItemKind.Text, -// data: 2 -// } -// ]; -// } -// ); - -// This handler resolves additional information for the item selected in -// the completion list. -// connection.onCompletionResolve( -// (item: CompletionItem): CompletionItem => { -// if (item.data === 1) { -// item.detail = 'TypeScript details'; -// item.documentation = 'TypeScript documentation'; -// } else if (item.data === 2) { -// item.detail = 'JavaScript details'; -// item.documentation = 'JavaScript documentation'; -// } -// return item; -// } -// ); - -// Make the text document manager listen on the connection -// for open, change and close text document events -// documents.listen(connection); - -// Listen on the connection -connection.listen(); - -// // Event handler for folding ranges. -// connection.onFoldingRanges((params) => { -// const doc = documents.get(params.textDocument.uri); -// if (doc) { -// return getFoldingRanges(doc, Number.MAX_VALUE); -// } -// }); - -// Event pass-through to keep server.ts simple. -connection.onDidChangeWatchedFiles(c => docInfo.onDidChangeWatchedFiles(c)); -connection.onDidChangeConfiguration((change => docInfo.onDidChangeConfiguration(change))); - -connection.onHover((params) => docInfo.getHover(params)); -connection.onCompletion((params) => docInfo.getCompletion(params)); -connection.onFoldingRanges((params) => docInfo.getFoldingRanges(params.textDocument.uri)); -connection.onDocumentSymbol((params) => docInfo.getDocumentSymbols(params.textDocument.uri)); -connection.onCompletionResolve((item) => docInfo.getCompletionResolve(item)); - -connection.onRequest((method: string, params: object | object[] | any) => { - switch (method) { - case 'textDocument/semanticTokens/full': { - const stp = params as SemanticTokensParams; - return docInfo.getSemanticTokens(stp); - } - case 'textDocument/semanticTokens/range': - return docInfo.getSemanticTokens(params); - default: - console.error(`Unresolved request path: ${method}`); + constructor(capabilities: ServerCapabilities) { + this.capabilities = capabilities; } -}); - -// connection.onCodeAction; +} -// connection.onDocumentSymbol((params) => { -// const doc = documents.get(params.textDocument.uri); -// if (doc) { -// return service.symbolProvider.Symbols(); -// } -// }); \ No newline at end of file +const languageServer = new LanguageServer(); diff --git a/server/src/utils/diagnositics.ts b/server/src/utils/diagnositics.ts new file mode 100644 index 0000000..59326fa --- /dev/null +++ b/server/src/utils/diagnositics.ts @@ -0,0 +1,42 @@ +import { CompletionItem, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbol, FoldingRange, Hover, HoverParams, Location, NotificationHandler, Position, PublishDiagnosticsParams, Range, SemanticTokenModifiers, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRequest, SymbolInformation, SymbolKind, TextDocumentPositionParams, TextDocuments, uinteger, _Connection, integer } from 'vscode-languageserver'; + + +abstract class BaseDiagnostic { + description: string; + severity: DiagnosticSeverity; + errCode: integer; + + constructor() { + this.description = "Not assigned!"; + this.severity = DiagnosticSeverity.Information; + this.errCode = 0; + } + + create(range: Range, relatedInfo?: DiagnosticRelatedInformation[] | undefined) { + return Diagnostic.create( + range, + this.description, + this.severity, + this.errCode, + "VBAPro", + relatedInfo + ); + } +} + +export class AmbiguousDeclarationDiagnostic extends BaseDiagnostic { + description = "Ambiguous variable declaration."; + severity = DiagnosticSeverity.Error; + errCode = 500; + +} + +export class MissingDeclarationDiagnostic extends BaseDiagnostic { + description = "No declaration for variable or method."; + errCode = 404; + + constructor (severity: DiagnosticSeverity) { + super(); + this.severity = severity; + } +} \ No newline at end of file From af8314942b60961ca2a5ee105c4553fc5537e8db Mon Sep 17 00:00:00 2001 From: sslinky Date: Sun, 19 May 2024 15:20:29 +0800 Subject: [PATCH 03/29] new diagnostics capability --- server/src/capabilities/diagnostics.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 server/src/capabilities/diagnostics.ts diff --git a/server/src/capabilities/diagnostics.ts b/server/src/capabilities/diagnostics.ts new file mode 100644 index 0000000..1b552c5 --- /dev/null +++ b/server/src/capabilities/diagnostics.ts @@ -0,0 +1,6 @@ +import { TextDocumentClientCapabilities } from 'vscode-languageserver'; + + +function hasDiagnosticRelatedInformationCapability(x: TextDocumentClientCapabilities) { + return !!(x && x.publishDiagnostics && x.publishDiagnostics.relatedInformation); +} \ No newline at end of file From 7cedd3ba15ce8d0fbfb5f6a7b7a0259ae0505e8c Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 10:02:07 +0800 Subject: [PATCH 04/29] Updated snippets --- snippets/vba.json | 241 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 199 insertions(+), 42 deletions(-) diff --git a/snippets/vba.json b/snippets/vba.json index 3c7f9df..ddcc259 100644 --- a/snippets/vba.json +++ b/snippets/vba.json @@ -6,6 +6,7 @@ "Private m${1:PropertyName} As ${2:PropertyType}", "Public Property Let $1(var As $2)", "Attribute $1.VB_Description = \"${3:Dosctring}.\"", + "' $3.", " m$1 = var", "End Property", "", @@ -16,7 +17,6 @@ "$0" ] }, - "Public Property Set": { "prefix": "propset", "description": "Property set and get with backing store", @@ -24,42 +24,54 @@ "Private m${1:PropertyName} As ${2:PropertyType}", "Public Property Set $1(var As $2)", "Attribute $1.VB_Description = \"${3:Dosctring}.\"", - " Set m$1 = var", + "' $3.", + " Set m$1 = var", "End Property", "", "Public Property Get $1() As $2", - " Set $1 = m$1", + " Set $1 = m$1", "End Property", "", "$0" ] }, - + "Public Property Get": { + "prefix": "propget", + "description": "Property get only", + "body": [ + "Public Property Get ${1:PropertyName}() As ${2:PropertyType}", + "Attribute $1.VB_Description = \"${3:Dosctring}.\"", + "' $3.", + " $0", + "End Property", + "" + ] + }, "Subroutine": { "prefix": "sub", "description": "Subroutine", "body": [ "${1:Public }Sub ${2:Identifier}($3)", "Attribute $2.VB_Description = \"${4:Dosctring}.\"", - "' $4.", + "' $4.", "'", "' Args:", "' param1:", "'", "' Raises:", "'", + "", " $0", "End Sub" ] }, - "Function": { "prefix": "func", - "description": "Subroutine", + "description": "Function", "body": [ "${1:Public }Function ${2:Identifier}($3) As $4", "Attribute $2.VB_Description = \"${5:Dosctring}.\"", - "' $5.", + "' $5.", "'", "' Args:", "' param1:", @@ -68,13 +80,104 @@ "'", "' Raises:", "'", + " Dim result As $4", " $0", + "", + " $2 = result", "End Function" ] }, - + "EnumExcl": { + "prefix": "enumexcl", + "description": "Exclusive Enum", + "body": [ + "Public Enum ${1:Identifier}", + " ${2:Enum1}", + " ${3:Enum2}", + " ${4:Enum3}", + "End Enum" + ] + }, + "EnumIncl": { + "prefix": "enumincl", + "description": "Inclusive Enum", + "body": [ + "Public Enum ${1:Identifier}", + " ${2:Enum1} = 2 ^^ 1", + " ${3:Enum2} = 2 ^^ 2", + " ${4:Enum3} = 2 ^^ 3", + "End Enum" + ] + }, + "ForLoop": { + "prefix": "fori", + "description": "For Loop", + "body": [ + "${4:Dim $1 As Long}", + "For ${1:i} = ${2:low} To ${3:high}", + " $0", + "Next $1" + ] + }, + "ForEachLoop": { + "prefix": "foreach", + "description": "For Each Loop", + "body": [ + "For Each ${1:object} In ${2:collection}", + " $0", + "Next $1" + ] + }, + "If": { + "prefix": "ifblock", + "description": "If Block", + "body": [ + "If ${1:condition} Then", + " $0", + "End If" + ] + }, + "IfElse": { + "prefix": "ifelse", + "description": "If Else Block", + "body": [ + "If ${1:condition} Then", + " $0", + "Else", + "End If" + ] + }, + "TypeCheckAssignment": { + "prefix": "typecheck", + "description": "Object type check assignment", + "body": [ + "If IsObject(${1:obj?}) Then", + " Set ${2:Assignment}", + "Else", + " $2", + "End If$0" + ] + }, + "Case": { + "prefix": "case", + "description": "Select Case", + "body": [ + "Select Case $1", + " Case Is = $2:", + " $0", + " Case Else:", + "End Select" + ] + }, + "Event": { + "prefix": "event", + "description": "Event", + "body": [ + "Public Event ${1:Identifier}($3)" + ] + }, "Constructor": { - "prefix": "init", + "prefix": "ctor", "description": "Class_Initialize", "body": [ "Private Sub Class_Initialize()", @@ -82,7 +185,15 @@ "End Sub" ] }, - + "Destructor": { + "prefix": "dtor", + "description": "Class_Terminate", + "body": [ + "Private Sub Class_Terminate()", + " $0", + "End Sub" + ] + }, "Base Class Template": { "prefix": "class", "description": "Basic class", @@ -92,17 +203,46 @@ " MultiUse = -1 'True", "END", "Attribute VB_Name = \"${1:ClassName}\"", + "Attribute VB_Description = \"${2:Class description goes here}\"", "Attribute VB_GlobalNameSpace = False", "Attribute VB_Creatable = False", "Attribute VB_PredeclaredId = False", "Attribute VB_Exposed = False", + "' Copyright 2024 Sam Vanderslink", + "' ", + "' Permission is hereby granted, free of charge, to any person obtaining a copy ", + "' of this software and associated documentation files (the \"Software\"), to deal ", + "' in the Software without restriction, including without limitation the rights ", + "' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ", + "' copies of the Software, and to permit persons to whom the Software is ", + "' furnished to do so, subject to the following conditions:", + "' ", + "' The above copyright notice and this permission notice shall be included in ", + "' all copies or substantial portions of the Software.", + "' ", + "' THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ", + "' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ", + "' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ", + "' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ", + "' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ", + "' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ", + "' IN THE SOFTWARE.", + "", "Option Explicit", "$0", "'-------------------------------------------------------------------------------", "' Class: $1", - "' ${2:Class description goes here}", + "' $2", + "'-------------------------------------------------------------------------------", + "", + "' Enums", + "'-------------------------------------------------------------------------------", + "", + "", + "' Events", "'-------------------------------------------------------------------------------", "", + "", "' Private Backing Store", "'-------------------------------------------------------------------------------", "", @@ -133,39 +273,56 @@ "" ] }, - - "Select Case": { - "prefix": "case", - "description": "Select Case", - "body": [ - "Select Case $1", - " Case Is = $2:", - " $0", - " Case Else:", - "End Select" - ] - }, - - "Enum": { - "prefix": "enum", - "description": "Enum", + "Base Module Template": { + "prefix": "module", + "description": "Basic module", "body": [ - "Public Enum ${1:Name}", - " ${2:Option}", - "End Enum" + "Attribute VB_Name = \"${1:ModuleName}\"", + "' Copyright 2024 Sam Vanderslink", + "' ", + "' Permission is hereby granted, free of charge, to any person obtaining a copy ", + "' of this software and associated documentation files (the \"Software\"), to deal ", + "' in the Software without restriction, including without limitation the rights ", + "' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ", + "' copies of the Software, and to permit persons to whom the Software is ", + "' furnished to do so, subject to the following conditions:", + "' ", + "' The above copyright notice and this permission notice shall be included in ", + "' all copies or substantial portions of the Software.", + "' ", + "' THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ", + "' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ", + "' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ", + "' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ", + "' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ", + "' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ", + "' IN THE SOFTWARE.", + "", + "Option Explicit", + "$0" ] }, - - "EnumMulti": { - "prefix": "enummulti", - "description": "Enum with multi select", + "Unit Test Template": { + "prefix": "test", + "description": "Unit test basic template", "body": [ - "Public Enum ${1:Name}", - " ${2:Option} = 2", - " ${3:Option} = 4", - " ${4:Option} = 8", - " ${5:Option} = 16", - "End Enum" + "Private Function TestList_${1:Identifier}() As TestResult", + "Attribute TestList_$1.VB_Description = \"${2:Dosctring}.\"", + "' $2.", + " Dim tr As New TestResult", + "", + "' Arrange", + " $0", + "", + "' Act", + "", + "", + "' Assert", + "", + "", + "Finally:", + " Set TestList_$1 = tr", + "End Function" ] } -} \ No newline at end of file +} From 3cd9113f5b34406de89578b3bdd1c306a297952f Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 10:23:51 +0800 Subject: [PATCH 05/29] Remove files relating to old version --- server/src/docInfo.ts | 577 ------------------ server/src/parser/document/scope.jsonc | 51 -- server/src/parser/document/scope.ts | 128 ---- server/src/parser/document/symbols.ts | 22 - server/src/parser/elements/base.ts | 191 ------ server/src/parser/elements/memory/base.ts | 81 --- .../src/parser/elements/memory/constants.ts | 19 - server/src/parser/elements/memory/events.ts | 19 - .../src/parser/elements/memory/functions.ts | 18 - .../src/parser/elements/memory/references.ts | 20 - server/src/parser/elements/memory/types.ts | 23 - .../src/parser/elements/memory/variables.ts | 31 - server/src/parser/elements/method.ts | 121 ---- server/src/parser/elements/module.ts | 60 -- server/src/parser/elements/variable.ts | 228 ------- server/src/parser/elements/vbLang.ts | 79 --- server/src/parser/vbaSyntaxParser.ts | 225 ------- server/src/scope.json | 60 -- server/src/scope.ts | 67 -- 19 files changed, 2020 deletions(-) delete mode 100644 server/src/docInfo.ts delete mode 100644 server/src/parser/document/scope.jsonc delete mode 100644 server/src/parser/document/scope.ts delete mode 100644 server/src/parser/document/symbols.ts delete mode 100644 server/src/parser/elements/base.ts delete mode 100644 server/src/parser/elements/memory/base.ts delete mode 100644 server/src/parser/elements/memory/constants.ts delete mode 100644 server/src/parser/elements/memory/events.ts delete mode 100644 server/src/parser/elements/memory/functions.ts delete mode 100644 server/src/parser/elements/memory/references.ts delete mode 100644 server/src/parser/elements/memory/types.ts delete mode 100644 server/src/parser/elements/memory/variables.ts delete mode 100644 server/src/parser/elements/method.ts delete mode 100644 server/src/parser/elements/module.ts delete mode 100644 server/src/parser/elements/variable.ts delete mode 100644 server/src/parser/elements/vbLang.ts delete mode 100644 server/src/parser/vbaSyntaxParser.ts delete mode 100644 server/src/scope.json delete mode 100644 server/src/scope.ts diff --git a/server/src/docInfo.ts b/server/src/docInfo.ts deleted file mode 100644 index 9d76a4c..0000000 --- a/server/src/docInfo.ts +++ /dev/null @@ -1,577 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; - -import { MethodElement } from './parser/elements/method'; -import { ModuleElement } from './parser/elements/module'; -import { sortSemanticTokens } from './capabilities/vbaSemanticTokens'; -import { sleep, rangeIsChildOfElement } from './utils/helpers'; -import { FoldableElement, SyntaxElement } from './parser/elements/base'; -import { ResultsContainer, SyntaxParser } from './parser/vbaSyntaxParser'; -import { VariableAssignElement, VariableDeclarationElement, VariableStatementElement } from './parser/elements/variable'; - -import { CompletionItem, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbol, FoldingRange, Hover, HoverParams, Location, NotificationHandler, Position, PublishDiagnosticsParams, Range, SemanticTokenModifiers, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRequest, SymbolInformation, SymbolKind, TextDocumentPositionParams, TextDocuments, uinteger, _Connection } from 'vscode-languageserver'; - -declare global { - interface Map { - has

(key: P): this is { get(key: P): V } & this - } -} - -interface ExampleSettings { - maxNumberOfProblems: number; -} - - -enum NameLinkType { - 'variable' = 0, - 'method' = 1, -} - -const defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 }; -let globalSettings: ExampleSettings = defaultSettings; - -export class ProjectInformation { - readonly conn: _Connection; - readonly docs: TextDocuments = new TextDocuments(TextDocument); - readonly docInfos: Map = new Map(); - readonly docSettings: Map> = new Map(); - - private hasConfigurationCapability = false; - private hasWorkspaceFolderCapability = false; - private hasDiagnosticRelatedInformationCapability = false; - - private readonly syntaxUtil: SyntaxParser; - private readonly scopes: Scope = new Scope(); - - constructor(conn: _Connection, hasCfg: boolean, hasWsFld: boolean, hasDiag: boolean) { - this.conn = conn; - this.syntaxUtil = new SyntaxParser(); - this.hasConfigurationCapability = hasCfg; - this.hasWorkspaceFolderCapability = hasWsFld; - this.hasDiagnosticRelatedInformationCapability = hasDiag; - this.addEventHandlers(); - - this.docs.listen(conn); - } - - - onDidChangeConfiguration: NotificationHandler = (change => { - if (this.hasConfigurationCapability) { - this.docSettings.clear(); - } else { - globalSettings = ( - (change.settings.languageServerExample || defaultSettings) - ); - } - - // Revalidate all open text documents - // this.docs.all().forEach(validateTextDocument); - }); - - onDidChangeWatchedFiles: NotificationHandler = (params) => { - this.conn.console.log(`onDidChangeWatchedFiles: ${params}`); - }; - - getFoldingRanges(docUri: string) { - return this.docInfos.get(docUri)?.getFoldingRanges() ?? []; - } - - async getDocumentSymbols(docUri: string): Promise { - const docInfo = this.docInfos.get(docUri); - if (docInfo) { - while (docInfo.isBusy) { - await sleep(50); - } - return docInfo!.getSymbols(docUri); - } - return []; - } - - // TODO: Implement - getCompletion(params: TextDocumentPositionParams) { - return []; - } - - // TODO: Implement - getCompletionResolve(item: CompletionItem): CompletionItem { - return item; - } - - getHover({ textDocument, position }: HoverParams): Hover { - return this.docInfos.get(textDocument.uri)?.getHover(position) ?? { contents: '' }; - } - - getSemanticTokens(f: SemanticTokensParams): SemanticTokens | null; - getSemanticTokens(r: SemanticTokensRangeParams): SemanticTokens | null; - getSemanticTokens(f?: SemanticTokensParams, r?: SemanticTokensRangeParams): SemanticTokens | null { - const range = r?.range; - const uri = f?.textDocument?.uri ?? r?.textDocument?.uri ?? ''; - return this.docInfos.get(uri)?.getSemanticTokens(range) ?? null; - } - - sendDiagnostics = (docInfo: DocumentInformation) => - this.conn.sendDiagnostics(docInfo.getDiagnostics()); - - private getDocumentSettings(docUri: string): Thenable { - if (!this.hasConfigurationCapability) { - return Promise.resolve(globalSettings); - } - let result = this.docSettings.get(docUri); - if (!result) { - result = this.conn.workspace.getConfiguration({ - scopeUri: docUri, - section: 'vbaLanguageServer' - }); - this.docSettings.set(docUri, result); - } - return result; - } - - private async addEventHandlers() { - this.docs.onDidClose(e => { - this.docSettings.delete(e.document.uri); - }); - - this.docs.onDidOpen(async e => { - const uri = e.document.uri; - const stg = await this.getDocumentSettings(uri); - this.docSettings.set(uri, Promise.resolve(stg)); - }); - - this.docs.onDidChangeContent(change => { - const doc = change.document; - const docInfo = new DocumentInformation(this.scopes, doc.uri); - this.scopes.getScope(`undeclared|${doc.uri}`).clear(); - this.docInfos.set(doc.uri, docInfo); - this.syntaxUtil.parse(doc, docInfo); - docInfo.finalise(); - this.sendDiagnostics(docInfo); - }); - } -} - -export class DocumentInformation implements ResultsContainer { - module?: ModuleElement; - elements: SyntaxElement[] = []; - attrubutes: Map = new Map(); - foldingRanges: FoldingRange[] = []; - isBusy = true; - - private docUri: string; - private ancestors: SyntaxElement[] = []; - private documentScope: Scope; - - constructor(scope: Scope, docUri: string) { - this.docUri = docUri; - scope.links.set(docUri, new Map()); - this.docUri = docUri; - this.documentScope = scope; - } - - addModule(emt: ModuleElement) { - this.module = emt; - this.elements.push(emt); - this.ancestors.push(emt); - } - - addFoldingRange(emt: FoldableElement) { - this.foldingRanges.push(emt.foldingRange()!); - } - - addElement(emt: SyntaxElement) { - // Find the parent element. - while (this.ancestors) { - const pnt = this.ancestors.pop()!; - if (emt.isChildOf(pnt)) { - emt.parent = pnt; - pnt.children.push(emt); - this.ancestors.push(pnt); - this.ancestors.push(emt); - break; - } - } - - // Add the element. - this.elements.push(emt); - this.ancestors.push(emt); - - // Also add identifier elements - if (emt.identifier) { - this.addElement(emt.identifier); - // emt.fqName = `${(emt.fqName ?? '')}.${emt.identifier.text}`; - } - - return this; - } - - /** - * Use this method to set as the current scope. - * @param emt The function, sub, or property to scope to. - */ - pushScopeElement(emt: MethodElement) { - this.addScopedDeclaration(emt); - } - - popScopeElement() { - // - } - - addScopedDeclaration(emt: MethodElement | VariableDeclarationElement) { - // Add a declared scope. - const elId = emt.identifier!.text; - const link = this.getNameLink(elId, emt.parent?.namespace ?? '', emt.hasPrivateModifier); - link.declarations.push(emt); - - // Check the undeclared links and merge if found. - const undeclaredScope = this.scope.getScope(`undeclared|${this.docUri}`); - const undeclaredLink = undeclaredScope.get(elId); - if (undeclaredLink) { - link.merge(undeclaredLink); - undeclaredScope.delete(elId); - } - } - - // addScopeReference(emt: VariableAssignElement) { - // const link = this.getNameLink(emt.identifier!.text, emt.parent?.namespace ?? '', false, true); - // link.references.push(emt); - // } - - /** - * Creates scope references for the left and right sides - * of the variable assignment if they exist. - * @param emt the variable assignment element. - */ - addScopeReferences(emt: VariableAssignElement) { - throw new Error("Not implemented exception"); - } - - private getNameLink(identifier: string, fqName: string, isPrivate = false, searchScopes = false): NameLink { - const targetScope = searchScopes ? this.searchScopes(identifier) : this.getScope(fqName, isPrivate); - - // Return an existing link. - if (targetScope.has(identifier)) { - return targetScope.get(identifier); - } - - // Create a new link and return it. - const link = new NameLink(); - targetScope.set(identifier, link); - return link; - } - - private getScope(fqName: string, isPrivate: boolean): Map { - const globalScope = this.scope.getScope('global'); - const localScope = this.scope.getScope(this.docUri); - - const isAtModuleLevel = !(fqName ?? '').includes('.'); - return (isAtModuleLevel && !isPrivate) ? globalScope : localScope; - } - - private searchScopes(identifier: string): Map { - const scopeNames = [this.docUri, 'global']; - for (let i = 0; i < scopeNames.length; i++) { - const scope = this.scope.getScope(scopeNames[i]); - if (scope.has(identifier)) { - return scope; - } - } - return this.scope.getScope(`undeclared|${this.docUri}`); - } - - - finalise() { - // TODO: Intelligently pass opt. explicit. - this.documentScope.processLinks(this.docUri, true); - this.isBusy = false; - } - - // setModuleAttribute = (attr: ModuleAttribute) => - // this.attrubutes.set(attr.key(), attr.value()); - - // setModuleIdentifier(ctx: LiteralContext, doc: TextDocument) { - // if (this.module) - // this.module.identifier = new IdentifierElement(ctx, doc); - // } - - getHover = (p: Position) => - this.getElementAtPosition(p)?.hover(); - - getSemanticTokens(range?: Range): SemanticTokens | null { - const r = range ?? this.module?.range; - if (!r) - return null; - - const semanticTokens = sortSemanticTokens( - this.getElementsInRange(r) - .filter((e) => !!e.identifier?.semanticToken) - .map((e) => e.identifier!.semanticToken!)); - - if (semanticTokens.length === 0) - return null; - - let data: uinteger[] = []; - let line = 0; - let char = 0; - semanticTokens.forEach((t) => { - data = data.concat(t.toDeltaToken(line, char)!); - line = t.line; - char = t.startChar; - }); - return { data: data }; - } - - private getElementAtPosition(p: Position): SyntaxElement | undefined { - const r = Range.create(p, p); - - // Filter eligible parents by range. - let parents = this.elements.filter((x) => rangeIsChildOfElement(r, x)); - if (parents.length === 0) { return; } - if (parents.length === 1) { console.log(`hover@${r.toString()}: ${parents[0].identifier?.text}`); return parents[0]; } - - // Narrow parents down to the one(s) with the narrowest row scope. - // In the incredibly unlikely case that we have two parents with the same number of rows - // and they span more than one row, then it's all too hard. Just pick one. - const minRows = Math.min(...parents.map((x) => x.range.end.line - x.range.start.line)); - parents = parents.filter((x) => x.range.end.line - x.range.start.line === minRows); - if (parents.length === 1 || minRows > 0) { - console.log(`hover@${this.rangeAddress(r)}: ${parents[0].toString()}`); - return parents[0]; - } - - // Narrow parents down to the one with the narrowest character scope. - const minChars = Math.min(...parents.map((x) => x.range.end.character - x.range.start.character)); - parents = parents.filter((x) => x.range.end.character - x.range.start.character === minChars); - return parents[0]; - } - - private getElementsInRange(r: Range): SyntaxElement[] { - const results: SyntaxElement[] = []; - if (r.start.line === r.end.line) { - this.elements.forEach((x) => { - if (x.range.start.character >= r.start.character && x.range.end.character <= r.end.character) - results.push(x); - }); - } else { - this.elements.forEach((x) => { - if (x.range.start.line >= r.start.line && x.range.end.line <= r.end.line) - results.push(x); - }); - } - - return results; - } - - getDiagnostics(): PublishDiagnosticsParams { - return { uri: this.docUri, diagnostics: this.elements.map((e) => e.diagnostics).flat(1) }; - } - - getSymbols = (uri: string): SymbolInformation[] => - this.elements - .filter((x) => (!!x.identifier) && (x.identifier.text !== '')) - .map((x) => x.symbolInformation(uri)) - .filter((x): x is SymbolInformation => !!x); - - // getFoldingRanges = (): (FoldingRange)[] => - // this.elements - // .filter((x) => !(x instanceof ModuleElement)) - // .map((x) => x.foldingRange()) - // .filter((x): x is FoldingRange => !!x); - - getFoldingRanges = (): (FoldingRange)[] => - this.foldingRanges; - - private rangeAddress(r: Range): string { - const sl = r.start.line; - const el = r.end.line; - const sc = r.start.character; - const ec = r.end.character; - - if(sl==el) { - return `${sl}:${sc}-${ec}`; - } - return `${sl}:${sc}-${el}:${ec}`; - } -} - -class Scope { - // { docUri: { identifier: [ declarationElement, elementLink... ] } } - links: Map> = new Map(); - private subScopes: string[] = []; - private currentDoc = ''; - - constructor() { - this.links.set('global', new Map()); - } - - /** - * Gets the scope related to the key. Lazy instantiates. - * @param key the key of the scope to get. - * @returns a Scope. - */ - getScope(key: string): Map { - if (key !== this.currentDoc) { - this.currentDoc = key; - this.subScopes = ['module']; - } - if (!this.links.has(key)) { - this.links.set(key, new Map()); - } - return this.links.get(key)!; - } - - pushSubScope(namespace: string) { - this.subScopes.push(namespace); - } - - popSubScope() { - this.subScopes.pop(); - } - - processLinks(key: string, optExplicit = false) { - // TODO: check global for undeclareds - // TODO: implement explicit paths, e.g. Module1.MyVar - const undeclared = this.getScope(`undeclared|${key}`); - const docScope = this.getScope(key); - undeclared.forEach((v, k) => docScope.set(k, v)); - docScope.forEach((x) => x.process(optExplicit)); - } -} - -class NameLink { - type: NameLinkType = NameLinkType.variable; - - // The original decalarations that affect this name. - // 0: Variable or method not declared. - // 1: Declared once. - // 2: Multiple conflicting declarations. - private _declarations: SyntaxElement[] = []; - declarations: SyntaxElement[] = []; - - // The places this name is referenced. - references: SyntaxElement[] = []; - diagnostics: Diagnostic[] = []; - - private diagnosticRelatedInfo: DiagnosticRelatedInformation[] = []; - - merge(link: NameLink) { - this.declarations.concat(link.declarations); - this.references.concat(link.references); - this.diagnostics.concat(link.diagnostics); - - if (link.declarations.length > 0) { - this.type = link.type; - } - } - - process(optExplicit = false) { - this.addDeclarationReferences(); - this.processDiagnosticRelatedInformation(); - this.validateDeclarationCount(optExplicit); - this.validateMethodSignatures(); - - this.assignSemanticTokens(); - this.assignDiagnostics(); - } - - private addDeclarationReferences() { - this.references.forEach((x) => this.addDecToRef(x)); - } - - private addDecToRef(ref: SyntaxElement) { - if(!(ref instanceof VariableStatementElement)) { - return; - } - const dec = this.declarations[0]; - if(dec instanceof MethodElement) { - ref.setDeclaredType(dec); - } - } - - private processDiagnosticRelatedInformation() { - this.diagnosticRelatedInfo = this.declarations - .concat(this.references) - .map((x) => DiagnosticRelatedInformation.create( - x.location(), - x.text - )); - } - - private validateDeclarationCount(optExplicit: boolean) { - if (this.declarations.length === 1) { - return; - } - - const undecSeverity = (optExplicit) ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; - if (this.declarations.length === 0) { - if (optExplicit || this.type !== NameLinkType.variable) - this.references.forEach((x) => - this.diagnostics.push(Diagnostic.create( - x.range, - "No declaration for variable or method.", - undecSeverity, - 404, - 'VbaPro', - this.diagnosticRelatedInfo - ))); - return; - } - this.declarations.forEach((x) => - this.diagnostics.push(Diagnostic.create( - x.range, - "Ambiguous variable declaration", - DiagnosticSeverity.Error, - 500, - 'VbaPro', - this.diagnosticRelatedInfo - ))); - } - - private assignSemanticTokens() { - if (this.declarations.length === 0) { - return; - } - - this.references.forEach((x) => x.semanticToken = - this.declarations[0] - .semanticToken - ?.toNewRange(x.range)); - } - - private assignDiagnostics() { - if (this.diagnostics.length === 0) { - return; - } - const els = this.declarations.concat(this.references); - els.forEach((x) => x.addDiagnostics(this.diagnostics)); - } - - private validateMethodSignatures() { - // TODO: implement. - } -} - -class Scope2 { - context: SyntaxElement; - parent?: Scope2; - nameRefs: Map = new Map(); - - constructor(ctx: SyntaxElement); - constructor(ctx: SyntaxElement, pnt: Scope2); - constructor(ctx: SyntaxElement, pnt?: Scope2) { - this.context = ctx; - this.parent = pnt; - } - - addRef(ctx: IdentifiableSyntaxElement) { - const nameLink = this.getName(ctx.identifier.text); - // if dec - - // if not dec - } - - getName(identifier: string): NameLink { - if (!this.nameRefs.has(identifier)) { - this.nameRefs.set(identifier, new NameLink()); - } - return this.nameRefs.get(identifier)!; - } -} \ No newline at end of file diff --git a/server/src/parser/document/scope.jsonc b/server/src/parser/document/scope.jsonc deleted file mode 100644 index 4ea4217..0000000 --- a/server/src/parser/document/scope.jsonc +++ /dev/null @@ -1,51 +0,0 @@ -{ - // Represents a project - "markdoc": { - "global": {}, - "LexerMarkdown": { - "global": { - "mTokens": "VariableDeclarationsElement", - "Tokenize": "SubDeclarationElement", - "TokenFactory": "FunctionDeclarationElement", - "Tokens": "PropertyDeclarationElement" - }, - "Tokens": { - "get": {}, - "set": { // no need to distinguish let here. - "var": "VariableDeclarationsElement" - } - } - } - }, - // Similar to a project, available for anything. - // This probably needs to legit be a json resource. - "excelVbaObjectModel": { - "Range": { - "functions": { - "SpecialCells": { - "doc": "Returns a **Range** object that represents all the cells that match the specified type and value.", - "args": { - "Type": "XlCellType", - "[Value]": "Variant" - }, - "returns": "Excel.Range" - } - } - } - }, - "vbaObjectModel": { - "Collection": { - "subs": { - "Add": { - "doc": "Adds a member to a **Collection** object.", - "args": { - "Item": "Variant", - "[Key]": "String", - "[Before]": "Long", - "[After]": "Long" - } - } - } - } - } -} \ No newline at end of file diff --git a/server/src/parser/document/scope.ts b/server/src/parser/document/scope.ts deleted file mode 100644 index 432adbf..0000000 --- a/server/src/parser/document/scope.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { BaseDeclaration } from '../elements/memory/base'; - - -export class ScopeManager { - language = ""; - - - /** - * Initialises the VBA scopes at the language and application level. - * @returns A scope with language and application initialised. - */ - initialiseScope(): Scope { - return new VbaScope(); - } -} - - -/** - * A scope represents a region of code that has a scope. - * This can be a module or a method. - */ -class Scope { - namespace: string; - parent: Scope | undefined; - - constructor(namespace: string, parent: Scope | undefined) { - this.namespace = namespace; - this.parent = parent; - } -} - -class VbaScope extends Scope { - globals: Map = new Map(); - - constructor() { - super("vba", undefined); - } -} - -export class ScopeTable { - // Items listed by their fully qualified name. - definitions: Map = new Map(); - - findDefinition(identifier:string, context: string) - : ScopeTableItem | undefined { - - // Check fqns - if(this.definitions.has(identifier)) { - return this.definitions.get(identifier)[0]; - } - - // Check the project scope. - let result = this.findDef(identifier, context); - if(result) { return result; } - - // Check the application scope. - result = this.findDef(identifier, 'Application'); - if(result) { return result; } - - // Check the language scope. - result = this.findDef(identifier, 'Vba'); - if(result) { return result; } - } - - private findDef(identifier:string, context: string) - : ScopeTableItem | undefined { - const names = context.split('.'); - while(names) { - const checkDef = `${names.join('.')}.${identifier}`; - if(this.definitions.has(checkDef)) { - return this.definitions.get(checkDef)[0]; - } - names.pop(); - } - } -} - - -export interface ScopeTableItem { - //namespace: string - docstring: string - reference: ScopeItem - returnsAs: any -} - -interface ScopeItem { - args: string[] - kwargs: Map -} - -/** - * - * global - * module - * method - * language - * application - */ - - - /** - * When passed an identifier, need to find it. - * There are a number of top level namespaces and - * each may be the object itself. - * - myProject.myModule.mySub.foo - myProject.myModule.foo - myProject.foo - myProject.foo - Application.foo - Vba.foo - - Could be passed any part of the chain. - e.g., myModule.a - - Therefore the resolver needs to first understand the context - of the highest level calling object. - - It also needs to understand the context from where it's being called. - e.g., finding `foo` from myFunc scope, the resolver must recursively - walk up the scope tree to find a definition for foo: - - myProject.myModule.myFunc - - myProject.myModule - - myProject.otherModules (public definitions only) - - myProject - - app - - lang - */ \ No newline at end of file diff --git a/server/src/parser/document/symbols.ts b/server/src/parser/document/symbols.ts deleted file mode 100644 index 653d1c5..0000000 --- a/server/src/parser/document/symbols.ts +++ /dev/null @@ -1,22 +0,0 @@ - -/** - * A data class representing a method, function, or variable. - */ -class VbaSymbol { - scope: string; - name: string; - - constructor(scope: string, name: string, ) { - this.scope = scope; - this.name = name; - } -} - -class VbaReference extends VbaSymbol { - declaration: VbaSymbol; - - constructor(scope: string, name: string, declaration: VbaSymbol) { - super(scope, name); - this.declaration = declaration; - } -} \ No newline at end of file diff --git a/server/src/parser/elements/base.ts b/server/src/parser/elements/base.ts deleted file mode 100644 index 9618d54..0000000 --- a/server/src/parser/elements/base.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { ParserRuleContext } from 'antlr4ts'; -import { Diagnostic, FoldingRange, Hover, Location, MarkupContent, MarkupKind, Range, SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { AmbiguousIdentifierContext, LiteralContext } from '../../antlr/out/vbaParser'; -import { SemanticToken } from '../../capabilities/vbaSemanticTokens'; -import { getCtxRange } from '../../utils/helpers'; - - -export interface SyntaxElement { - uri: string; - text: string; - range: Range; - identifier?: IdentifierElement; - context: ParserRuleContext; - symbolKind: SymbolKind; - semanticToken?: SemanticToken; - diagnostics: Diagnostic[]; - name: string; - - readonly namespace: string; - - parent?: SyntaxElement; - children: SyntaxElement[]; - - getAncestorCount(): number; - hover(): Hover | undefined; - isChildOf(element: SyntaxElement): boolean; - symbolInformation(uri: string): SymbolInformation | undefined; - foldingRange(): FoldingRange | undefined; - location(): Location; - addDiagnostics(diagnostics: Diagnostic[]): void; -} - -export interface Identifiable { - ambiguousIdentifier(): AmbiguousIdentifierContext; - ambiguousIdentifier(i: number): AmbiguousIdentifierContext; -} - -export abstract class BaseElement implements SyntaxElement { - abstract name: string; - uri: string; - text: string; - range: Range; - identifier?: IdentifierElement; - context: ParserRuleContext; - symbolKind: SymbolKind; - semanticToken?: SemanticToken; - diagnostics: Diagnostic[] = []; - - parent?: SyntaxElement; - - children: SyntaxElement[] = []; - private _countAncestors = 0; - - protected _hoverContent: MarkupContent | undefined; - - get namespace(): string { - return `${this.parent?.namespace}.${this.identifier?.text}`; - } - - get hoverContent(): MarkupContent { - if(this._hoverContent === undefined) { - this.hoverContent = { - kind: MarkupKind.PlainText, - value: '' - }; - } - return this._hoverContent!; - } - - set hoverContent(value: MarkupContent) { - this._hoverContent = value; - } - - constructor(ctx: ParserRuleContext, doc: TextDocument) { - this.uri = doc.uri; - this.context = ctx; - this.range = getCtxRange(ctx, doc); - this.text = ctx.text; - this.setIdentifierFromDoc(doc); - this.symbolKind = SymbolKind.Null; - } - - hover = () => this.parent?.hover(); - - location = (): Location => Location.create(this.uri, this.range); - - isChildOf(element: SyntaxElement): boolean { - const tr = this.range; - const pr = element.range; - - const psl = pr.start.line; - const psc = pr.start.character; - const tsl = tr.start.line; - const tsc = tr.start.character; - - const pel = pr.end.line; - const pec = pr.end.character; - const tel = tr.end.line; - const tec = tr.end.character; - - const prStartEarlier = (psl < tsl) || (psl === tsl && psc <= tsc); - const prEndsAfter = (pel > tel) || (pel === tel && pec >= tec); - - return prStartEarlier && prEndsAfter; - } - - symbolInformation = (uri: string): SymbolInformation | undefined => - SymbolInformation.create( - this.identifier!.text, - this.symbolKind, - this.range, - uri, - this.parent?.identifier?.text ?? ''); - - foldingRange = (): FoldingRange | undefined => - FoldingRange.create( - this.range.start.line, - this.range.end.line, - this.range.start.character, - this.range.end.character - ); - - addDiagnostics(diagnostics: Diagnostic[]) { - this.diagnostics = this.diagnostics.concat(diagnostics); - } - - getAncestorCount(n = 0): number { - if (this._countAncestors === 0) { - const pnt = this.getParent(); - if (pnt) { - this._countAncestors = pnt.getAncestorCount(n + 1); - return this._countAncestors; - } - } - return this._countAncestors + n; - } - - toString = () => `${"-".repeat(this.getAncestorCount())} ${this.constructor.name}: ${this.context.text}`; - - protected setIdentifierFromDoc(doc: TextDocument): void { - if (this.isIdentifiable(this.context)) { - const identCtx = this.context.ambiguousIdentifier(0); - if (identCtx) { - this.identifier = new IdentifierElement(identCtx, doc); - } - } - } - - protected isIdentifiable = (o: any): o is Identifiable => - 'ambiguousIdentifier' in o; - - private getParent(): BaseElement | undefined { - if (this.parent) { - if (this.parent instanceof BaseElement) { - return this.parent; - } - } - } -} - -export class IdentifierElement extends BaseElement { - name = "IdentifierElement"; - constructor(ctx: AmbiguousIdentifierContext | LiteralContext, doc: TextDocument, name?: string | undefined) { - super(ctx, doc); - if(name) this.name = name; - } - - createSemanticToken(tokType: SemanticTokenTypes, tokMods?: SemanticTokenModifiers[]) { - if (!(this.context instanceof AmbiguousIdentifierContext) && !(this.context instanceof LiteralContext)) - return; - - this.semanticToken = new SemanticToken( - this.range.start.line, - this.range.start.character, - this.text.length, - tokType, - tokMods ?? [] - ); - - - } -} - -export class FoldableElement extends BaseElement { - name = "FoldableElement"; -} - -export class UnknownElement extends BaseElement { - name = "UnknownElement"; -} diff --git a/server/src/parser/elements/memory/base.ts b/server/src/parser/elements/memory/base.ts deleted file mode 100644 index 920551e..0000000 --- a/server/src/parser/elements/memory/base.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver'; -import { BaseElement } from '../base'; -import { SemanticToken } from '../../../capabilities/vbaSemanticTokens'; -import { VariableDeclarationElement } from './variables'; -import { ArgListContext, ConstStmtContext, VariableStmtContext } from '../../../antlr/out/vbaParser'; -import { TextDocument } from 'vscode-languageserver-textdocument'; - -export enum Visibility { - "Private", - "Friend", - "Public", - "Global" -} - -export enum BaseTypes { - 'string', - 'oct', - 'hex', - 'short', - 'double', - 'integer', - 'date', - 'boolean', - 'variant' -} - -export interface IDeclaration { - asType(): any; -} - -export interface IReference { - declaration(): IDeclaration -} - -export abstract class BaseDeclarations extends BaseElement { - variableList: VariableDeclarationElement[] = []; - - constructor(ctx: VariableStmtContext | ConstStmtContext | ArgListContext, doc: TextDocument) { - super(ctx, doc); - } - - /** - * Creates a semantic token for this declaration. - * @param tokenModifiers the token modifiers as a list. - */ - protected createSemanticToken(tokenModifiers: SemanticTokenModifiers[]) { - const name = this.identifier!; - this.semanticToken = new SemanticToken( - name.range.start.line, - name.range.start.character, - name.text.length, - SemanticTokenTypes.variable, - tokenModifiers - ); - } -} - -// Need to figure out how to handle call chains like MyClass.MyProp.Value -export abstract class BaseDeclaration extends BaseElement { - semanticToken?: SemanticToken | undefined; - visibility = Visibility.Private; - isArray = false; - hasScope = false; - references: BaseReference[] = []; - - get isPublic(): boolean { - return [Visibility.Global, Visibility.Public].includes(this.visibility); - } - - get identifierText(): string { - return this.identifier?.context.text ?? "Undefined"; - } -} - -export abstract class BaseReference extends BaseElement { - declarationElement: BaseDeclaration | undefined; - - get identifierText(): string { - return this.identifier?.context.text ?? "Undefined"; - } -} \ No newline at end of file diff --git a/server/src/parser/elements/memory/constants.ts b/server/src/parser/elements/memory/constants.ts deleted file mode 100644 index 17d24a9..0000000 --- a/server/src/parser/elements/memory/constants.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { ConstStmtContext } from '../../../antlr/out/vbaParser'; -import { BaseDeclarations } from './base'; -import { SemanticTokenModifiers } from 'vscode-languageserver'; - - - -export class ConstDeclarationElement extends BaseDeclarations { - name = "ConstDeclarationElement"; - constructor(ctx: ConstStmtContext, doc: TextDocument) { - super(ctx, doc); - - const modifiers = this.getSemanticTokenModifiers(); - this.createSemanticToken(modifiers); - } - - private getSemanticTokenModifiers = (): SemanticTokenModifiers[] => - [SemanticTokenModifiers.declaration, SemanticTokenModifiers.readonly]; -} \ No newline at end of file diff --git a/server/src/parser/elements/memory/events.ts b/server/src/parser/elements/memory/events.ts deleted file mode 100644 index 9279316..0000000 --- a/server/src/parser/elements/memory/events.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { BaseDeclaration, Visibility } from './base'; - - - -export class EventDeclarationElement extends BaseDeclaration { - name = "EventDeclarationElement"; - visibility = Visibility.Public; - // Public Event OnNewLine(pos As Long) -} - -export class EventMethodDeclarationElement extends BaseDeclaration { - name = "EventMethodDeclarationElement"; - visibility = Visibility.Public; - // Acts as both a reference and a declaration. - // Should reference the WithEvents variable. - - // Private WithEvents myRaiser As EventRaiser - // Private Sub myRaiser_OnNewLine(pos As Long) -} \ No newline at end of file diff --git a/server/src/parser/elements/memory/functions.ts b/server/src/parser/elements/memory/functions.ts deleted file mode 100644 index 80e0b38..0000000 --- a/server/src/parser/elements/memory/functions.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { ArgListContext } from '../../../antlr/out/vbaParser'; -import { BaseDeclarations, BaseDeclaration, Visibility } from './base'; - - - -export class FunctionDeclarationElement extends BaseDeclaration { - name = "FunctionDeclarationElement"; - visibility = Visibility.Public; -} - -export class ArgDeclarationsElement extends BaseDeclarations { - name = "ArgDeclarationsElement"; - - constructor(ctx: ArgListContext, doc: TextDocument) { - super(ctx, doc); - } -} \ No newline at end of file diff --git a/server/src/parser/elements/memory/references.ts b/server/src/parser/elements/memory/references.ts deleted file mode 100644 index 688c6e2..0000000 --- a/server/src/parser/elements/memory/references.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BaseDeclaration, BaseReference } from './base'; - - -export class MethodCallReferenceElement extends BaseReference { - name = "MethodCallReferenceElement"; -} - - -export class VariableReferenceElement extends BaseReference { - name = "VariableReferenceElement"; -} - - -export class LiteralReferenceElement extends BaseReference { - name = "LiteralReferenceElement"; -} - -class LiteralDeclarationMockElement extends BaseDeclaration { - name = "LiteralDeclarationMockElement"; -} \ No newline at end of file diff --git a/server/src/parser/elements/memory/types.ts b/server/src/parser/elements/memory/types.ts deleted file mode 100644 index b13978f..0000000 --- a/server/src/parser/elements/memory/types.ts +++ /dev/null @@ -1,23 +0,0 @@ - - -export class Primative { - -} - -export class Struct { - name: string; - type: Struct | Primative; - - constructor(name: string, type: Struct | Primative) { - this.name = name; - this.type = type; - } -} - -export class TypeRegister { - registeredTypes: Map; - - constructor() { - this.registeredTypes = new Map(); - } -} \ No newline at end of file diff --git a/server/src/parser/elements/memory/variables.ts b/server/src/parser/elements/memory/variables.ts deleted file mode 100644 index 7165772..0000000 --- a/server/src/parser/elements/memory/variables.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { VariableStmtContext } from '../../../antlr/out/vbaParser'; -import { BaseDeclaration, BaseDeclarations } from './base'; -import { SemanticTokenModifiers } from 'vscode-languageserver'; - - -export class VariableDeclarationsElement extends BaseDeclarations { - name = "VariableDeclarationsElement"; - - constructor(ctx: VariableStmtContext, doc: TextDocument) { - super(ctx, doc); - - const modifiers = this.getSemanticTokenModifiers(!!(ctx.STATIC())); - this.createSemanticToken(modifiers); - } - - private getSemanticTokenModifiers(isStatic: boolean): SemanticTokenModifiers[] { - const result: SemanticTokenModifiers[] = [SemanticTokenModifiers.declaration]; - if(isStatic) { result.push(SemanticTokenModifiers.static); } - return result; - } -} - -export class VariableDeclarationElement extends BaseDeclaration { - name = "VariableDeclarationElement"; - constructor(ctx: VariableStmtContext, doc: TextDocument) { - super(ctx, doc); - } - - -} \ No newline at end of file diff --git a/server/src/parser/elements/method.ts b/server/src/parser/elements/method.ts deleted file mode 100644 index e102303..0000000 --- a/server/src/parser/elements/method.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { TypeContext } from './vbLang'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { BaseElement, IdentifierElement } from './base'; -import { Hover, MarkupContent, MarkupKind, SymbolKind } from 'vscode-languageserver'; -import { AsTypeClauseContext, DocstringStmtContext, MethodSignatureStmtContext, MethodStmtContext, PropertySignatureStmtContext, PropertyStmtContext } from '../../antlr/out/vbaParser'; - - -export class MethodElement extends BaseElement { - readonly name = "MethodElement"; - returnType: TypeContext | undefined; - hasPrivateModifier = false; - - public get signature(): MethodSignatureStmtContext | PropertySignatureStmtContext | undefined { - if (this.context instanceof MethodStmtContext) - return this.context.methodSignatureStmt(); - if (this.context instanceof PropertyStmtContext) { return this.context.propertySignatureStmt(); } - } - - constructor(ctx: MethodStmtContext | PropertyStmtContext, doc: TextDocument) { - super(ctx, doc); - this.returnType = this.getReturnType(doc); - if (ctx instanceof MethodStmtContext) { - this.setUpMethod(ctx); - } else { - this.setUpProperty(ctx); - } - } - - private setUpMethod(ctx: MethodStmtContext) { - this.hasPrivateModifier = !!(ctx.methodSignatureStmt().visibility()?.PRIVATE); - this.symbolKind = ctx.methodSignatureStmt().FUNCTION() ? - SymbolKind.Function : SymbolKind.Method; - } - - private setUpProperty(ctx: PropertyStmtContext) { - this.hasPrivateModifier = !!(ctx.propertySignatureStmt().visibility()?.PRIVATE); - this.symbolKind = SymbolKind.Property; - } - - private getReturnType(doc: TextDocument): TypeContext | undefined { - const asTypeCtx = this.getTypeContext(); - - if (asTypeCtx) { - const t = asTypeCtx.type_(); - const typeCtx = t.baseType() ?? t.complexType(); - if (typeCtx) { - return new TypeContext(typeCtx, doc); - } - } - } - - private getTypeContext(): AsTypeClauseContext | undefined { - return this.getSignatureContext().asTypeClause(); - } - - protected setIdentifierFromDoc(doc: TextDocument): void { - if (this.isIdentifiable(this.getSignatureContext())) { - const identCtx = this.getSignatureContext().ambiguousIdentifier(); - if (identCtx) { - this.identifier = new IdentifierElement(identCtx, doc); - } - } - } - - private getSignatureContext(): MethodSignatureStmtContext | PropertySignatureStmtContext { - if (this.context instanceof MethodStmtContext) { - return this.context.methodSignatureStmt(); - } - if (this.context instanceof PropertyStmtContext) { - return this.context.propertySignatureStmt(); - } - throw new Error("Method has no signature."); - } - - hover = (): Hover => { - return { - contents: this.getHoverText(), - range: this.range - }; - }; - - getHoverText(): MarkupContent { - const lineBreak = '---\n'; - const signature = this.signature; - const docs = this.getDocstringContent(); - - const result = [ - this.parent?.namespace ?? this.namespace, - `\`\`\`vba\n${signature?.text}\n\`\`\`\n`, - lineBreak, - docs - ]; - - return { - kind: MarkupKind.Markdown, - value: result.join('\n') - }; - } - - private getDocstringContent(): string | undefined { - const docstring = this.children.find(x => x.name == "DocstringElement"); - if (docstring instanceof DocstringElement) - return docstring.toMarkupContentValue(); - } - - -} - -export class DocstringElement extends BaseElement { - readonly name = "DocstringElement"; - constructor(ctx: DocstringStmtContext, doc: TextDocument) { - super(ctx, doc); - } - - toMarkupContentValue(): string { - let lines = this.text.split('\n'); - lines = lines.map(str => str.replace(/^[']\s{0,3}/, '')); - lines = lines.map(str => str.replace(/\s/, ' ')); - return lines.join(' \n'); - } -} diff --git a/server/src/parser/elements/module.ts b/server/src/parser/elements/module.ts deleted file mode 100644 index 6674389..0000000 --- a/server/src/parser/elements/module.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { BaseElement, IdentifierElement } from './base'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { AttributeStmtContext, ModuleContext } from '../../antlr/out/vbaParser'; -import { SymbolKind } from 'vscode-languageserver'; -import { stripQuotes } from '../../utils/helpers'; - -export class ModuleElement extends BaseElement { - readonly name = "ModuleElement"; - - /** - * Override the base namespace function so that it's not recurring. - * Removes the quote marks from the identifier. - */ - get namespace() { - return this.identifier?.name.replace(/^"(.*)"$/, '$1') ?? this.name; - } - - constructor(ctx: ModuleContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.Module; - } - - protected setIdentifierFromDoc(doc: TextDocument): void { - const ctx = this.context as ModuleContext; - const attrs = ctx.moduleAttributes(); - const vbName = attrs?.attributeStmt().find( - x => x.implicitCallStmt_InStmt().text == "VB_Name") - ?.implicitCallStmt_InStmt().iCS_S_VariableOrProcedureCall(); - - if (this.isIdentifiable(vbName)) { - const identCtx = vbName.ambiguousIdentifier(); - if (identCtx) { - this.identifier = new IdentifierElement(identCtx, doc); - } - } - } -} - -export class ModuleAttribute { - identifier?: IdentifierElement; - literal?: IdentifierElement; - - key = (): string => this.identifier?.text ?? 'Undefined'; - value = (): string => (this.literal) ? stripQuotes(this.literal.text) : 'Undefined'; - - constructor(ctx: AttributeStmtContext, doc: TextDocument) { - const idCtx = ctx - .implicitCallStmt_InStmt() - ?.iCS_S_VariableOrProcedureCall() - ?.ambiguousIdentifier(); - - if (idCtx) { - this.identifier = new IdentifierElement(idCtx, doc); - const pntCtx = idCtx.parent?.parent?.parent; - if(pntCtx instanceof AttributeStmtContext) { - this.literal = new IdentifierElement(idCtx, doc, pntCtx.literal(0)?.STRINGLITERAL()?.text); - } - } - } -} \ No newline at end of file diff --git a/server/src/parser/elements/variable.ts b/server/src/parser/elements/variable.ts deleted file mode 100644 index 9cbc890..0000000 --- a/server/src/parser/elements/variable.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { SemanticToken } from '../../capabilities/vbaSemanticTokens'; -import { vbaTypeToSymbolConverter } from '../../utils/converters'; -import { BaseElement, IdentifierElement, SyntaxElement } from './base'; -import { ImplicitCallElement, TypeContext } from './vbLang'; -import { Hover, SemanticTokenModifiers, SemanticTokenTypes, SymbolKind } from 'vscode-languageserver'; - -import { ConstStmtContext, ConstSubStmtContext, ImplicitCallStmt_InBlockContext, ImplicitCallStmt_InStmtContext, LetStmtContext, LiteralContext, SetStmtContext, ValueStmtContext, VariableStmtContext, VariableSubStmtContext, VsLiteralContext } from '../../antlr/out/vbaParser'; -import { MethodElement } from './method'; - -interface IsVarOrCall { - setDeclaredType(e: MethodElement): void; -} - -export enum Type { - 'string' = 0, - 'oct', - 'hex', - 'short', - 'double', - 'integer', - 'date', - 'boolean', - 'object', - 'variant' -} - -/** - * Represents a variable declaration either explicit or via a method signature. - */ -export class VariableStatementElement extends BaseElement implements IsVarOrCall { - readonly name = "VariableStatementElement"; - variableList: VariableDeclarationElement[] = []; - hasPrivateModifier = false; - foldingRange = () => undefined; - declaration: MethodElement | undefined; - - constructor(ctx: VariableStmtContext | ConstStmtContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.Variable; - // TODO: No private modifier is, by default, private. - // MS recommends explicitly using Public or Private rather than Dim at the module level. - // Similar, Global means Public and common convention is not to use Global. - this.hasPrivateModifier = !!(ctx.visibility()?.PRIVATE()); - this.resolveDeclarations(ctx, doc); - } - - hover = (): Hover => this.declaration?.hover() ?? - ({ - contents: this.hoverContent, - range: this.range - }); - - getHoverText(): string { - return "Variant"; - } - - setDeclaredType(e: MethodElement) { - this.declaration = e; - } - - private resolveDeclarations(ctx: VariableStmtContext | ConstStmtContext, doc: TextDocument) { - if (ctx instanceof VariableStmtContext) { - this.resolveVarDeclarations(ctx, doc); - } else { - this.resolveConstDeclarations(ctx, doc); - } - } - - private resolveVarDeclarations(ctx: VariableStmtContext, doc: TextDocument) { - const tokenMods = this.getSemanticTokenModifiers(ctx); - ctx.variableListStmt().variableSubStmt().forEach((x) => - this.variableList.push(new VariableDeclarationElement(x, doc, tokenMods, this.hasPrivateModifier))); - } - - private resolveConstDeclarations(ctx: ConstStmtContext, doc: TextDocument) { - const tokenMods = this.getSemanticTokenModifiers(ctx); - ctx.constSubStmt().forEach((x) => this.variableList.push( - new VariableDeclarationElement(x, doc, tokenMods, this.hasPrivateModifier))); - } - - private getSemanticTokenModifiers(ctx: VariableStmtContext | ConstStmtContext): SemanticTokenModifiers[] { - const result: SemanticTokenModifiers[] = [SemanticTokenModifiers.declaration]; - if (ctx instanceof VariableStmtContext && ctx.STATIC()) - result.push(SemanticTokenModifiers.static); - if (ctx instanceof ConstStmtContext) - result.push(SemanticTokenModifiers.readonly); - return result; - } -} - -export class VariableDeclarationElement extends BaseElement { - readonly name = "VariableDeclarationElement"; - asType: TypeContext | undefined; - hasPrivateModifier = false; - - constructor(ctx: VariableSubStmtContext | ConstSubStmtContext, doc: TextDocument, tokenModifiers: SemanticTokenModifiers[], hasPrivateModifier: boolean) { - super(ctx, doc); - this.hasPrivateModifier = hasPrivateModifier; - this.asType = TypeContext.create(ctx, doc); - this.symbolKind = vbaTypeToSymbolConverter(this.asType?.text ?? 'variant'); - this.createSemanticToken(tokenModifiers); - } - - private createSemanticToken(tokenModifiers: SemanticTokenModifiers[]) { - const name = this.identifier!; - this.semanticToken = new SemanticToken( - name.range.start.line, - name.range.start.character, - name.text.length, - SemanticTokenTypes.variable, - tokenModifiers - ); - } -} - -export class VariableAssignElement extends BaseElement { - readonly name = "VariableAssignElement"; - - private _leftImplicitCall: ImplicitCallElement | undefined; - private _rightImplicitCall: ImplicitCallElement | undefined; - - get leftImplicitCall() { - return this._leftImplicitCall!; - } - get rightImplicitCall() { - return this._rightImplicitCall!; - } - - constructor(ctx: LetStmtContext | SetStmtContext, doc: TextDocument) { - super(ctx, doc); - const callStmtContext = ctx.implicitCallStmt_InStmt(); - const varOrProc = callStmtContext.iCS_S_VariableOrProcedureCall(); - if (varOrProc) { - this.identifier = new IdentifierElement(varOrProc.ambiguousIdentifier(), doc); - return; - } - } - symbolInformation = (_: string) => undefined; - - /** - * Adds an implicit call to the left or right side of the assignment. - * @param implicitCall the implicit call to add. - */ - addImplicitCall(implicitCall: ImplicitCallElement) { - if (this._leftImplicitCall) { - this._rightImplicitCall = implicitCall; - this.validateTypes(); - } - else { - this._leftImplicitCall = implicitCall; - } - } - - private validateTypes() { - return false; - } -} - -/** - * Represents a literal, variable, or call that can be assigned to a variable. - */ -export class AssignmentElement extends BaseElement { - readonly name = "AssignmentElement"; - variableType: Type; - - constructor(ctx: ValueStmtContext | ImplicitCallStmt_InStmtContext, doc: TextDocument) { - super(ctx, doc); - this.variableType = this.getType(ctx); - } - - private getType(ctx: ValueStmtContext | ImplicitCallStmt_InStmtContext): Type { - if(ctx instanceof VsLiteralContext) { - const litCtx = ctx.literal(); - - if(litCtx.STRINGLITERAL()) { - return Type.string; - } - - if(litCtx.INTEGERLITERAL()) { - return Type.integer; - } - - if(litCtx.DOUBLELITERAL()) { - return Type.double; - } - - if(litCtx.TRUE() || litCtx.FALSE()) { - return Type.boolean; - } - - if(litCtx.DATELITERAL()) { - return Type.date; - } - - if(litCtx.SHORTLITERAL()) { - return Type.short; - } - - if(litCtx.NOTHING() || litCtx.NULL_()) { - return Type.variant; - } - - - if(litCtx.HEXLITERAL()) { - return Type.hex; - } - - if(litCtx.OCTLITERAL()) { - return Type.oct; - } - - throw new Error("Unknown literal type."); - - } - else { - - return Type.variant; - } - } - - private getLiteralType(ctx: LiteralContext): Type { - - return Type.variant; - } - - -} \ No newline at end of file diff --git a/server/src/parser/elements/vbLang.ts b/server/src/parser/elements/vbLang.ts deleted file mode 100644 index 8cb76c2..0000000 --- a/server/src/parser/elements/vbLang.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { BaseElement } from './base'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { BaseTypeContext, ComplexTypeContext, ConstSubStmtContext, VariableSubStmtContext } from '../../antlr/out/vbaParser'; - -export class TypeContext extends BaseElement { - readonly name = "TypeContext"; - constructor(ctx: BaseTypeContext | ComplexTypeContext, doc: TextDocument) { - super(ctx, doc); - } - - static create(ctx: VariableSubStmtContext | ConstSubStmtContext, doc: TextDocument): TypeContext | undefined { - const xCtx = ctx.asTypeClause()?.type_(); - const tCtx = xCtx?.baseType() ?? xCtx?.complexType(); - if (tCtx) { return new TypeContext(tCtx, doc); } - } -} - - -import { SemanticTokenTypes, SymbolKind } from 'vscode-languageserver'; -import { EnumerationStmtContext, EnumerationStmt_ConstantContext } from '../../antlr/out/vbaParser'; - -export class EnumElement extends BaseElement { - readonly name = "EnumElement"; - constructor(ctx: EnumerationStmtContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.Enum; - if (this.identifier) - this.identifier.createSemanticToken(SemanticTokenTypes.enum); - } -} - -export class EnumConstantElement extends BaseElement { - readonly name = "EnumConstant"; - foldingRange = () => undefined; - symbolInformation = (_: string) => undefined; - constructor(ctx: EnumerationStmt_ConstantContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.EnumMember; - if (this.identifier) { - this.identifier.createSemanticToken( - SemanticTokenTypes.enumMember); - } - } -} - - -import { LiteralContext } from '../../antlr/out/vbaParser'; - -export class LiteralElement extends BaseElement { - name = "LiteralElement"; - private _valueString: string; - - constructor(ctx: LiteralContext, doc: TextDocument, name?: string) { - super(ctx, doc); - this._valueString = ctx.STRINGLITERAL.toString(); - if(name) this.name = name; - } - - get valueString() { - return this._valueString; - } -} - - -import { ImplicitCallStmt_InBlockContext, ImplicitCallStmt_InStmtContext } from '../../antlr/out/vbaParser'; - -/** -* An implicit call can refer to a member call -* with a variable or procedure, e.g., -* Module1.Greeting, -* dict.Item(itemKey). -*/ -export class ImplicitCallElement extends BaseElement { - readonly name = "ImplicitCallElement"; - constructor(ctx: ImplicitCallStmt_InBlockContext | ImplicitCallStmt_InStmtContext, doc: TextDocument) { - super(ctx, doc); - - } -} \ No newline at end of file diff --git a/server/src/parser/vbaSyntaxParser.ts b/server/src/parser/vbaSyntaxParser.ts deleted file mode 100644 index 488ffa8..0000000 --- a/server/src/parser/vbaSyntaxParser.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { ANTLRInputStream, CommonTokenStream, ConsoleErrorListener, ParserRuleContext, RecognitionException, Recognizer } from 'antlr4ts'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { vbaListener } from '../antlr/out/vbaListener'; -import { AttributeStmtContext, ConstStmtContext, DocstringStmtContext, EnumerationStmtContext, EnumerationStmt_ConstantContext, FoldingBlockStmtContext, MethodStmtContext, IfThenElseStmtContext, ImplicitCallStmt_InBlockContext, ImplicitCallStmt_InStmtContext, LetStmtContext, ModuleContext, ModuleHeaderContext, SetStmtContext, StartRuleContext, UnknownLineContext, VariableStmtContext, vbaParser, PropertyStmtContext, ICS_S_VariableOrProcedureCallContext, ICS_S_MembersCallContext, AmbiguousIdentifierContext, ModuleConfigContext, ArgListContext } from '../antlr/out/vbaParser'; -import { vbaLexer as VbaLexer } from '../antlr/out/vbaLexer'; -import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker'; -import { ErrorNode } from 'antlr4ts/tree/ErrorNode'; -import { DocstringElement, MethodElement } from './elements/method'; -import { SymbolKind } from 'vscode-languageserver'; -import { ModuleAttribute, ModuleElement } from './elements/module'; -import { FoldableElement, SyntaxElement } from './elements/base'; -import { VariableAssignElement, VariableDeclarationElement, VariableStatementElement } from './elements/variable'; -import { EnumConstantElement, EnumElement } from './elements/vbLang'; - - -export interface ResultsContainer { - module?: ModuleElement; - addModule(element: ModuleElement): void; - addElement(element: SyntaxElement): ResultsContainer; - addScopeReferences(emt: VariableAssignElement): void; - addScopeDeclaration(emt: MethodElement | VariableDeclarationElement): void; - addFoldingRange(emt: FoldableElement): void; -} - -// TODO: Break down into smaller blocks of work. -// The parser is attempting to parse all of VBA. It would be more efficient to parse each function separately. -// A pre-parser should detect module level elements, and if anything has changed, the element should be re-parsed -// using the lower parser, e.g., funcitons / subs / props / decs, etc. The part of the document that has changed -// should also force a refresh to that specific element. -export class SyntaxParser { - parse(doc: TextDocument, resultsContainer: ResultsContainer) { - const listener = new VbaTreeWalkListener(doc, resultsContainer); - const parser = this.createParser(doc); - - ParseTreeWalker.DEFAULT.walk( - listener, - parser.startRule() - ); - } - - private createParser(doc: TextDocument): vbaParser { - const lexer = new VbaLexer(new ANTLRInputStream(doc.getText())); - const parser = new vbaParser(new CommonTokenStream(lexer)); - - parser.removeErrorListeners(); - parser.addErrorListener(new VbaErrorListener()); - return parser; - } -} - -class VbaTreeWalkListener implements vbaListener { - doc: TextDocument; - resultsContainer: ResultsContainer; - - // State flags - inModuleConfig = false; - inAttributeStatement = false; - inDeclarationStatement = false; - - constructor(doc: TextDocument, resultsContainer: ResultsContainer) { - this.doc = doc; - this.resultsContainer = resultsContainer; - } - - enterFoldingBlockStmt = (ctx: FoldingBlockStmtContext) => - this.registerFoldingRange(ctx); - - enterIfThenElseStmt?: ((ctx: IfThenElseStmtContext) => void) | undefined; - - visitErrorNode(node: ErrorNode) { - console.log(node.payload); - } - - enterUnknownLine = (ctx: UnknownLineContext) => { - console.log(ctx.text); - }; - - enterModule = (ctx: ModuleContext) => - this.resultsContainer.addModule( - new ModuleElement(ctx, this.doc)); - - // Only classes have a header. TODO: Test this for forms. - enterModuleHeader = (_: ModuleHeaderContext) => - this.resultsContainer.module!.symbolKind = SymbolKind.Class; - - enterAttributeStmt = (ctx: AttributeStmtContext) => { - this.inAttributeStatement = true; - const attr = new ModuleAttribute(ctx, this.doc); - // this.resultsContainer.setModuleAttribute(attr); - if (attr.identifier?.text === 'VB_Name') { - const module = this.resultsContainer.module; - if (module) { - module.identifier = attr.literal; - } - } - }; - - exitAttributeStmt = (_: AttributeStmtContext) => this.inAttributeStatement = false; - - enterModuleConfig = (_: ModuleConfigContext) => this.inModuleConfig = true; - exitModuleConfig = (_: ModuleConfigContext) => this.inModuleConfig = false; - - enterMethodStmt = (ctx: MethodStmtContext) => { - const e = new MethodElement(ctx, this.doc); - this.registerFoldingRange(ctx); - this.resultsContainer - .addElement(e) - .addScopeDeclaration(e); - }; - - enterPropertyStmt = (ctx: PropertyStmtContext) => { - const e = new MethodElement(ctx, this.doc); - this.registerFoldingRange(ctx); - this.resultsContainer - .addElement(e) - .addScopeDeclaration(e); - }; - - enterDocstringStmt = (ctx: DocstringStmtContext) => - this.resultsContainer.addElement( - new DocstringElement(ctx, this.doc) - ); - - enterEnumerationStmt = (ctx: EnumerationStmtContext) => { - this.resultsContainer.addElement( - new EnumElement(ctx, this.doc)); - this.registerFoldingRange(ctx); - }; - - enterEnumerationStmt_Constant = (ctx: EnumerationStmt_ConstantContext) => - this.resultsContainer.addElement( - new EnumConstantElement(ctx, this.doc)); - - enterVariableStmt = (ctx: VariableStmtContext) => this.enterVarOrConstStmt(ctx); - enterConstStmt = (ctx: ConstStmtContext) => this.enterVarOrConstStmt(ctx); - - private enterVarOrConstStmt(ctx: VariableStmtContext | ConstStmtContext) { - const declaration = new VariableStatementElement(ctx, this.doc); - this.resultsContainer.addElement(declaration); - declaration.variableList.forEach((v) => this.resultsContainer.addScopeDeclaration(v)); - } - - enterImplicitCallStmt_InStmt = (ctx: ImplicitCallStmt_InStmtContext) => this.enterImplicitCallStmt(ctx); - enterImplicitCallStmt_InBlock = (ctx: ImplicitCallStmt_InBlockContext) => this.enterImplicitCallStmt(ctx); - - private enterImplicitCallStmt(ctx: ImplicitCallStmt_InStmtContext | ImplicitCallStmt_InBlockContext) { - // console.log('imp call ' + ctx.text); - } - - enterICS_S_MembersCall?: ((ctx: ICS_S_MembersCallContext) => void) | undefined; - enterICS_S_VariableOrProcedureCall?: ((ctx: ICS_S_VariableOrProcedureCallContext) => void) | undefined; - - enterAmbiguousIdentifier = (ctx: AmbiguousIdentifierContext) => { - if (this.inModuleConfig || this.inAttributeStatement) - return; - - // if(ctx.ambiguousKeyword(0).text == "Me") { - // console.log(`me: ${ctx.text}`); - // } - - try { - if (ctx.ambiguousKeyword().length == 0) { - const ctxText = ctx.text; - let pntText = ctxText; - let pnt: ParserRuleContext = ctx; - - while (ctxText == pntText && pnt.parent) { - pnt = pnt.parent; - pntText = pnt.text; - } - - console.log(`ident: ${ctxText}: ${pntText}`); - return; - } - if(ctx.ambiguousKeyword(0).text == "Me") { - console.log(`ident: ${ctx.text}`); - } - } - catch (e: any) { - console.log(`error: ${ctx.toString()}`); - console.log(e); - } - }; - - - enterLetStmt = (ctx: LetStmtContext) => this.enterAssignStmt(ctx); - enterSetStmt = (ctx: SetStmtContext) => this.enterAssignStmt(ctx); - - private enterAssignStmt(ctx: LetStmtContext | SetStmtContext) { - try { - const assignment = new VariableAssignElement(ctx, this.doc); - this.resultsContainer - .addElement(assignment) - .addScopeReferences(assignment); - - // Need to add a variable or call reference element. - // Should also take into account literals. And I don't think - // the logic should be here - add method to resultsContainer - // const left = ctx.implicitCallStmt_InStmt(); - - // const right = ctx.valueStmt(); - // this.resultsContainer.addScopeReference(assignment.leftImplicitCall) - } - catch (e) { - console.log(`ERROR: ${ctx.text}\n${e}`); - } - } - - private registerFoldingRange(ctx: ParserRuleContext) { - this.resultsContainer.addFoldingRange( - new FoldableElement(ctx, this.doc)); - } -} - -class VbaErrorListener extends ConsoleErrorListener { - syntaxError(recognizer: Recognizer, offendingSymbol: T, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined): void { - super.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); - console.error(e); - if (e) { - const y = recognizer.getErrorHeader(e); - console.log(y); - } - recognizer.inputStream?.consume(); - } -} \ No newline at end of file diff --git a/server/src/scope.json b/server/src/scope.json deleted file mode 100644 index 56c0b6f..0000000 --- a/server/src/scope.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "doc": { - "names": { - "MYCONSTANT": [ - "CONST", - "PUBLIC", - "String", - "literal value" - ], - "mMyPrivateVar": [ - "VAR", - "PRIVATE", - "Long" - ], - "MyFunction": [ - "FUNC", - "PUBLIC", - "String", - null, - [ - "arg1", - "arg2" - ] - ] - }, - "objects": { - "MYCONSTANT": { - "storageType": "C", - "returnType": "String", - "access": 1, //public - "literal": "some string" - }, - "mMyPrivateVar": { - "storageType": "V", - "returnType": "Long", - "access": 0 //private - }, - "MyFunction": { - "storageType": "F", - "returnType": "Long", - "access": 1, //public - "args": [ - { - "name": "arg1", - "optional": false, - "default": "nice", - "returnType": "String" - } - ] - } - }, - "scopes": { - "MyFunction": { - "names": {}, - "objects": {}, - "scopes": {} - } - } - } -} \ No newline at end of file diff --git a/server/src/scope.ts b/server/src/scope.ts deleted file mode 100644 index 915b001..0000000 --- a/server/src/scope.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { BaseDeclaration, BaseReference } from './parser/elements/memory/base'; - -class Project { - names: Map = new Map(); - scopes: Map = new Map(); - - constructor() { - // Me should never be a scope name as it's reserved. - this.scopes.set("Me", new Scope()); - } - - /** - * Attempts to find a project scope from the passed in namespace. - * @param namespace the namespace for the scope. - * @returns a project scope if found. - */ - getExplicitScope(namespace: string): Scope | undefined { - - return; - } - - getImplicitScope(namespace: string): Scope { - return this.scopes.get("Me")!; - } - - addScopedDeclaration(element: BaseDeclaration) { - // TODO: Declarations cannot override in VBA so a public / module level - // declaration that is redeclared anywhere (including method) is error. - const ns = element.isPublic ? "Me" : element.namespace.replace(/\.\w+$/, ""); - const scope = this.scopes.get(ns); - if(scope) { - scope.addDeclaration(element); - } - } -} - -class Scope { - declarations: Map = new Map(); - undeclaredReferences: BaseReference[] = []; - - addDeclaration(element: BaseDeclaration) { - const declarationName = element.identifierText; - } - - addReference(element: BaseReference) { - const declarations = this.declarations.get(element.identifierText); - if(declarations) { - declarations.forEach(declaration => { - declaration.references.push(element); - }); - } - this.undeclaredReferences.push(element); - } - - resolveReferences(globals: Map) { - this.undeclaredReferences.forEach(ref => { - const refName = ref.identifierText; - const global = globals.get(refName); - if(global) { - // Only the first declaration matters. - global[0].references.push(ref); - ref.declarationElement = global[0]; - } - }); - } -} - From dade8e4734a94fa37143a6216cf05caa0bb8abf0 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 10:24:10 +0800 Subject: [PATCH 06/29] Add a Contributing md --- contributing.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 contributing.md diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..d6e83d9 --- /dev/null +++ b/contributing.md @@ -0,0 +1,35 @@ +# Contributing + +## Setup + +1. Clone repo +2. Install Java >= 11 +3. Install NPM +4. `npm install` to install dependencies. +5. `npm run textMate` first time and every time grammar is changed. +6. `npm run antlr4ts` first time and every time grammar is changed. +7. (Optional) Install [ANTLR4 grammar syntax support](https://marketplace.visualstudio.com/items?itemName=mike-lischke.vscode-antlr4) VSCode extension. + +## Debugging Language Server + +* Use the Client + Server launch configuration. + +## Debugging Antlr Grammar + +Requires the ANTLR4 grammar syntax support extension installed. Each time you open the workspace, you'll need to open a grammar file to activate the extension. + +Note the grammar file for this project can be found at /server/src/antlr/vba.g4 + +### Show Parse Tree + +* Open the VBA file you want to debug. +* Run `Debug ANTLR4 grammar` from launch configurations. + +### Debug Rule + +* Open the grammar file. +* Right click a rule and choose an option from the context menu. + * Show Railroad Diagram for Rule + * Show ATN Graph for Rule + +The grammar call graph is interesting, but not particularly useful. Generating "valid" input generates garbage. From 060a5b67cba3f2888289568a28314c6135470151 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 10:24:32 +0800 Subject: [PATCH 07/29] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0a24e62..06f39f7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ out node_modules client/server .vscode-test -test*/** \ No newline at end of file +sample*/** \ No newline at end of file From 38f4fdc98dd218ca6c43cefa412d7d53188d48fb Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 10:26:00 +0800 Subject: [PATCH 08/29] Start debugging in sample workspace --- .vscode/launch.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index dc52bd8..b2ca3b6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,10 @@ "request": "launch", "name": "Launch Client", "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}"], + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}", + "${workspaceFolder}/sample" + ], "outFiles": ["${workspaceRoot}/client/out/**/*.js"], "preLaunchTask": { "type": "npm", From 6e28794f8b6aab49c6afd1de030b66c8bcb2460a Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 11:18:17 +0800 Subject: [PATCH 09/29] Added capabilities --- server/src/capabilities/folding.ts | 57 ++++++++++++ server/src/capabilities/semanticTokens.ts | 93 ++++++++++++++++---- server/src/capabilities/symbolInformation.ts | 15 ++++ server/src/project/elements/base.ts | 92 +++++++++++++++++++ server/src/project/elements/special.ts | 15 ++++ server/src/project/workspace.ts | 10 +-- server/src/server.ts | 6 +- 7 files changed, 265 insertions(+), 23 deletions(-) create mode 100644 server/src/capabilities/folding.ts create mode 100644 server/src/capabilities/symbolInformation.ts create mode 100644 server/src/project/elements/base.ts create mode 100644 server/src/project/elements/special.ts diff --git a/server/src/capabilities/folding.ts b/server/src/capabilities/folding.ts new file mode 100644 index 0000000..da503c2 --- /dev/null +++ b/server/src/capabilities/folding.ts @@ -0,0 +1,57 @@ +import { FoldingRange as LSFoldingRange } from 'vscode-languageserver'; +import { FoldableElement } from '../project/elements/special'; + +/** + * Enum of known range kinds + * ~~ https://github.com/microsoft/vscode-languageserver-protocol-foldingprovider/blob/master/protocol.foldingProvider.md + */ +export enum FoldingRangeKind { + /** + * Folding range for a comment + */ + Comment = 'comment', + /** + * Folding range for a imports or includes + */ + Imports = 'imports', + /** + * Folding range for a region (e.g. `#region`) + */ + Region = 'region' +} + + +export class FoldingRange implements LSFoldingRange { + /** + * The zero-based line number from where the folded range starts. + */ + startLine: number; + + /** + * The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line. + */ + startCharacter?: number; + + /** + * The zero-based line number where the folded range ends. + */ + endLine: number; + + /** + * The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line. + */ + endCharacter?: number; + + /** + * Describes the kind of the folding range such as 'comment' or 'region'. The kind + * is used to categorize folding ranges and used by commands like 'Fold all comments'. See + * [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds. + */ + kind?: string; + + constructor(element: FoldableElement, foldingRangeKind?: FoldingRangeKind) { + this.startLine = element.range.start.line; + this.endLine = element.range.end.line; + this.kind = foldingRangeKind; + } +} \ No newline at end of file diff --git a/server/src/capabilities/semanticTokens.ts b/server/src/capabilities/semanticTokens.ts index 13fe7ff..4bca659 100644 --- a/server/src/capabilities/semanticTokens.ts +++ b/server/src/capabilities/semanticTokens.ts @@ -1,25 +1,26 @@ -import {InitializeResult, Range, SemanticTokenModifiers, SemanticTokenTypes, uinteger, _LanguagesImpl} from 'vscode-languageserver'; +import {InitializeResult, Range, SemanticTokenModifiers, SemanticTokenTypes, uinteger, _LanguagesImpl, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams} from 'vscode-languageserver'; +import { HasSemanticToken } from '../project/elements/base'; -const tokenTypes = new Map((Object.keys(SemanticTokenTypes) as (keyof typeof SemanticTokenTypes)[]).map((k, i) => ([k, i]))); -const tokenModifiers = new Map((Object.keys(SemanticTokenModifiers) as (keyof typeof SemanticTokenModifiers)[]).map((k, i) => ([k, 2**i]))); +const registeredTokenTypes = new Map((Object.keys(SemanticTokenTypes) as (keyof typeof SemanticTokenTypes)[]).map((k, i) => ([k, i]))); +const registeredTokenModifiers = new Map((Object.keys(SemanticTokenModifiers) as (keyof typeof SemanticTokenModifiers)[]).map((k, i) => ([k, 2**i]))); export function activateSemanticTokenProvider(result: InitializeResult) { result.capabilities.semanticTokensProvider = { "full": true, legend: { - tokenTypes: Array.from(tokenTypes.keys()), - tokenModifiers: Array.from(tokenModifiers.keys()), + tokenTypes: Array.from(registeredTokenTypes.keys()), + tokenModifiers: Array.from(registeredTokenModifiers.keys()), } }; } -export function sortSemanticTokens(toks: SemanticToken[]): SemanticToken[] { - return toks.sort((a, b) => { +export function sortSemanticTokens(tokens: SemanticToken[]): SemanticToken[] { + return tokens.sort((a, b) => { // Sort a before than b. - if ((a.line < b.line) || (a.line === b.line && a.startChar < b.startChar)) + if ((a.line < b.line) || (a.line === b.line && a.char < b.char)) return -1; // Sort b before a. - if ((a.line > b.line) || (a.line === b.line && a.startChar > b.startChar)) + if ((a.line > b.line) || (a.line === b.line && a.char > b.char)) return 1; // No difference. return 0; @@ -28,21 +29,35 @@ export function sortSemanticTokens(toks: SemanticToken[]): SemanticToken[] { export class SemanticToken { line: uinteger; - startChar: uinteger; + char: uinteger; length: uinteger; tokenType: uinteger; tokenModifiers: uinteger = 0; + element: HasSemanticToken; - constructor(line: uinteger, startChar: uinteger, length: uinteger, tokenType: SemanticTokenTypes, tokenModifier: SemanticTokenModifiers[]) { + constructor(element: HasSemanticToken, line: uinteger, startChar: uinteger, length: uinteger, tokenType: SemanticTokenTypes, tokenModifiers: SemanticTokenModifiers[]) { + this.element = element; this.line = line; - this.startChar = startChar; + this.char = startChar; this.length = length; - this.tokenType = tokenTypes.get(tokenType)!; - tokenModifier.forEach((x) => this.tokenModifiers += tokenModifiers.get(x) ?? 0); + this.tokenType = registeredTokenTypes.get(tokenType)!; + tokenModifiers.forEach((x) => this.tokenModifiers += registeredTokenModifiers.get(x) ?? 0); + } + + static create(element: HasSemanticToken): SemanticToken { + return new SemanticToken( + element, + element.identifier.range.start.line, + element.identifier.range.start.character, + element.identifier.context.text.length, + element.tokenType, + element.tokenModifiers + ); } toNewRange(range: Range): SemanticToken { const token = new SemanticToken( + this.element, range.start.line, range.start.character, this.length, @@ -56,7 +71,55 @@ export class SemanticToken { toDeltaToken(line: uinteger = 0, startChar: uinteger = 0): uinteger[] { const deltaLine = this.line - line; - const deltaChar = deltaLine === 0 ? this.startChar - startChar : this.startChar; + const deltaChar = deltaLine === 0 ? this.char - startChar : this.char; return [deltaLine, deltaChar, this.length, this.tokenType, this.tokenModifiers]; } + + toSemanticTokensArray(reference?: SemanticToken): uinteger[] { + const line = this.line - (reference?.line ?? 0); + const char = line === 0 ? this.char - reference!.char : this.char; + return [ + line, + char, + this.length, + this.tokenType, + this.tokenModifiers + ]; + } +} + + +/** + * Tracks, sorts, and provides LSP response for SemanticTokens. + */ +export class SemanticTokensManager { + private _tokens: SemanticToken[] = []; + + private _tokensInRange = (range: Range) => + this._tokens.filter(token => token.element.isChildOf(range)); + + add(element: HasSemanticToken) { + this._tokens.push(SemanticToken.create(element)); + } + + // getSemanticTokens(params: SemanticTokensParams): SemanticTokens | null; + // getSemanticTokens(rangeParams: SemanticTokensRangeParams): SemanticTokens | null; + // getSemanticTokens(_?: SemanticTokensParams, rangeParams?: SemanticTokensRangeParams): SemanticTokens | null { + getSemanticTokens(range?: Range): SemanticTokens | null { + // Get the range if we have one. + // const range: Range | undefined = rangeParams?.range; + + // Filter and sort the semantic tokens. + const filteredTokens = range ? this._tokensInRange(range) : this._tokens; + const sortedTokens = sortSemanticTokens(filteredTokens); + if (sortedTokens.length === 0) + return null; + + // Get an array of SemanticTokens relative to previous token. + const packedData: uinteger[][] = sortedTokens.map((token, i) => + token.toSemanticTokensArray(i === 0 ? undefined : sortedTokens[i - 1])); + + // Return the flattened array. + return { data: ([] as uinteger[]).concat(...packedData) }; + } } diff --git a/server/src/capabilities/symbolInformation.ts b/server/src/capabilities/symbolInformation.ts new file mode 100644 index 0000000..bf94b5c --- /dev/null +++ b/server/src/capabilities/symbolInformation.ts @@ -0,0 +1,15 @@ +import { SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { NamedSyntaxElement } from '../project/elements/base'; + + +export class SymbolInformationFactory { + static create(element: NamedSyntaxElement, symbolKind: SymbolKind): SymbolInformation { + return SymbolInformation.create( + element.name, + symbolKind, + element.range, + element.uri + ); + } +} + diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts new file mode 100644 index 0000000..fcd1ce0 --- /dev/null +++ b/server/src/project/elements/base.ts @@ -0,0 +1,92 @@ +import { ParserRuleContext } from 'antlr4ts'; +import { getCtxRange } from '../../utils/helpers'; +import { SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { Position, Range, TextDocument } from 'vscode-languageserver-textdocument'; +import { FoldingRangeKind } from '../../capabilities/folding'; +import { IdentifierElement } from './memory'; +import { AttributeStmtContext } from '../../antlr/out/vbaParser'; + +export interface ContextOptionalSyntaxElement { + range?: Range; + parent?: ContextOptionalSyntaxElement; + context?: ParserRuleContext; + get text(): string; + get uri(): string; + + isChildOf(range: Range): boolean; +} + +interface SyntaxElement extends ContextOptionalSyntaxElement { + range: Range; + context: ParserRuleContext; +} + +export interface HasAttribute { + processAttribute(context: AttributeStmtContext): void; +} + +export interface NamedSyntaxElement extends SyntaxElement { + get name(): string; +} + +export interface IdentifiableSyntaxElement extends NamedSyntaxElement { + identifier: IdentifierElement; +} + +export interface HasSymbolInformation extends NamedSyntaxElement { + symbolInformation: SymbolInformation; +} + +export interface HasSemanticToken extends NamedSyntaxElement, IdentifiableSyntaxElement { + tokenType: SemanticTokenTypes; + tokenModifiers: SemanticTokenModifiers[]; +} + +export interface MemoryElement extends BaseSyntaxElement { + name: string; + returnType: any; + symbol: SymbolKind; +} + +export interface FoldingRangeElement { + range: Range; + foldingRangeKind?: FoldingRangeKind; +} + +export abstract class BaseSyntaxElement implements ContextOptionalSyntaxElement { + protected document: TextDocument; + + range?: Range; + parent?: ContextOptionalSyntaxElement; + context?: ParserRuleContext; + + get text(): string { return this.context?.text ?? ''; } + get uri(): string { return this.document.uri; } + + constructor(context: ParserRuleContext | undefined, document: TextDocument) { + this.context = context; + this.document = document; + this.range = context ? getCtxRange(context, document) : undefined; + } + + isChildOf = (range: Range): boolean => { + if (!this.range) { + return false; + } + + const isPositionBefore = (x: Position, y: Position) => + x.line < y.line || (x.line === y.line && x.character <= y.character); + + return isPositionBefore(range.start, this.range.start) + && isPositionBefore(this.range.end, range.end); + }; +} + +export abstract class BaseContextSyntaxElement extends BaseSyntaxElement { + range!: Range; + context!: ParserRuleContext; + + constructor(ctx: ParserRuleContext, doc: TextDocument) { + super(ctx, doc); + } +} \ No newline at end of file diff --git a/server/src/project/elements/special.ts b/server/src/project/elements/special.ts new file mode 100644 index 0000000..876cd8c --- /dev/null +++ b/server/src/project/elements/special.ts @@ -0,0 +1,15 @@ +import { ParserRuleContext } from 'antlr4ts'; +import { FoldingRangeKind } from '../../capabilities/folding'; +import { BaseContextSyntaxElement, FoldingRangeElement } from './base'; +import { Range, TextDocument } from 'vscode-languageserver-textdocument'; + + +export class FoldableElement extends BaseContextSyntaxElement implements FoldingRangeElement { + range!: Range; + foldingRangeKind?: FoldingRangeKind; + + constructor(ctx: ParserRuleContext, doc: TextDocument, foldingRangeKind?: FoldingRangeKind) { + super(ctx, doc); + this.foldingRangeKind = foldingRangeKind; + } +} diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index 1974285..fec2a39 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -112,14 +112,14 @@ class WorkspaceEvents { } private async onDocumentSymbolAsync(params: DocumentSymbolParams): Promise { - console.log('onDocumentSymbolAsync: ' + params); - await sleep(1); - return []; + console.log(`onDocumentSymbolAsync: ${params.textDocument.uri}`); + return await this.activeDocument?.languageServerSymbolInformationAsync() ?? []; } private onFoldingRanges(params: FoldingRangeParams): FoldingRange[] { - console.log('onFoldingRanges: ' + params.textDocument.uri); - return []; + const foldingRanges = this._workspace.activeDocument?.foldableElements ?? []; + console.log(`onFoldingRanges: ${params.textDocument.uri} (${foldingRanges.length} ranges)`); + return foldingRanges; } private onHover(params: HoverParams): Hover { diff --git a/server/src/server.ts b/server/src/server.ts index eb0b719..38a1a65 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -56,8 +56,8 @@ export class LanguageServerConfiguration { hoverProvider: false, textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: { resolveProvider: false }, - foldingRangeProvider: false, - documentSymbolProvider: false, + foldingRangeProvider: true, + documentSymbolProvider: true, }; constructor(params: InitializeParams) { @@ -68,7 +68,7 @@ export class LanguageServerConfiguration { class ConnectionInitializeResult implements InitializeResult { [custom: string]: any; capabilities: ServerCapabilities; - serverInfo?: { name: string; version?: string | undefined; } | undefined; + serverInfo?: { name: string; version?: string; }; constructor(capabilities: ServerCapabilities) { this.capabilities = capabilities; From 175b7856d583cb05feeff01ad18196db5243790f Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 11:19:21 +0800 Subject: [PATCH 10/29] Extensions and helpers --- server/src/extensions/parserExtensions.ts | 93 +++++++++++++++++++++++ server/src/extensions/stringExtensions.ts | 12 +++ server/src/project/elements/base.ts | 4 +- server/src/utils/helpers.ts | 87 --------------------- 4 files changed, 107 insertions(+), 89 deletions(-) create mode 100644 server/src/extensions/parserExtensions.ts create mode 100644 server/src/extensions/stringExtensions.ts diff --git a/server/src/extensions/parserExtensions.ts b/server/src/extensions/parserExtensions.ts new file mode 100644 index 0000000..e79ed10 --- /dev/null +++ b/server/src/extensions/parserExtensions.ts @@ -0,0 +1,93 @@ +import { Range, SymbolKind } from 'vscode-languageserver'; +import { BaseTypeContext, ComplexTypeContext } from '../antlr/out/vbaParser'; +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { ParserRuleContext } from 'antlr4ts'; + + +declare module 'antlr4ts' { + export interface ParserRuleContext { + toRange(document: TextDocument): Range; + } +} + +ParserRuleContext.prototype.toRange = function (document: TextDocument): Range { + const startIndex = this.start.startIndex; + const stopIndex = this.stop?.stopIndex ?? startIndex; + return Range.create( + document.positionAt(startIndex), + document.positionAt(stopIndex) + ); +}; + + +declare module '../antlr/out/vbaParser' { + export interface BaseTypeContext { + toSymbolKind(): SymbolKind; + } + + export interface ComplexTypeContext { + toSymbolKind(): SymbolKind; + } + + export interface ParserRuleContext { + toRange(document: TextDocument): Range; + } +} + +BaseTypeContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this); +}; + +ComplexTypeContext.prototype.toSymbolKind = function (): SymbolKind { + return toSymbolKind(this); +}; + +function toSymbolKind(context: BaseTypeContext | ComplexTypeContext): SymbolKind { + switch (context.text.toLocaleLowerCase()) { + case 'boolean': + return SymbolKind.Boolean; + case 'byte': + case 'string': + return SymbolKind.String; + case 'double': + case 'currency': + case 'integer': + case 'long': + case 'longPtr': + case 'longLong': + return SymbolKind.Number; + case 'object': + return SymbolKind.Object; + default: + return SymbolKind.Class; + } +} + +/** + * const File: 1; + const Module: 2; + const Namespace: 3; + const Package: 4; + const Class: 5; + const Method: 6; + const Property: 7; + const Field: 8; + const Constructor: 9; + const Enum: 10; + const Interface: 11; + const Function: 12; + const Variable: 13; + const Constant: 14; + const String: 15; + const Number: 16; + const Boolean: 17; + const Array: 18; + const Object: 19; + const Key: 20; + const Null: 21; + const EnumMember: 22; + const Struct: 23; + const Event: 24; + const Operator: 25; + const TypeParameter: 26; + */ \ No newline at end of file diff --git a/server/src/extensions/stringExtensions.ts b/server/src/extensions/stringExtensions.ts new file mode 100644 index 0000000..24caf61 --- /dev/null +++ b/server/src/extensions/stringExtensions.ts @@ -0,0 +1,12 @@ +import '.'; + +declare global { + export interface String { + stripQuotes(): string; + } +} + +String.prototype.stripQuotes = function (): string { + const exp = /^"?(.*?)"?$/; + return exp.exec(this.toString())![1]; +}; diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index fcd1ce0..b2760d1 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -1,10 +1,10 @@ import { ParserRuleContext } from 'antlr4ts'; -import { getCtxRange } from '../../utils/helpers'; import { SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; import { Position, Range, TextDocument } from 'vscode-languageserver-textdocument'; import { FoldingRangeKind } from '../../capabilities/folding'; import { IdentifierElement } from './memory'; import { AttributeStmtContext } from '../../antlr/out/vbaParser'; +import '../../extensions/parserExtensions'; export interface ContextOptionalSyntaxElement { range?: Range; @@ -66,7 +66,7 @@ export abstract class BaseSyntaxElement implements ContextOptionalSyntaxElement constructor(context: ParserRuleContext | undefined, document: TextDocument) { this.context = context; this.document = document; - this.range = context ? getCtxRange(context, document) : undefined; + this.range = context ? context.toRange(document) : undefined; } isChildOf = (range: Range): boolean => { diff --git a/server/src/utils/helpers.ts b/server/src/utils/helpers.ts index 93322bf..7e27081 100644 --- a/server/src/utils/helpers.ts +++ b/server/src/utils/helpers.ts @@ -1,90 +1,3 @@ -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { SyntaxElement } from '../parser/elements/base'; -import { ParserRuleContext } from 'antlr4ts'; -import { Range } from 'vscode-languageserver'; - -export function indexOfNearestUnder(arr: Array, n: number) { - if (arr.length === 0) { return -1; } - for (let i = 0; i < arr.length; i++) { - const result = n - arr[i]; - if (result === 0) { return i; } - if (result < 0) { return i - 1; } - } - return arr.length - 1; -} - -export function getMatchIndices(re: RegExp, text: string, maxReturned?: number): number[] { - let m = re.exec(""); - let i = 0; - const results: number[] = []; - while ((m = re.exec(text)) && i++ < (maxReturned ?? 2000)) { - results.push(groupIndex(m, 1)); - } - return results; -} - -export function stripQuotes(text: string): string { - const exp = /^"?(.*?)"?$/; - return exp.exec(text)![1]; -} - -function groupIndex(reExec: RegExpExecArray, i: number) { - return reExec.index + reExec[0].indexOf(reExec[i]); -} - -export function rangeIsChildOfElement(tr: Range, element: SyntaxElement): boolean { - const pr = element.range; - - const psl = pr.start.line; - const psc = pr.start.character; - const tsl = tr.start.line; - const tsc = tr.start.character; - - const pel = pr.end.line; - const pec = pr.end.character; - const tel = tr.end.line; - const tec = tr.end.character; - - const prStartEarlier = (psl < tsl) || (psl === tsl && psc <= tsc); - const prEndsAfter = (pel > tel) || (pel === tel && pec >= tec); - - return prStartEarlier && prEndsAfter; -} - export function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms) ); } - -/** - * Gets the document Range address of the context element. - * @param ctx the context element - * @param doc the underlying document - * @returns A Range representing an address of the context in the document. - */ -export function getCtxRange(ctx: ParserRuleContext, doc: TextDocument): Range { - const start = ctx.start.startIndex; - const stop = ctx.stop?.stopIndex ?? start; - return Range.create( - doc.positionAt(start), - doc.positionAt(stop + 1)); -} - -/** - * Represents the range as a line:char address string. - * @param r a range object. - * @returns The line/char address of the range. - */ -export function rangeAddress(r: Range): string { - const sl = r.start.line; - const el = r.end.line; - const sc = r.start.character; - const ec = r.end.character; - - if(sl==el && sc==ec) { - return `${sl}:${sc}`; - } - if(sl==el) { - return `${sl}:${sc}-${ec}`; - } - return `${sl}:${sc}-${el}:${ec}`; -} From 9dcbc8073c0999988118ccf18419026ddb03f046 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 11:19:37 +0800 Subject: [PATCH 11/29] Removed old project files --- server/src/utils/converters.ts | 22 -- server/src/utils/diagnositics.ts | 42 --- server/src/utils/nameTree.ts | 137 -------- server/src/utils/vbaSyntaxElements.ts | 443 -------------------------- server/src/utils/vbaSyntaxParser.ts | 147 --------- 5 files changed, 791 deletions(-) delete mode 100644 server/src/utils/converters.ts delete mode 100644 server/src/utils/diagnositics.ts delete mode 100644 server/src/utils/nameTree.ts delete mode 100644 server/src/utils/vbaSyntaxElements.ts delete mode 100644 server/src/utils/vbaSyntaxParser.ts diff --git a/server/src/utils/converters.ts b/server/src/utils/converters.ts deleted file mode 100644 index 06cfee2..0000000 --- a/server/src/utils/converters.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SymbolKind } from 'vscode-languageserver'; - - -export function vbaTypeToSymbolConverter(t: string): SymbolKind { - switch (t.toLocaleLowerCase()) { - case 'boolean': - return SymbolKind.Boolean; - case 'single': - case 'double': - case 'currency': - case 'long': - case 'longlong': - case 'longptr': - return SymbolKind.Number; - case 'string': - return SymbolKind.String; - case 'variant': - return SymbolKind.Variable; - default: - return SymbolKind.Object; - } -} \ No newline at end of file diff --git a/server/src/utils/diagnositics.ts b/server/src/utils/diagnositics.ts deleted file mode 100644 index 59326fa..0000000 --- a/server/src/utils/diagnositics.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CompletionItem, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbol, FoldingRange, Hover, HoverParams, Location, NotificationHandler, Position, PublishDiagnosticsParams, Range, SemanticTokenModifiers, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRequest, SymbolInformation, SymbolKind, TextDocumentPositionParams, TextDocuments, uinteger, _Connection, integer } from 'vscode-languageserver'; - - -abstract class BaseDiagnostic { - description: string; - severity: DiagnosticSeverity; - errCode: integer; - - constructor() { - this.description = "Not assigned!"; - this.severity = DiagnosticSeverity.Information; - this.errCode = 0; - } - - create(range: Range, relatedInfo?: DiagnosticRelatedInformation[] | undefined) { - return Diagnostic.create( - range, - this.description, - this.severity, - this.errCode, - "VBAPro", - relatedInfo - ); - } -} - -export class AmbiguousDeclarationDiagnostic extends BaseDiagnostic { - description = "Ambiguous variable declaration."; - severity = DiagnosticSeverity.Error; - errCode = 500; - -} - -export class MissingDeclarationDiagnostic extends BaseDiagnostic { - description = "No declaration for variable or method."; - errCode = 404; - - constructor (severity: DiagnosticSeverity) { - super(); - this.severity = severity; - } -} \ No newline at end of file diff --git a/server/src/utils/nameTree.ts b/server/src/utils/nameTree.ts deleted file mode 100644 index 4a8fffc..0000000 --- a/server/src/utils/nameTree.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity } from 'vscode-languageserver'; -import { AmbiguousIdentifierContext, CertainIdentifierContext } from '../antlr/out/vbaParser'; -import { MethodElement, SyntaxElement, VariableDeclarationElement } from './vbaSyntaxElements'; - - -enum NameLinkType { - 'variable' = 0, - 'method' = 1, -} - -class Scope { - context: SyntaxElement; - parent?: Scope; - nameRefs: Map = new Map(); - - constructor(ctx: SyntaxElement); - constructor(ctx: SyntaxElement, pnt: Scope); - constructor(ctx: SyntaxElement, pnt?: Scope) { - this.context = ctx; - this.parent = pnt; - } - - addDeclaration(element: VariableDeclarationElement | MethodElement) { - // - } - - addRef(ctx: CertainIdentifierContext | AmbiguousIdentifierContext) { - const nameLink = this.getName(ctx.text); - // if dec - - // if not dec - } - - getName(identifier: string): NameLink { - if (!this.nameRefs.has(identifier)) { - this.nameRefs.set(identifier, new NameLink()); - } - return this.nameRefs.get(identifier)!; - } -} - -class NameLink { - type: NameLinkType = NameLinkType.variable; - - // The original decalarations that affect this name. - // 0: Variable or method not declared. - // 1: Declared once. - // 2: Multiple conflicting declarations. - declarations: SyntaxElement[] = []; - - // The places this name is referenced. - references: SyntaxElement[] = []; - diagnostics: Diagnostic[] = []; - - private diagnosticRelatedInfo: DiagnosticRelatedInformation[] = []; - - merge(link: NameLink) { - this.declarations.concat(link.declarations); - this.references.concat(link.references); - this.diagnostics.concat(link.diagnostics); - - if (link.declarations.length > 0) { - this.type = link.type; - } - } - - process(optExplicit = false) { - this.processDiagnosticRelatedInformation(); - this.validateDeclarationCount(optExplicit); - this.validateMethodSignatures(); - - this.assignSemanticTokens(); - this.assignDiagnostics(); - } - - private processDiagnosticRelatedInformation() { - this.diagnosticRelatedInfo = this.declarations - .concat(this.references) - .map((x) => DiagnosticRelatedInformation.create( - x.location(), - x.text - )); - } - - private validateDeclarationCount(optExplicit: boolean) { - if (this.declarations.length === 1) { - return; - } - - const undecSeverity = (optExplicit) ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning; - if (this.declarations.length === 0) { - if (optExplicit || this.type !== NameLinkType.variable) - this.references.forEach((x) => - this.diagnostics.push(Diagnostic.create( - x.range, - "No declaration for variable or method.", - undecSeverity, - 404, - 'VbaPro', - this.diagnosticRelatedInfo - ))); - return; - } - this.declarations.forEach((x) => - this.diagnostics.push(Diagnostic.create( - x.range, - "Ambiguous variable declaration", - DiagnosticSeverity.Error, - 500, - 'VbaPro', - this.diagnosticRelatedInfo - ))); - } - - private assignSemanticTokens() { - if (this.declarations.length === 0) { - return; - } - - this.references.forEach((x) => x.semanticToken = - this.declarations[0] - .semanticToken - ?.toNewRange(x.range)); - } - - private assignDiagnostics() { - if (this.diagnostics.length === 0) { - return; - } - const els = this.declarations.concat(this.references); - els.forEach((x) => x.addDiagnostics(this.diagnostics)); - } - - private validateMethodSignatures() { - // TODO: implement. - } -} \ No newline at end of file diff --git a/server/src/utils/vbaSyntaxElements.ts b/server/src/utils/vbaSyntaxElements.ts deleted file mode 100644 index 0d2062f..0000000 --- a/server/src/utils/vbaSyntaxElements.ts +++ /dev/null @@ -1,443 +0,0 @@ -import { ParserRuleContext } from 'antlr4ts'; -import { Diagnostic, FoldingRange, Hover, Location, Range, SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind, uinteger } from 'vscode-languageserver'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { AmbiguousIdentifierContext, AttributeStmtContext, BaseTypeContext, ComplexTypeContext, ConstStmtContext, ConstSubStmtContext, EnumerationStmtContext, EnumerationStmt_ConstantContext, FunctionStmtContext, ICS_S_VariableOrProcedureCallContext, LiteralContext, ModuleContext, SubStmtContext, VariableStmtContext, VariableSubStmtContext } from '../antlr/out/vbaParser'; -import { SemanticToken } from '../capabilities/vbaSemanticTokens'; -import { vbaTypeToSymbolConverter } from './converters'; -import { stripQuotes } from './helpers'; - -export {SyntaxElement, IdentifiableSyntaxElement, UnknownElement, ModuleElement, ModuleAttribute, MethodDeclarationElement as MethodElement, EnumElement, EnumConstant, VariableDeclarationStatementElement as VariableStatementElement, VariableDeclarationElement, IdentifierElement}; - -interface SyntaxElement { - uri: string; - text: string; - range: Range; - identifier?: IdentifierElement; - context: ParserRuleContext; - symbolKind: SymbolKind; - semanticToken?: SemanticToken; - diagnostics: Diagnostic[]; - hoverText: string; - fqName?: string; - - parent?: SyntaxElement; - children: SyntaxElement[]; - - getAncestorCount(): number; - hover(): Hover | undefined; - isChildOf(element: SyntaxElement): boolean; - symbolInformation(uri: string): SymbolInformation | undefined; - foldingRange(): FoldingRange | undefined; - location(): Location; - addDiagnostics(diagnostics: Diagnostic[]): void; -} - -interface IdentifiableSyntaxElement { - identifier: IdentifierElement; - ambiguousIdentifier(): AmbiguousIdentifierContext; - ambiguousIdentifier(i: number): AmbiguousIdentifierContext; -} - -abstract class BaseElement implements SyntaxElement { - uri: string; - text: string; - range: Range; - identifier?: IdentifierElement; - context: ParserRuleContext; - symbolKind: SymbolKind; - semanticToken?: SemanticToken; - diagnostics: Diagnostic[] = []; - hoverText: string; - fqName?: string; - - parent?: SyntaxElement; - - children: SyntaxElement[] = []; - private _countAncestors = 0; - - constructor(ctx: ParserRuleContext, doc: TextDocument) { - this.uri = doc.uri; - this.context = ctx; - this.range = getCtxRange(ctx, doc); - this.text = ctx.text; - this.symbolKind = SymbolKind.Null; - this.hoverText = ''; - } - - hover = (): Hover | undefined => undefined; - location = (): Location => Location.create(this.uri, this.range); - - isChildOf(element: SyntaxElement): boolean { - const tr = this.range; - const pr = element.range; - - const psl = pr.start.line; - const psc = pr.start.character; - const tsl = tr.start.line; - const tsc = tr.start.character; - - const pel = pr.end.line; - const pec = pr.end.character; - const tel = tr.end.line; - const tec = tr.end.character; - - const prStartEarlier = (psl < tsl) || (psl === tsl && psc <= tsc); - const prEndsAfter = (pel > tel) || (pel === tel && pec >= tec); - - return prStartEarlier && prEndsAfter; - } - - symbolInformation = (uri: string): SymbolInformation | undefined => - SymbolInformation.create( - this.identifier!.text, - this.symbolKind, - this.range, - uri, - this.parent?.identifier?.text ?? ''); - - foldingRange = (): FoldingRange | undefined => - FoldingRange.create( - this.range.start.line, - this.range.end.line, - this.range.start.character, - this.range.end.character - ); - - addDiagnostics(diagnostics: Diagnostic[]) { - this.diagnostics = this.diagnostics.concat(diagnostics); - } - - // protected setIdentifierFromDoc(doc: TextDocument): void { - // if (this.isIdentifiable(this.context)) { - // const identCtx = this.context.ambiguousIdentifier(0); - // if (identCtx) { - // this.identifier = new IdentifierElement(identCtx, doc); - // } - // } - // } - - private getParent(): BaseElement | undefined { - if (this.parent) { - if (this.parent instanceof BaseElement) { - return this.parent; - } - } - } - - getAncestorCount(n = 0): number { - if (this._countAncestors === 0) { - const pnt = this.getParent(); - if (pnt) { - this._countAncestors = pnt.getAncestorCount(n + 1); - return this._countAncestors; - } - } - return this._countAncestors + n; - } - - toString = () => `${"-".repeat(this.getAncestorCount())} ${this.constructor.name}: ${this.context.text}`; -} - -class UnknownElement extends BaseElement { - -} - -class IdentifierElement extends BaseElement { - constructor(ctx: AmbiguousIdentifierContext | LiteralContext, doc: TextDocument) { - super(ctx, doc); - } - - createSemanticToken(tokType: SemanticTokenTypes, tokMods?: SemanticTokenModifiers[]) { - if (!(this.context instanceof AmbiguousIdentifierContext) && !(this.context instanceof LiteralContext)) - return; - - this.semanticToken = new SemanticToken( - this.range.start.line, - this.range.start.character, - this.text.length, - tokType, - tokMods ?? [] - ); - } -} - -class ModuleElement extends BaseElement { - constructor(ctx: ModuleContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.Module; - this.fqName = doc.uri; - } -} - -class MethodDeclarationElement extends BaseElement { - identifier: IdentifierElement; - returnType: TypeContext | undefined; - hasPrivateModifier = false; - - constructor(ctx: FunctionStmtContext | SubStmtContext, doc: TextDocument) { - super(ctx, doc); - this.hasPrivateModifier = !!(ctx.visibility()?.PRIVATE()); - this.identifier = getIdentifier(ctx, doc); - if (ctx instanceof FunctionStmtContext) { - this.returnType = this.getReturnType(doc); - this.symbolKind = SymbolKind.Function; - } else { - this.symbolKind = SymbolKind.Method; - } - } - - private getReturnType(doc: TextDocument): TypeContext | undefined { - const ctx = this.context as FunctionStmtContext; - const asTypeCtx = ctx.asTypeClause(); - if (!asTypeCtx) { return; } - const t = asTypeCtx.type_(); - const typeCtx = t.baseType() ?? t.complexType(); - if (typeCtx) { - return new TypeContext(typeCtx, doc); - } - } - - hover = () => this.getHover(); - - getHoverText(): string { - return this.text; - // const ctx = this.context as FunctionStmtContext | SubStmtContext; - // let typeHint; - // if (ctx instanceof FunctionStmtContext) { - // typeHint = ctx.typeHint(); - // } - // const mdTypeHint = typeHint?.text ?? ''; - - // const argsCtx = ctx.argList(); - - // const codeFence = '`'; - // return `${codeFence}vba\n${mdTypeHint}${this.identifier}()${codeFence}`; - } - - private getHover(): Hover { - return { - contents: this.hoverText, - range: this.range - }; - } -} - -class MethodCallElement extends BaseElement { - /** - * - */ - constructor(ctx: ICS_S_VariableOrProcedureCallContext, doc: TextDocument) { - super(ctx, doc); - - } -} - -class EnumElement extends BaseElement { - constructor(ctx: EnumerationStmtContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.Enum; - if (this.identifier) - this.identifier.createSemanticToken(SemanticTokenTypes.enum); - } -} - -class EnumConstant extends BaseElement { - foldingRange = () => undefined; - symbolInformation = (_: string) => undefined; - constructor(ctx: EnumerationStmt_ConstantContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.EnumMember; - if (this.identifier) { - this.identifier.createSemanticToken( - SemanticTokenTypes.enumMember); - } - } -} - -class VariableDeclarationStatementElement extends BaseElement { - variableList: VariableDeclarationElement[] = []; - hasPrivateModifier = false; - foldingRange = () => undefined; - - constructor(ctx: VariableStmtContext | ConstStmtContext, doc: TextDocument) { - super(ctx, doc); - this.symbolKind = SymbolKind.Variable; - this.hasPrivateModifier = !!(ctx.visibility()?.PRIVATE()); - this.resolveDeclarations(ctx, doc); - } - - hover = (): Hover => ({ - contents: this.hoverText, - range: this.range - }); - - getHoverText(): string { - return this.text; - } - - private resolveDeclarations(ctx: VariableStmtContext | ConstStmtContext, doc: TextDocument) { - if (ctx instanceof VariableStmtContext) { - this.resolveVarDeclarations(ctx, doc); - } else { - this.resolveConstDeclarations(ctx, doc); - } - } - - private resolveVarDeclarations(ctx: VariableStmtContext, doc: TextDocument) { - const tokenMods = this.getSemanticTokenModifiers(ctx); - ctx.variableListStmt().variableSubStmt().forEach((x) => - this.variableList.push(new VariableDeclarationElement(x, doc, tokenMods, this.hasPrivateModifier))); - } - - private resolveConstDeclarations(ctx: ConstStmtContext, doc: TextDocument) { - const tokenMods = this.getSemanticTokenModifiers(ctx); - ctx.constSubStmt().forEach((x) => this.variableList.push( - new VariableDeclarationElement(x, doc, tokenMods, this.hasPrivateModifier))); - } - - private getSemanticTokenModifiers(ctx: VariableStmtContext | ConstStmtContext): SemanticTokenModifiers[] { - const result: SemanticTokenModifiers[] = [SemanticTokenModifiers.declaration]; - if (ctx instanceof VariableStmtContext && ctx.STATIC()) - result.push(SemanticTokenModifiers.static); - if (ctx instanceof ConstStmtContext) - result.push(SemanticTokenModifiers.readonly); - return result; - } -} - -class VariableDeclarationElement extends BaseElement { - asType: TypeContext | undefined; - identifier: IdentifierElement; - hasPrivateModifier = false; - - constructor(ctx: VariableSubStmtContext | ConstSubStmtContext, doc: TextDocument, tokenModifiers: SemanticTokenModifiers[], hasPrivateModifier: boolean) { - super(ctx, doc); - this.identifier = getIdentifier(ctx, doc); - this.hasPrivateModifier = hasPrivateModifier; - this.asType = TypeContext.create(ctx, doc); - this.symbolKind = vbaTypeToSymbolConverter(this.asType?.text ?? 'variant'); - this.createSemanticToken(tokenModifiers); - } - - private createSemanticToken(tokenModifiers: SemanticTokenModifiers[]) { - const name = this.identifier!; - this.semanticToken = new SemanticToken( - name.range.start.line, - name.range.start.character, - name.text.length, - SemanticTokenTypes.variable, - tokenModifiers - ); - } -} - -// class VariableElement extends BaseElement { -// members: MemberElement[] = []; - -// constructor(ctx: ICS_S_VariableOrProcedureCallContext, doc: TextDocument) { -// super(ctx, doc); - -// } -// } - -// class ValueElement extends BaseElement { -// method?: MethodCallElement; -// variable?: VariableElement; - - -// constructor(ctx: ValueStmtContext, doc: TextDocument) { -// super(ctx, doc); - -// } -// } - -// class VariableAssignElement extends BaseElement { -// leftElement: VariableElement; -// rightElement: ValueElement; - -// constructor(ctx: LetStmtContext | SetStmtContext, doc: TextDocument) { -// super(ctx, doc); - -// this.leftElement = this.getLeftHandVariable(doc); -// this.rightElement = new ValueElement(ctx.valueStmt(), doc); - -// const callStmtContext = ctx.implicitCallStmt_InStmt(); -// const varOrProc = callStmtContext.iCS_S_VariableOrProcedureCall(); -// const isProcedure = !!varOrProc?.subscripts(); -// if (isProcedure) { -// // -// } else { -// this.rightElement = getVariableAssignment(ctx.valueStmt(), doc); -// } -// } - -// private getLeftHandVariable(doc: TextDocument): VariableElement { -// const ctx = this.context as LetStmtContext | SetStmtContext; -// const varElement = ctx.implicitCallStmt_InStmt().iCS_S_VariableOrProcedureCall(); -// if (!varElement) { -// throw new Error('Context is not a variable.'); -// } -// return new VariableElement(varElement, doc); -// } - -// private getFromVpc(ctx: ICS_S_VariableOrProcedureCallContext, doc: TextDocument): VariableElement | MethodCallElement { -// if (ctx.subscripts()) { -// return new MethodCallElement(ctx, doc); -// } -// return new VariableElement(ctx, doc); -// } - -// symbolInformation = (_: string) => undefined; -// } - -class ModuleAttribute { - identifier?: IdentifierElement; - literal?: IdentifierElement; - - key = (): string => this.identifier?.text ?? 'Undefined'; - value = (): string => (this.literal) ? stripQuotes(this.literal.text) : 'Undefined'; - - constructor(ctx: AttributeStmtContext, doc: TextDocument) { - const idCtx = ctx - .implicitCallStmt_InStmt() - ?.iCS_S_VariableOrProcedureCall() - ?.ambiguousIdentifier(); - - if (idCtx) { - this.identifier = new IdentifierElement(idCtx, doc); - this.literal = new IdentifierElement(idCtx, doc); - } - } -} - -class TypeContext extends BaseElement { - constructor(ctx: BaseTypeContext | ComplexTypeContext, doc: TextDocument) { - super(ctx, doc); - } - - static create(ctx: VariableSubStmtContext | ConstSubStmtContext, doc: TextDocument): TypeContext | undefined { - const xCtx = ctx.asTypeClause()?.type_(); - const tCtx = xCtx?.baseType() ?? xCtx?.complexType(); - if (tCtx) { return new TypeContext(tCtx, doc); } - } -} - -function getCtxRange(ctx: ParserRuleContext, doc: TextDocument): Range { - const start = ctx.start.startIndex; - const stop = ctx.stop?.stopIndex ?? start; - return Range.create( - doc.positionAt(start), - doc.positionAt(stop + 1)); -} - -function getIdentifier(ctx: ParserRuleContext, doc: TextDocument): IdentifierElement { - if (isIdentifiable(ctx)) { - return new IdentifierElement( - ctx.ambiguousIdentifier(0), doc); - } - throw new Error('Not an identifiable context'); -} - -function isIdentifiable(o: any): o is IdentifiableSyntaxElement { - return 'ambiguousIdentifier' in o; -} diff --git a/server/src/utils/vbaSyntaxParser.ts b/server/src/utils/vbaSyntaxParser.ts deleted file mode 100644 index 4ef5535..0000000 --- a/server/src/utils/vbaSyntaxParser.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { ANTLRInputStream, CommonTokenStream, ConsoleErrorListener, RecognitionException, Recognizer } from 'antlr4ts'; -import { TextDocument } from 'vscode-languageserver-textdocument'; -import { vbaListener } from '../antlr/out/vbaListener'; -import { AmbiguousIdentifierContext, AttributeStmtContext, CertainIdentifierContext, ConstStmtContext, EnumerationStmtContext, EnumerationStmt_ConstantContext, FunctionStmtContext, ImplicitCallStmt_InBlockContext, ImplicitCallStmt_InStmtContext, LetStmtContext, ModuleContext, ModuleHeaderContext, SetStmtContext, StartRuleContext, SubStmtContext, UnknownLineContext, VariableStmtContext, vbaParser } from '../antlr/out/vbaParser'; -import { vbaLexer as VbaLexer } from '../antlr/out/vbaLexer'; -import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker'; -import { ErrorNode } from 'antlr4ts/tree/ErrorNode'; -import { MethodElement, ModuleAttribute, ModuleElement, SyntaxElement, EnumElement, EnumConstant as EnumConstantElement, VariableStatementElement, VariableDeclarationElement, IdentifierElement } from './vbaSyntaxElements'; -import { SymbolKind } from 'vscode-languageserver'; - - -export interface ResultsContainer { - module?: ModuleElement; - addModule(element: ModuleElement): void; - addElement(element: SyntaxElement): void; - setModuleAttribute(attr: ModuleAttribute): void; - addScopedReference(emt: IdentifierElement): void; - addScopedDeclaration(emt: MethodElement | VariableDeclarationElement): void; - pushScopeElement(emt: MethodElement): void; - popScopeElement(): void; -} - - -export class SyntaxParser { - parse(doc: TextDocument, resultsContainer: ResultsContainer) { - const listener = new VbaTreeWalkListener(doc, resultsContainer); - const parserEntry = this.getParseEntryPoint(doc); - - ParseTreeWalker.DEFAULT.walk( - listener, - parserEntry - ); - } - - private getParseEntryPoint(doc: TextDocument): StartRuleContext { - const lexer = new VbaLexer(new ANTLRInputStream(doc.getText())); - const parser = new vbaParser(new CommonTokenStream(lexer)); - - parser.removeErrorListeners(); - parser.addErrorListener(new VbaErrorListener()); - return parser.startRule(); - } -} - -class VbaTreeWalkListener implements vbaListener { - doc: TextDocument; - resultsContainer: ResultsContainer; - - constructor(doc: TextDocument, resultsContainer: ResultsContainer) { - this.doc = doc; - this.resultsContainer = resultsContainer; - } - - visitErrorNode(node: ErrorNode) { - console.log(node.payload); - } - - enterUnknownLine = (ctx: UnknownLineContext) => { - console.log(ctx.text); - }; - - enterModule = (ctx: ModuleContext) => - this.resultsContainer.addModule( - new ModuleElement(ctx, this.doc)); - - // Only classes have a header. TODO: Test this for forms. - enterModuleHeader = (_: ModuleHeaderContext) => - this.resultsContainer.module!.symbolKind = SymbolKind.Class; - - enterAttributeStmt = (ctx: AttributeStmtContext) => { - const attr = new ModuleAttribute(ctx, this.doc); - this.resultsContainer.setModuleAttribute(attr); - if (attr.identifier?.text === 'VB_Name') { - const module = this.resultsContainer.module; - if (module) { - module.identifier = attr.literal; - } - } - }; - - enterSubStmt = (ctx: SubStmtContext) => - this.resultsContainer.addElement( - new MethodElement(ctx, this.doc)); - - exitSubStmt = (_: SubStmtContext) => - this.resultsContainer.popScopeElement(); - - enterFunctionStmt = (ctx: FunctionStmtContext) => { - const e = new MethodElement(ctx, this.doc); - this.resultsContainer.addScopedDeclaration(e); - }; - - exitFunctionStmt = (_: FunctionStmtContext) => - this.resultsContainer.popScopeElement(); - - enterEnumerationStmt = (ctx: EnumerationStmtContext) => - this.resultsContainer.addElement( - new EnumElement(ctx, this.doc)); - - enterEnumerationStmt_Constant = (ctx: EnumerationStmt_ConstantContext) => - this.resultsContainer.addElement( - new EnumConstantElement(ctx, this.doc)); - - enterVariableStmt = (ctx: VariableStmtContext) => this.enterVarOrConstStmt(ctx); - enterConstStmt = (ctx: ConstStmtContext) => this.enterVarOrConstStmt(ctx); - - private enterVarOrConstStmt(ctx: VariableStmtContext | ConstStmtContext) { - const declaration = new VariableStatementElement(ctx, this.doc); - this.resultsContainer.addElement(declaration); - declaration.variableList.forEach((v) => this.resultsContainer.addScopedDeclaration(v)); - } - - // enterImplicitCallStmt_InStmt = (ctx: ImplicitCallStmt_InStmtContext) => this.enterImplicitCallStmt(ctx); - // enterImplicitCallStmt_InBlock = (ctx: ImplicitCallStmt_InBlockContext) => this.enterImplicitCallStmt(ctx); - - // private enterImplicitCallStmt(ctx: ImplicitCallStmt_InStmtContext | ImplicitCallStmt_InBlockContext) { - // // console.log('imp call ' + ctx.text); - // } - - enterCertainIdentifier = (ctx: CertainIdentifierContext) => this.enterIdentifier(ctx); - enterAmbiguousIdentifier = (ctx: AmbiguousIdentifierContext) => this.enterIdentifier(ctx); - - private enterIdentifier(ctx: CertainIdentifierContext | AmbiguousIdentifierContext) { - const ident = new IdentifierElement(ctx, this.doc); - this.resultsContainer.addScopedReference(ident); - } - - // enterLetStmt = (ctx: LetStmtContext) => this.enterAssignStmt(ctx); - // enterSetStmt = (ctx: SetStmtContext) => this.enterAssignStmt(ctx); - - // private enterAssignStmt(ctx: LetStmtContext | SetStmtContext) { - // const assignment = new VariableAssignElement(ctx, this.doc); - // this.resultsContainer.addScopedReference(assignment); - // } -} - -class VbaErrorListener extends ConsoleErrorListener { - syntaxError(recognizer: Recognizer, offendingSymbol: T, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined): void { - super.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); - console.error(e); - if (e) { - const y = recognizer.getErrorHeader(e); - console.log(y); - } - recognizer.inputStream?.consume(); - } -} \ No newline at end of file From 68bba6914b4ba2d879ecadbf5004a024f9a95daa Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 11:20:32 +0800 Subject: [PATCH 12/29] Modified module layout --- server/src/antlr/vba.g4 | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/server/src/antlr/vba.g4 b/server/src/antlr/vba.g4 index e8ca61c..1f3ec80 100644 --- a/server/src/antlr/vba.g4 +++ b/server/src/antlr/vba.g4 @@ -18,10 +18,17 @@ grammar vba; startRule: module EOF; module: - WS? (endOfLine | unknownLine)* (moduleHeader endOfLine*)? moduleConfig? endOfLine* - moduleAttributes? endOfLine* moduleDeclarations? endOfLine* moduleBody? endOfLine* WS?; + WS? moduleHeader + moduleBody? endOfLine* WS?; -moduleHeader: VERSION WS DOUBLELITERAL WS CLASS; +moduleHeader: + (endOfLine | unknownLine)* + (moduleVerson endOfLine*)? + moduleConfig? endOfLine* + moduleAttributes? endOfLine* + moduleDeclarations? endOfLine*; + +moduleVerson: VERSION WS DOUBLELITERAL WS CLASS; moduleConfig: BEGIN endOfLine* moduleConfigElement+ END; @@ -44,14 +51,8 @@ moduleOption: moduleDeclarationsElement: comment | declareStmt - | enumerationStmt - | eventStmt - | constStmt - | implementsStmt // TODO : not valid in a module!!! - | variableStmt + | implementsStmt | moduleOption - | typeStmt - | macroStmt | unknownLine; macroStmt: macroConstStmt | macroIfThenElseStmt; @@ -60,9 +61,14 @@ moduleBody: moduleBodyElement (endOfLine+ moduleBodyElement)* endOfLine*; moduleBodyElement: - methodStmt + constStmt + | enumerationStmt + | eventStmt + | macroStmt + | methodStmt | propertyStmt - | macroStmt; + | typeStmt + | variableStmt; // block ---------------------------------- From aea85a5195fa37c93042b35765a2dbfc63c3ae97 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 11:21:41 +0800 Subject: [PATCH 13/29] Create parser --- server/src/project/document.ts | 193 +++++++++++++++++++ server/src/project/elements/memory.ts | 189 ++++++++++++++++++ server/src/project/elements/module.ts | 45 +++++ server/src/project/parser/vbaSyntaxParser.ts | 128 ++++++++++++ server/src/project/workspace.ts | 90 ++++++--- 5 files changed, 617 insertions(+), 28 deletions(-) create mode 100644 server/src/project/document.ts create mode 100644 server/src/project/elements/memory.ts create mode 100644 server/src/project/elements/module.ts create mode 100644 server/src/project/parser/vbaSyntaxParser.ts diff --git a/server/src/project/document.ts b/server/src/project/document.ts new file mode 100644 index 0000000..08b6377 --- /dev/null +++ b/server/src/project/document.ts @@ -0,0 +1,193 @@ +import { Diagnostic, SemanticTokens, SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { Workspace } from './workspace'; +import { FoldableElement } from './elements/special'; +import { BaseSyntaxElement, HasAttribute, HasSemanticToken, HasSymbolInformation } from './elements/base'; +import { Range, TextDocument } from 'vscode-languageserver-textdocument'; +import { SyntaxParser } from './parser/vbaSyntaxParser'; +import { FoldingRange } from '../capabilities/folding'; +import { SemanticTokensManager } from '../capabilities/semanticTokens'; +import { sleep } from '../utils/helpers'; + + +export interface ProjectDocument { + name: string; + textDocument: TextDocument; + languageServerSemanticTokens: (range?: Range) => SemanticTokens | null; + languageServerSymbolInformationAsync(): Promise; + get foldableElements(): FoldingRange[]; + parse(): void; +} + + +export abstract class BaseProjectDocument implements ProjectDocument { + readonly workspace: Workspace; + readonly textDocument: TextDocument; + readonly name: string; + + protected _unhandledNamedElements: [] = []; + protected _publicScopeDeclarations: Map = new Map(); + protected _documentScopeDeclarations: Map> = new Map(); + + protected _diagnostics: Diagnostic[] = []; + protected _elementParents: BaseSyntaxElement[] = []; + protected _attributeElements: HasAttribute[] = []; + protected _foldableElements: FoldingRange[] = []; + protected _symbolInformations: SymbolInformation[] = []; + protected _semanticTokens: SemanticTokensManager = new SemanticTokensManager(); + + isBusy = false; + abstract symbolKind: SymbolKind + + get foldableElements() { + return this._foldableElements; + } + + get activeAttributeElement() { + return this._attributeElements?.at(-1); + } + + constructor(workspace: Workspace, name: string, document: TextDocument) { + this.textDocument = document; + this.workspace = workspace; + this.name = name; + } + + static create(workspace: Workspace, document: TextDocument): VbaClassDocument | VbaModuleDocument { + const slashParts = document.uri.split('/').at(-1); + const dotParts = slashParts?.split('.'); + const extension = dotParts?.at(-1); + const filename = dotParts?.join('.'); + + if (!filename || !extension) { + throw new Error("Unable to parse document uri"); + } + + switch (extension) { + case 'cls': + return new VbaClassDocument(workspace, filename, document, SymbolKind.Class); + case 'bas': + return new VbaModuleDocument(workspace, filename, document, SymbolKind.Class); + default: + throw new Error("Expected *.cls or *.bas but got *." + extension); + } + } + + languageServerSemanticTokens = (range?: Range) => { + return this._semanticTokens.getSemanticTokens(range); + }; + + async languageServerSymbolInformationAsync() { + while (this.isBusy) { + await sleep(5); + } + return this._symbolInformations; + } + + parse = (): void => { + this.isBusy = true; + console.log('Parsing document'); + (new SyntaxParser()).parse(this); + console.log('Finished parsing document'); + this.isBusy = false; + }; + + registerNamedElementDeclaration(element: any) { + // Check workspace if public. + // Check for existing entry in local scope. + // Check for overriding name in method (if relevant) + // Check for overriding name in document scope. + // Check for overriding name in workspace. + throw new Error("Not implemented"); + } + + /** + * Pushes an element to the attribute elements stack. + * Be careful to pair a register action with an appropriate deregister. + * @param element the element to register. + * @returns nothing of interest. + */ + registerAttributeElement = (element: HasAttribute) => { + this._attributeElements.push(element); + return this; + }; + + /** + * Pops an element from the attribute elements stack. + * Popping allows actions to be performed on the same element, + * e.g., registered in the entry event and deregistered in the exit event. + * @param element the element to register. + * @returns the element at the end of the stack. + */ + deregisterAttributeElement = () => { + return this._attributeElements.pop(); + }; + + registerFoldableElement = (element: FoldableElement) => { + this._foldableElements.push(new FoldingRange(element)); + return this; + }; + + registerNamedElement(element: BaseSyntaxElement) { + return this; + } + + /** + * Registers an element as a parent to be attached to subsequent elemements. + * Should be called when the parser context is entered and matched with + * deregisterScopedElement when the context exits. + * @param element the element to register. + * @returns this for chaining. + */ + registerScopedElement(element: BaseSyntaxElement) { + this._elementParents.push(element); + return this; + } + + /** + * Deregisters an element as a parent so it isn't attached to subsequent elemements. + * Should be called when the parser context is exited and matched with + * deregisterScopedElement when the context is entered. + * @returns this for chaining. + */ + deregisterScopedElement = () => { + this._elementParents.pop(); + return this; + }; + + /** + * Registers a semantic token element for tracking with the SemanticTokenManager. + * @param element element The element that has a semantic token. + * @returns void. + */ + registerSemanticToken = (element: HasSemanticToken): void => { + this._semanticTokens.add(element); + }; + + /** + * Registers a SymbolInformation. + * @param element The element that has symbol information. + * @returns a number for some reason. + */ + registerSymbolInformation = (element: HasSymbolInformation): number => { + console.debug(`Registering symbol for ${element.symbolInformation.name}`); + return this._symbolInformations.push(element.symbolInformation); + }; +} + + +export class VbaClassDocument extends BaseProjectDocument { + symbolKind: SymbolKind; + constructor(workspace: Workspace, name: string, document: TextDocument, symbolKind: SymbolKind) { + super(workspace, name, document); + this.symbolKind = symbolKind; + } +} + + +export class VbaModuleDocument extends BaseProjectDocument { + symbolKind: SymbolKind; + constructor(workspace: Workspace, name: string, document: TextDocument, symbolKind: SymbolKind) { + super(workspace, name, document); + this.symbolKind = symbolKind; + } +} diff --git a/server/src/project/elements/memory.ts b/server/src/project/elements/memory.ts new file mode 100644 index 0000000..2d07aa1 --- /dev/null +++ b/server/src/project/elements/memory.ts @@ -0,0 +1,189 @@ +import { AmbiguousIdentifierContext, AsTypeClauseContext, ConstStmtContext, ConstSubStmtContext, EnumerationStmtContext, EnumerationStmt_ConstantContext, MethodStmtContext, VariableStmtContext, VariableSubStmtContext } from '../../antlr/out/vbaParser'; + +import { TextDocument } from 'vscode-languageserver-textdocument'; + +import { BaseContextSyntaxElement, BaseSyntaxElement, HasSemanticToken, HasSymbolInformation } from './base'; +import { SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { FoldableElement } from './special'; +import { SymbolInformationFactory } from '../../capabilities/symbolInformation'; +import '../../extensions/parserExtensions'; + + +export class IdentifierElement extends BaseContextSyntaxElement { + constructor(ctx: AmbiguousIdentifierContext, doc: TextDocument) { + super(ctx, doc); + } +} + + +abstract class BaseEnumElement extends FoldableElement implements HasSemanticToken, HasSymbolInformation { + identifier: IdentifierElement; + tokenModifiers: SemanticTokenModifiers[] = []; + abstract tokenType: SemanticTokenTypes; + abstract symbolKind: SymbolKind; + + constructor(context: EnumerationStmtContext | EnumerationStmt_ConstantContext, document: TextDocument) { + super(context, document); + this.identifier = new IdentifierElement(context.ambiguousIdentifier(), document); + } + + get name(): string { return this.identifier.text; } + get symbolInformation(): SymbolInformation { + return SymbolInformationFactory.create( + this, this.symbolKind + ); + } + +} + + +export class EnumBlockDeclarationElement extends BaseEnumElement { + tokenType: SemanticTokenTypes; + tokenModifiers: SemanticTokenModifiers[] = []; + symbolKind: SymbolKind; + + constructor(context: EnumerationStmtContext, document: TextDocument) { + super(context, document); + this.tokenType = SemanticTokenTypes.enum; + this.symbolKind = SymbolKind.Enum; + } +} + + +export class EnumMemberDeclarationElement extends BaseEnumElement { + tokenType: SemanticTokenTypes; + tokenModifiers: SemanticTokenModifiers[] = []; + symbolKind: SymbolKind; + + constructor(context: EnumerationStmt_ConstantContext, document: TextDocument) { + super(context, document); + this.tokenType = SemanticTokenTypes.enumMember; + this.symbolKind = SymbolKind.EnumMember; + } +} + +abstract class BaseMethodElement extends FoldableElement implements HasSemanticToken, HasSymbolInformation { + identifier: IdentifierElement; + tokenModifiers: SemanticTokenModifiers[] = []; + abstract tokenType: SemanticTokenTypes; + abstract symbolKind: SymbolKind; + + constructor(context: MethodStmtContext, document: TextDocument) { + super(context, document); + this.identifier = new IdentifierElement(context.methodSignatureStmt().ambiguousIdentifier(), document); + } + + get name(): string { return this.identifier.text; } + get symbolInformation(): SymbolInformation { + return SymbolInformationFactory.create( + this, this.symbolKind + ); + } +} + +export class MethodBlockDeclarationElement extends BaseMethodElement { + tokenType: SemanticTokenTypes; + tokenModifiers: SemanticTokenModifiers[] = []; + symbolKind: SymbolKind; + + constructor(context: MethodStmtContext, document: TextDocument) { + super(context, document); + this.tokenType = SemanticTokenTypes.method; + this.symbolKind = SymbolKind.Method; + } +} + +abstract class BaseVariableDeclarationStatementElement extends BaseContextSyntaxElement { + abstract declarations: VariableDeclarationElement[]; + + constructor(context: ConstStmtContext | VariableStmtContext, document: TextDocument) { + super(context, document); + } +} + +export class ConstDeclarationsElement extends BaseVariableDeclarationStatementElement { + declarations: VariableDeclarationElement[] = []; + + constructor(context: ConstStmtContext, document: TextDocument) { + super(context, document); + context.constSubStmt().forEach((element) => + this.declarations.push(new VariableDeclarationElement( + element, document + )) + ); + } +} + +export class VariableDeclarationsElement extends BaseVariableDeclarationStatementElement { + declarations: VariableDeclarationElement[] = []; + + constructor(context: VariableStmtContext, document: TextDocument) { + super(context, document); + context.variableListStmt().variableSubStmt().forEach((element) => + this.declarations.push(new VariableDeclarationElement( + element, document + )) + ); + } +} + +class VariableDeclarationElement extends BaseContextSyntaxElement implements HasSymbolInformation { + identifier: IdentifierElement; + asType: VariableType; + arrayBounds?: ArrayBounds; + + constructor(context: ConstSubStmtContext | VariableSubStmtContext, document: TextDocument) { + super(context, document); + this.asType = new VariableType(context.asTypeClause(), document); + this.arrayBounds = ArrayBounds.create(context); + this.identifier = new IdentifierElement(context.ambiguousIdentifier(), document); + } + + get name(): string { return this.identifier.text; } + get symbolInformation(): SymbolInformation { + return SymbolInformationFactory.create( + this, this.asType.symbolKind + ); + } +} + +class VariableType extends BaseSyntaxElement { + typeName: string; + symbolKind: SymbolKind; + + constructor(context: AsTypeClauseContext | undefined, document: TextDocument, isArray?: boolean) { + super(context, document); + this.symbolKind = isArray ? SymbolKind.Array : SymbolKind.Variable; + + // Needs more ternery. + const type = context?.type_()?.baseType() ?? context?.type_()?.complexType(); + this.typeName = type?.text ?? type?.text ?? 'Variant'; + this.symbolKind = type ? type.toSymbolKind() : SymbolKind.Variable; + } +} + +class ArrayBounds { + dimensions: { lower: number, upper: number }[] = []; + + constructor(subStmt: VariableSubStmtContext) { + subStmt.subscripts()?.subscript_().forEach((x) => { + const vals = x.valueStmt(); + this.dimensions.push({ + lower: vals.length === 1 ? 0 : +vals[0].text, + upper: vals.length === 1 ? +vals[0].text : +vals[1].text + }); + }); + } + + /** + * Creates an ArrayBounds if the context is a variable and an array. + * @param subStmt a subStmt context for a variable or a constant. + * @returns A new array bounds if the context is an array variable. + */ + static create(subStmt: VariableSubStmtContext | ConstSubStmtContext) { + const hasLparenMethod = (x: any): x is VariableSubStmtContext => 'LPAREN' in x; + if (hasLparenMethod(subStmt) && subStmt.LPAREN()) { + return new ArrayBounds(subStmt); + } + } +} \ No newline at end of file diff --git a/server/src/project/elements/module.ts b/server/src/project/elements/module.ts new file mode 100644 index 0000000..4b97af0 --- /dev/null +++ b/server/src/project/elements/module.ts @@ -0,0 +1,45 @@ +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { AttributeStmtContext, ModuleContext } from '../../antlr/out/vbaParser'; + +import { BaseContextSyntaxElement, HasAttribute, HasSymbolInformation } from './base'; +import { SymbolInformationFactory } from '../../capabilities/symbolInformation'; + + +export class ModuleElement extends BaseContextSyntaxElement implements HasSymbolInformation, HasAttribute { + private _hasName = false; + private _name: string; + symbolKind: SymbolKind; + + constructor(context: ModuleContext, document: TextDocument, symbolKind: SymbolKind) { + super(context, document); + this._name = "Unknown Module"; + this.symbolKind = symbolKind; + } + + get name(): string { + return this._name; + } + + get symbolInformation(): SymbolInformation { + console.warn(`Creating symbol with name ${this._name}`); + return SymbolInformationFactory.create( + this, this.symbolKind + ); + } + + processAttribute(context: AttributeStmtContext): void { + if (this._hasName) { + return; + } + + const text = context.text; + if (text.startsWith("Attribute VB_Name = ")) { + const unquote = (x: string): string => + x.replace(/^"+|"+$/g, ''); + + this._name = unquote(text.split("= ")[1]); + this._hasName = true; + } + } +} diff --git a/server/src/project/parser/vbaSyntaxParser.ts b/server/src/project/parser/vbaSyntaxParser.ts new file mode 100644 index 0000000..c28c466 --- /dev/null +++ b/server/src/project/parser/vbaSyntaxParser.ts @@ -0,0 +1,128 @@ +import { TextDocument } from 'vscode-languageserver-textdocument'; + +import { ANTLRInputStream, CommonTokenStream, ConsoleErrorListener, RecognitionException, Recognizer } from 'antlr4ts'; + +import { ErrorNode } from 'antlr4ts/tree/ErrorNode'; +import { ParseTreeWalker } from 'antlr4ts/tree/ParseTreeWalker'; + +import { vbaLexer as VbaLexer } from '../../antlr/out/vbaLexer'; +import { AttributeStmtContext, ConstStmtContext, EnumerationStmtContext, EnumerationStmt_ConstantContext, FoldingBlockStmtContext, MethodStmtContext, ModuleContext, ModuleHeaderContext, VariableStmtContext, vbaParser as VbaParser } from '../../antlr/out/vbaParser'; +import { vbaListener } from '../../antlr/out/vbaListener'; + +import { VbaClassDocument, VbaModuleDocument } from '../document'; +import { FoldableElement } from '../elements/special'; +import { ConstDeclarationsElement, EnumBlockDeclarationElement, EnumMemberDeclarationElement, MethodBlockDeclarationElement, VariableDeclarationsElement } from '../elements/memory'; +import { ModuleElement } from '../elements/module'; + +export class SyntaxParser { + parse(document: VbaClassDocument | VbaModuleDocument) { + const listener = new VbaTreeWalkListener(document); + const parser = this.createParser(document.textDocument); + + ParseTreeWalker.DEFAULT.walk( + listener, + parser.startRule() + ); + } + + private createParser(doc: TextDocument): VbaParser { + const lexer = new VbaLexer(new ANTLRInputStream(doc.getText())); + const parser = new VbaParser(new CommonTokenStream(lexer)); + + parser.removeErrorListeners(); + parser.addErrorListener(new VbaErrorListener()); + return parser; + } +} + + +class VbaTreeWalkListener implements vbaListener { + document: VbaClassDocument | VbaModuleDocument; + + constructor(document: VbaClassDocument | VbaModuleDocument) { + this.document = document; + } + + visitErrorNode(node: ErrorNode) { + console.log(node.payload); + } + + enterAttributeStmt = (ctx: AttributeStmtContext) => { + this.document.activeAttributeElement?.processAttribute(ctx); + }; + + enterConstStmt = (ctx: ConstStmtContext) => { + const element = new ConstDeclarationsElement(ctx, this.document.textDocument); + element.declarations.forEach((e) => this.document.registerSymbolInformation(e)); + }; + + enterEnumerationStmt = (ctx: EnumerationStmtContext) => { + const element = new EnumBlockDeclarationElement(ctx, this.document.textDocument); + this.document.registerFoldableElement(element); + this.document.registerSemanticToken(element); + this.document.registerSymbolInformation(element); + this.document.registerScopedElement(element); + }; + + exitEnumerationStmt = (_: EnumerationStmtContext) => { + this.document.deregisterScopedElement(); + }; + + enterEnumerationStmt_Constant = (ctx: EnumerationStmt_ConstantContext) => { + const element = new EnumMemberDeclarationElement(ctx, this.document.textDocument); + this.document.registerSymbolInformation(element); + this.document.registerSemanticToken(element); + }; + + enterFoldingBlockStmt = (ctx: FoldingBlockStmtContext) => { + const element = new FoldableElement(ctx, this.document.textDocument); + this.document.registerFoldableElement(element); + }; + + enterMethodStmt = (ctx: MethodStmtContext) => { + const element = new MethodBlockDeclarationElement(ctx, this.document.textDocument); + this.document.registerNamedElement(element); + this.document.registerFoldableElement(element); + this.document.registerSymbolInformation(element); + this.document.registerScopedElement(element); + }; + + exitMethodStmt = (_: MethodStmtContext) => { + this.document.deregisterScopedElement(); + }; + + enterModule = (ctx: ModuleContext) => { + const element = new ModuleElement(ctx, this.document.textDocument, this.document.symbolKind); + this.document.registerAttributeElement(element); + this.document.registerScopedElement(element); + }; + + exitModule = (_: ModuleContext) => { + const element = this.document.deregisterAttributeElement() as ModuleElement; + this.document.registerSymbolInformation(element); + this.document.deregisterScopedElement(); + this.document.deregisterAttributeElement(); + }; + + enterModuleHeader = (ctx: ModuleHeaderContext) => { + const element = new FoldableElement(ctx, this.document.textDocument); + this.document.registerFoldableElement(element); + }; + + enterVariableStmt = (ctx: VariableStmtContext) => { + const element = new VariableDeclarationsElement(ctx, this.document.textDocument); + element.declarations.forEach((e) => this.document.registerSymbolInformation(e)); + }; +} + +class VbaErrorListener extends ConsoleErrorListener { + syntaxError(recognizer: Recognizer, offendingSymbol: T, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined): void { + super.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); + console.error(e); + if (e) { + const y = recognizer.getErrorHeader(e); + console.log(y); + } + recognizer.inputStream?.consume(); + } +} diff --git a/server/src/project/workspace.ts b/server/src/project/workspace.ts index fec2a39..52a2931 100644 --- a/server/src/project/workspace.ts +++ b/server/src/project/workspace.ts @@ -1,8 +1,8 @@ -import { CompletionItem, CompletionParams, DidChangeConfigurationNotification, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbolParams, FoldingRange, FoldingRangeParams, Hover, HoverParams, SymbolInformation, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver'; -import { IProjectDocument } from './document'; +import { CompletionItem, CompletionParams, DidChangeConfigurationNotification, DidChangeConfigurationParams, DidChangeWatchedFilesParams, DocumentSymbolParams, FoldingRange, FoldingRangeParams, Hover, HoverParams, SemanticTokensParams, SemanticTokensRangeParams, SymbolInformation, TextDocuments, WorkspaceFoldersChangeEvent, _Connection } from 'vscode-languageserver'; +import { BaseProjectDocument, ProjectDocument } from './document'; import { LanguageServerConfiguration } from '../server'; -import { sleep } from '../utils/helpers'; import { hasConfigurationCapability } from '../capabilities/workspaceFolder'; +import { TextDocument } from 'vscode-languageserver-textdocument'; /** @@ -10,16 +10,25 @@ import { hasConfigurationCapability } from '../capabilities/workspaceFolder'; * at a workspace level. */ export class Workspace { - private events: WorkspaceEvents; - private documents: IProjectDocument[] = []; - private publicScopeDeclarations: Map = new Map(); + private _events: WorkspaceEvents; + private _documents: ProjectDocument[] = []; + private _activeDocument?: ProjectDocument; + private _publicScopeDeclarations: Map = new Map(); readonly connection: _Connection; + activateDocument(document: ProjectDocument) { + this._activeDocument = document; + } + + get activeDocument() { + return this._activeDocument; + } + // constructor(connection: _Connection, capabilities: LanguageServerCapabilities) { constructor(params: {connection: _Connection, capabilities: LanguageServerConfiguration}) { this.connection = params.connection; - this.events = new WorkspaceEvents({ + this._events = new WorkspaceEvents({ workspace: this, connection: params.connection, configuration: params.capabilities, @@ -47,16 +56,23 @@ export class Workspace { } class WorkspaceEvents { - readonly workspace: Workspace; - readonly configuration: LanguageServerConfiguration; + private readonly _workspace: Workspace; + private readonly _documents: TextDocuments; + private readonly _configuration: LanguageServerConfiguration; + + activeDocument?: ProjectDocument; constructor(params: {connection: _Connection, workspace: Workspace, configuration: LanguageServerConfiguration}) { - this.workspace = params.workspace; - this.configuration = params.configuration; + this._workspace = params.workspace; + this._configuration = params.configuration; + this._documents = new TextDocuments(TextDocument); this.initialiseConnectionEvents(params.connection); + this.initialiseDocumentsEvents(); + this._documents.listen(params.connection); } private initialiseConnectionEvents(connection: _Connection) { + console.log('Initialising connection events...'); connection.onInitialized(() => this.onInitialized()); connection.onCompletion(params => this.onCompletion(params)); connection.onCompletionResolve(item => this.onCompletionResolve(item)); @@ -65,50 +81,54 @@ class WorkspaceEvents { connection.onDocumentSymbol((params) => this.onDocumentSymbolAsync(params)); connection.onHover(params => this.onHover(params)); - if (hasConfigurationCapability(this.configuration)) { + if (hasConfigurationCapability(this._configuration)) { connection.onFoldingRanges((params) => this.onFoldingRanges(params)); } connection.onRequest((method: string, params: object | object[] | any) => { switch (method) { case 'textDocument/semanticTokens/full': { - // const stp = params as SemanticTokensParams; - // return docInfo.getSemanticTokens(stp); - break; + return this.activeDocument?.languageServerSemanticTokens(); + } + case 'textDocument/semanticTokens/range': { + const rangeParams = params as SemanticTokensRangeParams; + return this.activeDocument?.languageServerSemanticTokens(rangeParams.range); } - case 'textDocument/semanticTokens/range': - // return docInfo.getSemanticTokens(params); - break; default: - // console.error(`Unresolved request path: ${method}`); + console.error(`Unresolved request path: ${method}`); } }); } + private initialiseDocumentsEvents() { + console.log('Initialising documents events...'); + this._documents.onDidChangeContent(e => this.onDidChangeContent(e.document)); + } + /** Connection event handlers */ private onCompletion(params: CompletionParams): never[] { - console.log('onCompletion: ' + params); + console.log(`onCompletion: ${params}`); return []; } private onCompletionResolve(item: CompletionItem): CompletionItem { - console.log('onCompletionResolve: ' + item.label); + console.log(`onCompletionResolve: ${item.label}`); return item; } private onDidChangeConfiguration(params: DidChangeConfigurationParams): void { - console.log('onDidChangeConfiguration: ' + params); + console.log(`onDidChangeConfiguration: ${params}`); } private onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) { - console.log('onDidChangeWatchedFiles: ' + params); + console.log(`onDidChangeWatchedFiles: ${params}`); } // TODO: Should trigger a full workspace refresh. private onDidChangeWorkspaceFolders(e: WorkspaceFoldersChangeEvent) { - console.log('onDidChangeWorkspaceFolders: ' + e); - this.workspace.connection.console.log('Workspace folder change event received.\n' + e); + console.log(`onDidChangeWorkspaceFolders: ${e}`); + this._workspace.connection.console.log(`Workspace folder change event received.\n${e}`); } private async onDocumentSymbolAsync(params: DocumentSymbolParams): Promise { @@ -123,21 +143,35 @@ class WorkspaceEvents { } private onHover(params: HoverParams): Hover { - console.log('onHover: ' + params.position.line + ',' + params.position.character); + console.log(`onHover: ${params.position.line},${params.position.character}`); return { contents: '' }; } private onInitialized(): void { console.log('onInitialized:---'); - const connection = this.workspace.connection; + const connection = this._workspace.connection; // Register for client configuration notification changes. connection.client.register(DidChangeConfigurationNotification.type, undefined); // This is how we can listen for changes to workspace folders. - if (hasConfigurationCapability(this.configuration)) { + if (hasConfigurationCapability(this._configuration)) { connection.workspace.onDidChangeWorkspaceFolders(e => this.onDidChangeWorkspaceFolders(e) ); } } + + /** Documents event handlers */ + + /** + * This event handler is called whenever a `TextDocuments` is changed. + * @param doc The document that changed. + */ + onDidChangeContent(doc: TextDocument) { + console.log('onDidChangeContent:--- ' + doc.uri); + this.activeDocument = BaseProjectDocument.create(this._workspace, doc); + this.activeDocument.parse(); + this._workspace.activateDocument(this.activeDocument); + } + } \ No newline at end of file From 6c1234335eb3dc4e1974b823fdaedace1352a877 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 11:21:47 +0800 Subject: [PATCH 14/29] Update package --- .vscode/settings.json | 6 +++++- client/package-lock.json | 4 ++-- client/package.json | 2 +- package-lock.json | 10 +++++----- package.json | 14 ++++++++------ server/package-lock.json | 4 ++-- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 21dc18c..e436cf3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,9 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "vbaLanguageServer.trace.server": "verbose" + "vbaLanguageServer.trace.server": "verbose", + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/node_modules/**": true + } } \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 2523118..3fca6ec 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,11 +1,11 @@ { - "name": "lsp-sample-client", + "name": "vba-lsp-client", "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "lsp-sample-client", + "name": "vba-lsp-client", "version": "0.0.1", "license": "MIT", "dependencies": { diff --git a/client/package.json b/client/package.json index c03dfdd..7215d19 100644 --- a/client/package.json +++ b/client/package.json @@ -7,7 +7,7 @@ "publisher": "SSlinky", "repository": { "type": "git", - "url": "https://github.com/SSlinky/tbc" + "url": "https://github.com/SSlinky/VB-LanguageServer" }, "engines": { "vscode": "^1.63.0" diff --git a/package-lock.json b/package-lock.json index 7fe0e56..bf06435 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,12 @@ "version": "1.0.0", "hasInstallScript": true, "license": "MIT", - "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" - }, "devDependencies": { "@types/mocha": "^9.1.0", "@types/node": "^16.11.7", "@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/parser": "^5.30.0", + "antlr4ts": "^0.5.0-alpha.4", "antlr4ts-cli": "^0.5.0-alpha.4", "eslint": "^8.13.0", "js-yaml": "^4.1.0", @@ -451,7 +449,8 @@ "node_modules/antlr4ts": { "version": "0.5.0-alpha.4", "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true }, "node_modules/antlr4ts-cli": { "version": "0.5.0-alpha.4", @@ -2411,7 +2410,8 @@ "antlr4ts": { "version": "0.5.0-alpha.4", "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true }, "antlr4ts-cli": { "version": "0.5.0-alpha.4", diff --git a/package.json b/package.json index 467d879..1e59cec 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "version": "1.0.0", "repository": { "type": "git", - "url": "https://github.com/SSlinky/TBC" + "url": "https://github.com/SSlinky/VBA-LanguageServer" }, "publisher": "SSlinky", "categories": [ @@ -27,8 +27,7 @@ { "id": "vba", "aliases": [ - "VBA", - "vba" + "VBA" ], "extensions": [ ".bas", @@ -38,6 +37,11 @@ "configuration": "./vba.language-configuration.json" } ], + "configurationDefaults": { + "[vba]": { + "editor.semanticHighlighting.enabled": true + } + }, "configuration": { "type": "object", "title": "Default configuration", @@ -90,13 +94,11 @@ "@types/node": "^16.11.7", "@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/parser": "^5.30.0", + "antlr4ts": "^0.5.0-alpha.4", "antlr4ts-cli": "^0.5.0-alpha.4", "eslint": "^8.13.0", "js-yaml": "^4.1.0", "mocha": "^9.2.1", "typescript": "^4.7.2" - }, - "dependencies": { - "antlr4ts": "^0.5.0-alpha.4" } } diff --git a/server/package-lock.json b/server/package-lock.json index b8585d2..c8256ad 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,11 +1,11 @@ { - "name": "lsp-sample-server", + "name": "vba-lsp-server", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "lsp-sample-server", + "name": "vba-lsp-server", "version": "1.0.0", "license": "MIT", "dependencies": { From 538fbc6c8cf0a8f47420d59dadc6b743c04a5ec6 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 12:41:51 +0800 Subject: [PATCH 15/29] Required for vsce publish --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1e59cec..7fe2d20 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "vscode": "^1.63.0" }, "main": "./client/out/extension", + "activationEvents": [], "contributes": { "languages": [ { From 2b7df82c2f920d39be19dc1523cb0368acbf54fe Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 12:42:14 +0800 Subject: [PATCH 16/29] Compiler issue with extending ParserRuleContext --- server/src/extensions/parserExtensions.ts | 35 +++++++++++------------ server/src/project/elements/base.ts | 19 ++++++++++-- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/server/src/extensions/parserExtensions.ts b/server/src/extensions/parserExtensions.ts index e79ed10..6b4e305 100644 --- a/server/src/extensions/parserExtensions.ts +++ b/server/src/extensions/parserExtensions.ts @@ -1,23 +1,24 @@ import { Range, SymbolKind } from 'vscode-languageserver'; import { BaseTypeContext, ComplexTypeContext } from '../antlr/out/vbaParser'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { ParserRuleContext } from 'antlr4ts'; +// import { ParserRuleContext } from 'antlr4ts'; +// This extension throws a compiler error TS2693: 'ParserRuleContext' only refers to a type, but is being used as a value here. +// Can maybe review later down the track, but for now have just made it a private method on BaseSyntaxElement. +// declare module 'antlr4ts' { +// export interface ParserRuleContext { +// toRange(document: TextDocument): Range; +// } +// } -declare module 'antlr4ts' { - export interface ParserRuleContext { - toRange(document: TextDocument): Range; - } -} - -ParserRuleContext.prototype.toRange = function (document: TextDocument): Range { - const startIndex = this.start.startIndex; - const stopIndex = this.stop?.stopIndex ?? startIndex; - return Range.create( - document.positionAt(startIndex), - document.positionAt(stopIndex) - ); -}; +// ParserRuleContext.prototype.toRange = function (document: TextDocument): Range { +// const startIndex = this.start.startIndex; +// const stopIndex = this.stop?.stopIndex ?? startIndex; +// return Range.create( +// document.positionAt(startIndex), +// document.positionAt(stopIndex) +// ); +// }; declare module '../antlr/out/vbaParser' { @@ -28,10 +29,6 @@ declare module '../antlr/out/vbaParser' { export interface ComplexTypeContext { toSymbolKind(): SymbolKind; } - - export interface ParserRuleContext { - toRange(document: TextDocument): Range; - } } BaseTypeContext.prototype.toSymbolKind = function (): SymbolKind { diff --git a/server/src/project/elements/base.ts b/server/src/project/elements/base.ts index b2760d1..3a3cdd3 100644 --- a/server/src/project/elements/base.ts +++ b/server/src/project/elements/base.ts @@ -1,6 +1,6 @@ import { ParserRuleContext } from 'antlr4ts'; -import { SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; -import { Position, Range, TextDocument } from 'vscode-languageserver-textdocument'; +import { Range, SemanticTokenModifiers, SemanticTokenTypes, SymbolInformation, SymbolKind } from 'vscode-languageserver'; +import { Position, TextDocument } from 'vscode-languageserver-textdocument'; import { FoldingRangeKind } from '../../capabilities/folding'; import { IdentifierElement } from './memory'; import { AttributeStmtContext } from '../../antlr/out/vbaParser'; @@ -66,7 +66,7 @@ export abstract class BaseSyntaxElement implements ContextOptionalSyntaxElement constructor(context: ParserRuleContext | undefined, document: TextDocument) { this.context = context; this.document = document; - this.range = context ? context.toRange(document) : undefined; + this.range = this._contextToRange(); } isChildOf = (range: Range): boolean => { @@ -80,6 +80,19 @@ export abstract class BaseSyntaxElement implements ContextOptionalSyntaxElement return isPositionBefore(range.start, this.range.start) && isPositionBefore(this.range.end, range.end); }; + + private _contextToRange(): Range | undefined { + if (!this.context) { + return; + } + + const startIndex = this.context.start.startIndex; + const stopIndex = this.context.stop?.stopIndex ?? startIndex; + return Range.create( + this.document.positionAt(startIndex), + this.document.positionAt(stopIndex) + ); + } } export abstract class BaseContextSyntaxElement extends BaseSyntaxElement { From 8e12e5f994e66e6ac619fac1a89080979b1725a6 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 12:43:11 +0800 Subject: [PATCH 17/29] 0.1.0-Alpha --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf06435..e1b35e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "1.0.0", + "version": "0.1.0-Alpha", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.0.0", + "version": "0.1.0-Alpha", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index 7fe2d20..64e0e84 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "A language server for VBA", "author": "SSlinky", "license": "MIT", - "version": "1.0.0", + "version": "0.1.0-Alpha", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" From 363c8bffb87a919b61d30da5fda9ae14211fc6fd Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 12:52:46 +0800 Subject: [PATCH 18/29] Fixed .vscodeignore --- .vscodeignore | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.vscodeignore b/.vscodeignore index 382ed7c..204f508 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,11 +1,23 @@ -.vscode/** -**/*.ts -**/*.map +# Project .gitignore +contributing.md +.vscode/** **/tsconfig.json **/tsconfig.base.json -contributing.md .travis.yml + +# Typescript +**/*.ts +**/*.map + +# Generated +.antlr + +# Samples and testing +.vscode-test +sample*/** + +# Client node modules client/node_modules/** !client/node_modules/vscode-jsonrpc/** !client/node_modules/vscode-languageclient/** From aa3acb8e16580bd46cbc3a92064c2e2288b1db55 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 12:52:52 +0800 Subject: [PATCH 19/29] 1.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1b35e1..bf06435 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "0.1.0-Alpha", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "0.1.0-Alpha", + "version": "1.0.0", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index 64e0e84..7fe2d20 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "A language server for VBA", "author": "SSlinky", "license": "MIT", - "version": "0.1.0-Alpha", + "version": "1.0.0", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" From 235134502d78ab1f1c0eb1d0e3f787e394765846 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 13:02:25 +0800 Subject: [PATCH 20/29] Fix publisher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fe2d20..5f077ea 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" }, - "publisher": "SSlinky", + "publisher": "NotisDataAnalytics", "categories": [ "Programming Languages", "Snippets", From fdf4709d0e52394a5c462be46052d90bea066afc Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 14:59:48 +0800 Subject: [PATCH 21/29] Ignore packages --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 06f39f7..02a2f8b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ out node_modules client/server .vscode-test -sample*/** \ No newline at end of file +sample*/** +*.vsix \ No newline at end of file From 76b7717a27f997f50e1c902c5a5dfcc3b49cbe7d Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:00:15 +0800 Subject: [PATCH 22/29] correct antlr4ts dependency --- package-lock.json | 3 ++- package.json | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf06435..05aca7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -450,7 +450,8 @@ "version": "0.5.0-alpha.4", "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/antlr4ts-cli": { "version": "0.5.0-alpha.4", diff --git a/package.json b/package.json index 5f077ea..a5f94c0 100644 --- a/package.json +++ b/package.json @@ -95,11 +95,13 @@ "@types/node": "^16.11.7", "@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/parser": "^5.30.0", - "antlr4ts": "^0.5.0-alpha.4", "antlr4ts-cli": "^0.5.0-alpha.4", "eslint": "^8.13.0", "js-yaml": "^4.1.0", "mocha": "^9.2.1", "typescript": "^4.7.2" + }, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" } } From 8dc05bea6e387caa30c7a6d90f1b04fba9810b2e Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:00:23 +0800 Subject: [PATCH 23/29] Update .vscodeignore --- .vscodeignore | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.vscodeignore b/.vscodeignore index 204f508..65c609c 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,21 +1,30 @@ # Project .gitignore -contributing.md +.eslintignore .vscode/** +.travis.yml **/tsconfig.json **/tsconfig.base.json -.travis.yml +contributing.md + +# Development +**/src -# Typescript +# # Typescript **/*.ts **/*.map # Generated .antlr +*.java +*.interp +*.tokens +*.vsix # Samples and testing -.vscode-test sample*/** +**/test/** +**/testFixture/** # Client node modules client/node_modules/** @@ -24,4 +33,4 @@ client/node_modules/** !client/node_modules/vscode-languageserver-protocol/** !client/node_modules/vscode-languageserver-types/** !client/node_modules/{minimatch,brace-expansion,concat-map,balanced-match}/** -!client/node_modules/{semver,lru-cache,yallist}/** \ No newline at end of file +!client/node_modules/{semver,lru-cache,yallist}/** From b660189eddb0ea2a4fa555698692ca4cd2373436 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:01:31 +0800 Subject: [PATCH 24/29] 1.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05aca7b..17fde37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vba-lsp", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vba-lsp", - "version": "1.0.0", + "version": "1.1.0", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index a5f94c0..73dac14 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "A language server for VBA", "author": "SSlinky", "license": "MIT", - "version": "1.0.0", + "version": "1.1.0", "repository": { "type": "git", "url": "https://github.com/SSlinky/VBA-LanguageServer" From 329870dde937b8b34044b34583f5285c1cbb8053 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:39:07 +0800 Subject: [PATCH 25/29] Prepare for publish --- README.md | 11 ++++++++++- images/vba-lsp.png | Bin 0 -> 204055 bytes package.json | 8 ++++---- 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 images/vba-lsp.png diff --git a/README.md b/README.md index 2103fa7..172dfc2 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,25 @@ Adds VBA language support to VSCode via LSP compliant Language Server. +![VBA LSP](images/vba-lsp.png) + ## Features + * Syntax highlighting (resolved on client) * Semantic highlighting * Folding ranges * Document symbols -* Diagnostics +## Coming Soon + +* Diagnostics (info, warnings, and errors) ## Installation +* Through the VBA Marketplace, +* VSCode command palette `ext install sslinky.vba-lsp`, or; +* Download the [visx](/releases/latest). + ## Contributing Contributors welcome! Please see [contributing.md](tbc). \ No newline at end of file diff --git a/images/vba-lsp.png b/images/vba-lsp.png new file mode 100644 index 0000000000000000000000000000000000000000..ff1f5e6f6ae24ebd611f40691ebba4d554f2d445 GIT binary patch literal 204055 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 Date: Wed, 22 May 2024 15:41:02 +0800 Subject: [PATCH 26/29] Update contributing link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 172dfc2..5ea2e9b 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,4 @@ Adds VBA language support to VSCode via LSP compliant Language Server. ## Contributing -Contributors welcome! Please see [contributing.md](tbc). \ No newline at end of file +Contributors welcome! Please see [contributing.md]. \ No newline at end of file From cb55b6392e40fa5ff1f6818f0f0fdd60dd806907 Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:41:34 +0800 Subject: [PATCH 27/29] fix contributing link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ea2e9b..44ce6cf 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,4 @@ Adds VBA language support to VSCode via LSP compliant Language Server. ## Contributing -Contributors welcome! Please see [contributing.md]. \ No newline at end of file +Contributors welcome! Please see [contributing.md](/contributing.md). \ No newline at end of file From c58ed8af7573459ec9e75c2015efc63374820d6e Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:53:30 +0800 Subject: [PATCH 28/29] added AI generated icon --- images/vba-lsp-icon.png | Bin 0 -> 15650 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/vba-lsp-icon.png diff --git a/images/vba-lsp-icon.png b/images/vba-lsp-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e9ddeda99ef9178a5d04083d178d2d503a23d582 GIT binary patch literal 15650 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

BNz=_iY+?iu)$zu+ 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 m{zd*?`M)iuB0x{(xoSNmu!fq~9SgPnK literal 0 HcmV?d00001 From 396c42ee190a248bc2fe10727f54a2a1d01800bd Mon Sep 17 00:00:00 2001 From: sslinky Date: Wed, 22 May 2024 15:53:44 +0800 Subject: [PATCH 29/29] Udpated readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 44ce6cf..2f17abe 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Adds VBA language support to VSCode via LSP compliant Language Server. ## Coming Soon +* Hovers * Diagnostics (info, warnings, and errors) ## Installation