diff --git a/server/src/core/codelens-provider.ts b/server/src/core/codelens-provider.ts new file mode 100644 index 0000000..1585110 --- /dev/null +++ b/server/src/core/codelens-provider.ts @@ -0,0 +1,60 @@ +/** + * @fileoverview Handles all CodeLens logic for Apache Dispatcher Config files. + * @author Darian Benam + */ + +import { ApacheDispatcherConfigToken } from "@language-server/core/tree-sitter"; +import { removeOuterQuotes } from "@language-server/utils/string"; +import { CodeLens, Command, Range } from "vscode-languageserver"; +import Parser = require("web-tree-sitter"); + +function containsEnvironmentVariable(value: string): boolean { + return /\$\{.*?\}/.test(value); +} + +export function getCodeLensDefinitions(syntaxTree: Parser.Tree | undefined): CodeLens[] { + if (syntaxTree === undefined) { + return []; + } + + const codeLensDefinitions: CodeLens[] = []; + const syntaxNodeStack: Parser.SyntaxNode[] = [ syntaxTree.rootNode ]; + + while (syntaxNodeStack.length > 0) { + const syntaxNode: Parser.SyntaxNode | undefined = syntaxNodeStack.pop(); + + if (syntaxNode === undefined || syntaxNode.hasError()) { + continue; + } + + const syntaxNodeValue: string = removeOuterQuotes(syntaxNode.text); + + if ( + syntaxNode.type === ApacheDispatcherConfigToken.String && + containsEnvironmentVariable(syntaxNodeValue) + ) { + const range: Range = { + start: { + line: syntaxNode.startPosition.row, + character: syntaxNode.startPosition.column + }, + end: { + line: syntaxNode.endPosition.row, + character: syntaxNode.endPosition.column + } + }; + + // TODO: Look into creating a lookup table for environment variables under the /conf.d/variables folder + codeLensDefinitions.push({ + range: range, + command: Command.create(`"${syntaxNodeValue}" resolves to ""`, "") + }); + } + + for (const childNode of syntaxNode.children) { + syntaxNodeStack.push(childNode); + } + } + + return codeLensDefinitions; +} diff --git a/server/src/core/definition-provider.ts b/server/src/core/definition-provider.ts index 33243ad..a525037 100644 --- a/server/src/core/definition-provider.ts +++ b/server/src/core/definition-provider.ts @@ -7,6 +7,7 @@ import { INCLUDE_FUNCTION_NAME } from "@language-server/constants/function"; import { DocumentParserTreeManager } from "@language-server/core/document-parser-tree-manager"; import { ApacheDispatcherConfigToken, getCurrentSyntaxNode } from "@language-server/core/tree-sitter"; import { FileExistenceContext, getFileExistenceContext } from "@language-server/utils/file-system"; +import { removeOuterQuotes } from "@language-server/utils/string"; import { DefinitionLink, DefinitionParams, @@ -19,22 +20,6 @@ import Parser = require("web-tree-sitter"); const START_POSITION: Position = Position.create(0, 0); const START_POSITION_RANGE: Range = Range.create(START_POSITION, START_POSITION); -function isSingleQuoteString(text: string): boolean { - return text.startsWith("'") && text.endsWith("'"); -} - -function isDoubleQuoteString(text: string): boolean { - return text.startsWith("\"") && text.endsWith("\""); -} - -function removeOuterQuotes(text: string): string { - if (isSingleQuoteString(text) || isDoubleQuoteString(text)) { - return text.substring(1, text.length - 1); - } - - return text; -} - export async function getDefinition( documentParserTreeManager: DocumentParserTreeManager, definitionParams: DefinitionParams diff --git a/server/src/core/document-parser-tree-manager.ts b/server/src/core/document-parser-tree-manager.ts index 95e8baa..5a502ba 100644 --- a/server/src/core/document-parser-tree-manager.ts +++ b/server/src/core/document-parser-tree-manager.ts @@ -3,12 +3,13 @@ * @author Darian Benam */ +import { getCodeLensDefinitions } from "@language-server/core/codelens-provider"; import { ApacheDispatcherConfigToken, loadApacheDispatcherConfigTreeSitterLanguage, tokenizeTextDocument } from "@language-server/core/tree-sitter"; -import { DocumentSymbol, SymbolKind, TextDocuments } from "vscode-languageserver"; +import { CodeLens, DocumentSymbol, SymbolKind, TextDocuments } from "vscode-languageserver"; import { Range, TextDocument } from "vscode-languageserver-textdocument"; import Parser = require("web-tree-sitter"); @@ -98,7 +99,6 @@ export class DocumentParserTreeManager { }; } - private getApacheDispatcherConfigTokenSymbolKind(tokenType: ApacheDispatcherConfigToken): SymbolKind { switch (tokenType) { case ApacheDispatcherConfigToken.Function: @@ -194,6 +194,10 @@ export class DocumentParserTreeManager { return rootSymbols; } + public getDocumentCodeLensDefinitions(documentUri: string): CodeLens[] { + return getCodeLensDefinitions(this.documentParseTree.get(documentUri)) + } + public updateParseTree(document: TextDocument): void { if (this.treeSitterParser === undefined) { throw new Error("Tree-sitter parser has not been initialized!"); diff --git a/server/src/server.ts b/server/src/server.ts index 0b3f9db..1b2fdd5 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -12,6 +12,8 @@ import * as path from "path"; import { TextDocument } from "vscode-languageserver-textdocument"; import { ClientCapabilities, + CodeLens, + CodeLensParams, CompletionItem, Connection, DefinitionLink, @@ -50,6 +52,9 @@ CONNECTION.onInitialize(async function(initializeParams: InitializeParams): Prom const result: InitializeResult = { capabilities: { + codeLensProvider: { + resolveProvider: true + }, completionProvider: { triggerCharacters: AUTOCOMPLETION_TRIGGER_CHARACTERS }, @@ -85,6 +90,20 @@ CONNECTION.onInitialized(async function(): Promise { console.info("Language server initialized."); }); +CONNECTION.onCodeLens(async function(params: CodeLensParams): Promise { + if (documentParserTreeManager === undefined) { + await waitForDocumentParserTreeManagerInitialization(documentParserTreeManager); + } + + const document: TextDocument | undefined = DOCUMENT_MANAGER.get(params.textDocument.uri); + + if (document === undefined) { + return []; + } + + return documentParserTreeManager.getDocumentCodeLensDefinitions(document.uri); +}); + CONNECTION.onCompletion( async (textDocumentPositionParams: TextDocumentPositionParams): Promise => { return await handleAutoCompletion( diff --git a/server/src/utils/string.ts b/server/src/utils/string.ts new file mode 100644 index 0000000..2484bea --- /dev/null +++ b/server/src/utils/string.ts @@ -0,0 +1,20 @@ +/** + * @fileoverview Miscellaneous utility functions for dealing with strings. + * @author Darian Benam + */ + +function isSingleQuoteString(text: string): boolean { + return text.startsWith("'") && text.endsWith("'"); +} + +function isDoubleQuoteString(text: string): boolean { + return text.startsWith("\"") && text.endsWith("\""); +} + +export function removeOuterQuotes(text: string): string { + if (isSingleQuoteString(text) || isDoubleQuoteString(text)) { + return text.substring(1, text.length - 1); + } + + return text; +}