diff --git a/.vscode/launch.json b/.vscode/launch.json index 445edf3..5ab8920 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,7 +25,7 @@ "address": "localhost", "restart": true, "outFiles": [ - "${workspaceFolder}/server/out/*.js" + "${workspaceFolder}/server/out/**/*.js" ] } ], diff --git a/server/src/providers/definition.ts b/server/src/providers/definition.ts index 94f1ba0..2c88530 100644 --- a/server/src/providers/definition.ts +++ b/server/src/providers/definition.ts @@ -4,7 +4,8 @@ import { URI } from 'vscode-uri'; import { DefinitionParams, DefinitionLink, Range } from 'vscode-languageserver/node'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { JRefSymbol } from '../visitor'; -import { ServerContext } from '../utils'; +import { analyze, ServerContext } from '../utils'; +import * as fs from 'fs'; const defaultTargetRange: Range = { start: { line: 0, character: 0 }, @@ -43,13 +44,26 @@ function createDefinitionLink( const uri = URI.parse(targetPath); const currentDir = path.dirname(URI.parse(document.uri).fsPath); const absolutePath = path.resolve(currentDir, uri.path.slice(1)); - const targetDocument = documents.get(URI.file(absolutePath).toString()); - const targetRange = getTargetRange(targetDocument); + const targetUri = URI.file(absolutePath).toString(); + const targetRange = getTargetRange(targetUri); - function getTargetRange(targetDocument: TextDocument | undefined): Range { - if (!targetDocument) return defaultTargetRange; - const targetSymbolTable = documentSymbols.get(targetDocument); - if (!targetSymbolTable) return defaultTargetRange; + function getTargetRange(targetUri: string): Range { + let targetDocument = documents.get(targetUri); + if (!targetDocument) { + try { + const filePath = URI.parse(targetUri).fsPath; + const content = fs.readFileSync(filePath, 'utf8'); + targetDocument = TextDocument.create(targetUri, 'jref', 1, content); + } catch (e) { + return defaultTargetRange; + } + } + let targetSymbolTable = documentSymbols.get(targetDocument); + if (!targetSymbolTable) { + const { symbols } = analyze(targetDocument.getText()); + documentSymbols.set(targetDocument, symbols); + targetSymbolTable = symbols; + } const targetSymbol = targetSymbolTable?.get(uri.fragment); if (!targetSymbol) return defaultTargetRange; return { diff --git a/server/src/server.ts b/server/src/server.ts index 30a83c7..e4508c9 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -10,12 +10,13 @@ import { import { TextDocument } from 'vscode-languageserver-textdocument'; -import { Node, ParseError, parseTree } from 'jsonc-parser'; +import { ParseError } from 'jsonc-parser'; import { createParseErrorDiagnostic } from './providers/diagnostics'; import { onDefinition } from './providers/definition'; -import { SymbolTable, visit } from './visitor'; +import { SymbolTable } from './visitor'; import { handleSemanticTokens, tokenTypes } from './providers/semanticTokens'; +import { analyze } from './utils'; // Create a connection for the server, using Node's IPC as a transport. // Also include all preview / proposed LSP features. @@ -44,10 +45,7 @@ connection.onInitialize((params: InitializeParams) => { // 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: TextDocumentChangeEvent) => { - const errors: ParseError[] = []; - const ast: Node | undefined = parseTree(change.document.getText(), errors); - const symbols: SymbolTable = new Map(); - visit(ast, symbols); + const { symbols, errors } = analyze(change.document.getText()); documentSymbols.set(change.document, symbols); sendDiagnostics(change.document, errors); }); diff --git a/server/src/utils.ts b/server/src/utils.ts index 6986815..6785899 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -1,8 +1,22 @@ import { TextDocuments } from 'vscode-languageserver/node'; -import { SymbolTable } from './visitor'; +import { SymbolTable, visit } from './visitor'; import { TextDocument } from 'vscode-languageserver-textdocument'; +import { ParseError, parseTree } from 'jsonc-parser'; export interface ServerContext { documents: TextDocuments; documentSymbols: WeakMap; } + +export interface DocumentAnalysis { + symbols: SymbolTable; + errors: ParseError[]; +} + +export function analyze(content: string): DocumentAnalysis { + const errors: ParseError[] = []; + const ast = parseTree(content, errors); + const symbols: SymbolTable = new Map(); + visit(ast, symbols); + return { symbols, errors }; +}