UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

674 lines (673 loc) 32.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.currentDocument = exports.enableWorkspaceFolderSupport = exports.hasWorkspaceFolderCapability = void 0; const analyze_1 = require("./analyze"); const vscode_languageserver_1 = require("vscode-languageserver"); const document_1 = require("./document"); const formatting_1 = require("./formatting"); const logger_1 = require("./logger"); const translation_1 = require("./utils/translation"); const tree_sitter_1 = require("./utils/tree-sitter"); const hover_1 = require("./hover"); const documentation_cache_1 = require("./utils/documentation-cache"); const workspace_1 = require("./utils/workspace"); const workspace_manager_1 = require("./utils/workspace-manager"); const symbol_1 = require("./parsing/symbol"); const pager_1 = require("./utils/completion/pager"); const documentation_1 = require("./utils/completion/documentation"); const list_1 = require("./utils/completion/list"); const snippets_1 = require("./utils/snippets"); const node_types_1 = require("./utils/node-types"); const config_1 = require("./config"); const documentation_2 = require("./documentation"); const signature_1 = require("./signature"); const startup_cache_1 = require("./utils/completion/startup-cache"); const document_highlight_1 = require("./document-highlight"); const comment_completions_1 = require("./utils/completion/comment-completions"); const code_action_handler_1 = require("./code-actions/code-action-handler"); const command_1 = require("./command"); const inlay_hints_1 = require("./inlay-hints"); const process_env_1 = require("./utils/process-env"); const flatten_1 = require("./utils/flatten"); const argparse_1 = require("./parsing/argparse"); const source_1 = require("./parsing/source"); const references_1 = require("./references"); const renames_1 = require("./renames"); const code_lens_1 = require("./code-lens"); const startup_1 = require("./utils/startup"); exports.hasWorkspaceFolderCapability = false; const enableWorkspaceFolderSupport = () => { exports.hasWorkspaceFolderCapability = true; }; exports.enableWorkspaceFolderSupport = enableWorkspaceFolderSupport; exports.currentDocument = null; class FishServer { completion; completionMap; documentationCache; static async create(connection, params) { await (0, process_env_1.setupProcessEnvExecFile)(); const capabilities = params.capabilities; const initializeResult = config_1.Config.initialize(params, connection); logger_1.logger.log({ server: 'FishServer', rootUri: params.rootUri, rootPath: params.rootPath, workspaceFolders: params.workspaceFolders, }); exports.hasWorkspaceFolderCapability = !!(capabilities.workspace && !!capabilities.workspace.workspaceFolders); logger_1.logger.debug('hasWorkspaceFolderCapability', exports.hasWorkspaceFolderCapability); const initializeUris = (0, workspace_1.getWorkspacePathsFromInitializationParams)(params); logger_1.logger.info('initializeUris', initializeUris); const [cache, _workspaces, completionsMap,] = await Promise.all([ (0, documentation_cache_1.initializeDocumentationCache)(), (0, workspace_1.initializeDefaultFishWorkspaces)(...initializeUris), startup_cache_1.CompletionItemMap.initialize(), ]); await analyze_1.Analyzer.initialize(); const completions = await (0, pager_1.initializeCompletionPager)(logger_1.logger, completionsMap); const server = new FishServer(completions, completionsMap, cache); server.register(connection); return { server, initializeResult }; } initializeParams; features; clientSupportsShowDocument; backgroundAnalysisComplete; constructor(completion, completionMap, documentationCache) { this.completion = completion; this.completionMap = completionMap; this.documentationCache = documentationCache; this.features = { codeActionDisabledSupport: true }; this.clientSupportsShowDocument = false; this.backgroundAnalysisComplete = false; } register(connection) { const { onCodeAction } = (0, code_action_handler_1.codeActionHandlers)(document_1.documents, analyze_1.analyzer); const documentHighlightHandler = (0, document_highlight_1.getDocumentHighlights)(analyze_1.analyzer); const commandCallback = (0, command_1.createExecuteCommandHandler)(connection, document_1.documents, analyze_1.analyzer); connection.onDidOpenTextDocument(this.didOpenTextDocument.bind(this)); connection.onDidChangeTextDocument(this.didChangeTextDocument.bind(this)); connection.onDidCloseTextDocument(this.didCloseTextDocument.bind(this)); connection.onDidSaveTextDocument(this.didSaveTextDocument.bind(this)); connection.onCompletion(this.onCompletion.bind(this)); connection.onCompletionResolve(this.onCompletionResolve.bind(this)); connection.onDocumentSymbol(this.onDocumentSymbols.bind(this)); connection.onWorkspaceSymbol(this.onWorkspaceSymbol.bind(this)); connection.onWorkspaceSymbolResolve(this.onWorkspaceSymbolResolve.bind(this)); connection.onDefinition(this.onDefinition.bind(this)); connection.onImplementation(this.onImplementation.bind(this)); connection.onReferences(this.onReferences.bind(this)); connection.onHover(this.onHover.bind(this)); connection.onRenameRequest(this.onRename.bind(this)); connection.onDocumentFormatting(this.onDocumentFormatting.bind(this)); connection.onDocumentRangeFormatting(this.onDocumentRangeFormatting.bind(this)); connection.onDocumentOnTypeFormatting(this.onDocumentTypeFormatting.bind(this)); connection.onCodeAction(onCodeAction); connection.onCodeLens(this.onCodeLens.bind(this)); connection.onFoldingRanges(this.onFoldingRanges.bind(this)); connection.onDocumentHighlight(documentHighlightHandler); connection.languages.inlayHint.on(this.onInlayHints.bind(this)); connection.onSignatureHelp(this.onShowSignatureHelp.bind(this)); connection.onExecuteCommand(commandCallback); connection.onInitialized(this.onInitialized.bind(this)); connection.onShutdown(this.onShutdown.bind(this)); logger_1.logger.log({ 'server.register': 'registered' }); } async didOpenTextDocument(params) { this.logParams('didOpenTextDocument', params); const path = (0, translation_1.uriToPath)(params.textDocument.uri); const doc = document_1.documents.openPath(path, params.textDocument); workspace_manager_1.workspaceManager.handleOpenDocument(doc); exports.currentDocument = doc; this.analyzeDocument({ uri: doc.uri }); workspace_manager_1.workspaceManager.handleUpdateDocument(doc); if (workspace_manager_1.workspaceManager.needsAnalysis() && workspace_manager_1.workspaceManager.allAnalysisDocuments().length > 0) { const progress = await startup_1.connection.window.createWorkDoneProgress(); progress.begin('[fish-lsp] analysis'); await workspace_manager_1.workspaceManager.analyzePendingDocuments(progress, (str) => logger_1.logger.info('didOpen', str)); progress.done(); } } async didChangeTextDocument(params) { this.logParams('didChangeTextDocument', params); const progress = await startup_1.connection.window.createWorkDoneProgress(); const path = (0, translation_1.uriToPath)(params.textDocument.uri); let doc = document_1.documents.get(path); if (!doc) { doc = analyze_1.analyzer.analyzePath(path)?.document; } if (!doc) { logger_1.logger.warning('didChangeTextDocument: document not found', { path }); return; } exports.currentDocument = doc; doc = doc.update(params.contentChanges); document_1.documents.set(doc); this.analyzeDocument({ uri: doc.uri }); if (!this.backgroundAnalysisComplete) { await workspace_manager_1.workspaceManager.analyzePendingDocuments(progress); progress.done(); return; } await workspace_manager_1.workspaceManager.analyzePendingDocuments(); progress.done(); } didCloseTextDocument(params) { this.logParams('didCloseTextDocument', params); workspace_manager_1.workspaceManager.handleCloseDocument(params.textDocument.uri); } async didSaveTextDocument(params) { this.logParams('didSaveTextDocument', params); const path = (0, translation_1.uriToPath)(params.textDocument.uri); const doc = document_1.documents.get(path); if (doc) { this.analyzeDocument({ uri: doc.uri }); workspace_manager_1.workspaceManager.handleOpenDocument(doc); workspace_manager_1.workspaceManager.handleUpdateDocument(doc); await workspace_manager_1.workspaceManager.analyzePendingDocuments(); } } async onShutdown() { workspace_manager_1.workspaceManager.clear(); document_1.documents.clear(); exports.currentDocument = null; this.backgroundAnalysisComplete = false; } async onInitialized(params) { logger_1.logger.log('onInitialized', params); if (exports.hasWorkspaceFolderCapability) { startup_1.connection.workspace.onDidChangeWorkspaceFolders(event => { logger_1.logger.info({ 'connection.workspace.onDidChangeWorkspaceFolders': 'analyzer.onInitialized', added: event.added.map(folder => folder.name), removed: event.removed.map(folder => folder.name), hasWorkspaceFolderCapability: exports.hasWorkspaceFolderCapability, }); this.handleWorkspaceFolderChanges(event); }); } const result = await startup_1.connection.window.createWorkDoneProgress().then(async (progress) => { progress.begin('[fish-lsp] analyzing workspaces'); const { totalDocuments } = await workspace_manager_1.workspaceManager.analyzePendingDocuments(progress, (str) => logger_1.logger.info('onInitialized', str)); progress.done(); this.backgroundAnalysisComplete = true; return totalDocuments; }); return { result, }; } async handleWorkspaceFolderChanges(event) { this.logParams('handleWorkspaceFolderChanges', event); const progress = await startup_1.connection.window.createWorkDoneProgress(); progress.begin(`[fish-lsp] analyzing workspaces [${event.added.map(s => s.name).join(',')}] added`); workspace_manager_1.workspaceManager.handleWorkspaceChangeEvent(event, progress); workspace_manager_1.workspaceManager.analyzePendingDocuments(progress); } onCommand(params) { const callback = (0, command_1.createExecuteCommandHandler)(startup_1.connection, document_1.documents, analyze_1.analyzer); return callback(params); } async onCompletion(params) { this.logParams('onCompletion', params); if (!this.backgroundAnalysisComplete) { return await this.completion.completeEmpty([]); } const { doc, path, current } = this.getDefaults(params); let list = list_1.FishCompletionList.empty(); if (!path || !doc) { logger_1.logger.logAsJson('onComplete got [NOT FOUND]: ' + path); return this.completion.empty(); } const symbols = analyze_1.analyzer.allSymbolsAccessibleAtPosition(doc, params.position); const { line, word } = analyze_1.analyzer.parseCurrentLine(doc, params.position); logger_1.logger.log({ symbols: symbols.map(s => s.name), }); if (!line) return await this.completion.completeEmpty(symbols); const fishCompletionData = { uri: doc.uri, position: params.position, context: { triggerKind: params.context?.triggerKind || vscode_languageserver_1.CompletionTriggerKind.Invoked, triggerCharacter: params.context?.triggerCharacter, }, }; try { if (line.trim().startsWith('#') && current) { logger_1.logger.log('completeComment'); return (0, comment_completions_1.buildCommentCompletions)(line, params.position, current, fishCompletionData, word); } if (word.trim().endsWith('$') || line.trim().endsWith('$') || word.trim() === '$' && !word.startsWith('$$')) { logger_1.logger.log('completeVariables'); return this.completion.completeVariables(line, word, fishCompletionData, symbols); } } catch (error) { logger_1.logger.warning('ERROR: onComplete ' + error?.toString() || 'error'); } try { logger_1.logger.log('complete'); list = await this.completion.complete(line, fishCompletionData, symbols); } catch (error) { logger_1.logger.logAsJson('ERROR: onComplete ' + error?.toString() || 'error'); } return list; } async onCompletionResolve(item) { const fishItem = item; logger_1.logger.log({ onCompletionResolve: fishItem }); try { if (fishItem.useDocAsDetail || fishItem.local) { item.documentation = { kind: vscode_languageserver_1.MarkupKind.Markdown, value: fishItem.documentation.toString(), }; return item; } const doc = await (0, documentation_1.getDocumentationResolver)(fishItem); if (doc) { item.documentation = doc; } } catch (err) { logger_1.logger.error('onCompletionResolve', err); } return item; } onDocumentSymbols(params) { this.logParams('onDocumentSymbols', params); const { doc } = this.getDefaultsForPartialParams(params); if (!doc) return []; const symbols = analyze_1.analyzer.cache.getDocumentSymbols(doc.uri); return (0, symbol_1.filterLastPerScopeSymbol)(symbols).map(s => s.toDocumentSymbol()).filter(s => !!s); } get supportHierarchicalDocumentSymbol() { const textDocument = this.initializeParams?.capabilities.textDocument; const documentSymbol = textDocument && textDocument.documentSymbol; return (!!documentSymbol && !!documentSymbol.hierarchicalDocumentSymbolSupport); } async onWorkspaceSymbol(params) { this.logParams('onWorkspaceSymbol', params.query); const symbols = []; const workspace = workspace_manager_1.workspaceManager.current; for (const uri of workspace?.allUris || []) { const doc = document_1.documents.get(uri); if (doc) { const docSymbols = analyze_1.analyzer.getFlatDocumentSymbols(doc.uri); symbols.push(...(0, symbol_1.filterLastPerScopeSymbol)(docSymbols)); } } logger_1.logger.log('symbols', { uris: workspace?.allUris, symbols: symbols.map(s => s.name), }); return analyze_1.analyzer.getWorkspaceSymbols(params.query) || []; } async onWorkspaceSymbolResolve(symbol) { this.logParams('onWorkspaceSymbolResolve', symbol); const { uri } = symbol.location; const foundSymbol = analyze_1.analyzer.getFlatDocumentSymbols(uri) .find(s => s.name === symbol.name && s.isGlobal()); if (foundSymbol) { return { ...foundSymbol.toWorkspaceSymbol(), ...foundSymbol.toDocumentSymbol(), }; } return symbol; } async onDefinition(params) { this.logParams('onDefinition', params); const { doc } = this.getDefaults(params); if (!doc) return []; const newDefs = analyze_1.analyzer.getDefinitionLocation(doc, params.position); for (const location of newDefs) { workspace_manager_1.workspaceManager.handleOpenDocument(location.uri); workspace_manager_1.workspaceManager.handleUpdateDocument(location.uri); } if (workspace_manager_1.workspaceManager.needsAnalysis()) { await workspace_manager_1.workspaceManager.analyzePendingDocuments(); } return newDefs; } async onReferences(params) { this.logParams('onReference', params); const { doc } = this.getDefaults(params); if (!doc) return []; const progress = await startup_1.connection.window.createWorkDoneProgress(); const defSymbol = analyze_1.analyzer.getDefinition(doc, params.position); if (!defSymbol) { logger_1.logger.log('onReferences: no definition found at position', params.position); return []; } const results = (0, references_1.getReferences)(defSymbol.document, defSymbol.toPosition(), { reporter: progress, }); logger_1.logger.info({ onReferences: 'found references', uri: defSymbol.uri, count: results.length, position: params.position, symbol: defSymbol.name, }); if (results.length === 0) { logger_1.logger.warning('onReferences: no references found', { uri: params.textDocument.uri, position: params.position }); return []; } return results; } async onImplementation(params) { this.logParams('onImplementation', params); const { doc } = this.getDefaults(params); if (!doc) return []; const symbols = analyze_1.analyzer.cache.getDocumentSymbols(doc.uri); const lastSymbols = (0, symbol_1.filterLastPerScopeSymbol)(symbols); logger_1.logger.log('symbols', (0, symbol_1.formatFishSymbolTree)(lastSymbols)); const result = analyze_1.analyzer.getImplementation(doc, params.position); logger_1.logger.log('implementationResult', { result }); return result; } async onHover(params) { this.logParams('onHover', params); const { doc, path, root, current } = this.getDefaults(params); if (!doc || !path || !root || !current) { return null; } let result = null; if ((0, source_1.isSourceCommandArgumentName)(current)) { result = (0, documentation_2.handleSourceArgumentHover)(analyze_1.analyzer, current); if (result) return result; } if (current.parent && (0, source_1.isSourceCommandArgumentName)(current.parent)) { result = (0, documentation_2.handleSourceArgumentHover)(analyze_1.analyzer, current.parent); if (result) return result; } if ((0, node_types_1.isAliasDefinitionName)(current)) { result = analyze_1.analyzer.getDefinition(doc, params.position)?.toHover(doc.uri) || null; if (result) return result; } if ((0, argparse_1.isArgparseVariableDefinitionName)(current)) { logger_1.logger.log('isArgparseDefinition'); result = analyze_1.analyzer.getDefinition(doc, params.position)?.toHover(doc.uri) || null; return result; } if ((0, node_types_1.isOption)(current)) { result = analyze_1.analyzer.getDefinition(doc, params.position)?.toHover(doc.uri) || null; if (result) return result; result = await (0, hover_1.handleHover)(analyze_1.analyzer, doc, params.position, current, this.documentationCache); if (result) return result; } const { kindType, kindString } = (0, translation_1.symbolKindsFromNode)(current); logger_1.logger.log({ currentText: current.text, currentType: current.type, symbolKind: kindString }); const prebuiltSkipType = [ ...snippets_1.PrebuiltDocumentationMap.getByType('pipe'), ...(0, node_types_1.isReturnStatusNumber)(current) ? snippets_1.PrebuiltDocumentationMap.getByType('status') : [], ].find(obj => obj.name === current.text); const isPrebuiltVariableWithoutDefinition = (0, hover_1.getVariableExpansionDocs)(analyze_1.analyzer, doc, params.position); const prebuiltHover = isPrebuiltVariableWithoutDefinition(current); if (prebuiltHover) return prebuiltHover; const symbolItem = analyze_1.analyzer.getHover(doc, params.position); if (symbolItem) return symbolItem; if (prebuiltSkipType) { return { contents: (0, documentation_2.enrichToMarkdown)([ `___${current.text}___ - _${(0, snippets_1.getPrebuiltDocUrl)(prebuiltSkipType)}_`, '___', `type - __(${prebuiltSkipType.type})__`, '___', `${prebuiltSkipType.description}`, ].join('\n')), }; } const definition = analyze_1.analyzer.getDefinition(doc, params.position); const allowsGlobalDocs = !definition || definition?.isGlobal(); const symbolType = [ 'function', 'class', 'variable', ].includes(kindString) ? kindType : undefined; const globalItem = await this.documentationCache.resolve(current.text.trim(), path, symbolType); logger_1.logger.log(`this.documentationCache.resolve() found ${!!globalItem}`, { docs: globalItem.docs }); if (globalItem && globalItem.docs && allowsGlobalDocs) { logger_1.logger.log(globalItem.docs); return { contents: { kind: vscode_languageserver_1.MarkupKind.Markdown, value: globalItem.docs, }, }; } const fallbackHover = await (0, hover_1.handleHover)(analyze_1.analyzer, doc, params.position, current, this.documentationCache); logger_1.logger.log(fallbackHover?.contents); return fallbackHover; } async onRename(params) { this.logParams('onRename', params); const { doc } = this.getDefaults(params); if (!doc) return null; const locations = (0, renames_1.getRenames)(doc, params.position, params.newName); const changes = {}; for (const location of locations) { const range = location.range; const uri = location.uri; const edits = changes[uri] || []; edits.push(vscode_languageserver_1.TextEdit.replace(range, location.newText)); changes[uri] = edits; } const workspaceEdit = { changes, }; return workspaceEdit; } async onDocumentFormatting(params) { this.logParams('onDocumentFormatting', params); const { doc } = this.getDefaultsForPartialParams(params); if (!doc) return []; const formattedText = await (0, formatting_1.formatDocumentContent)(doc.getText()).catch(error => { if (config_1.config.fish_lsp_show_client_popups) { startup_1.connection.window.showErrorMessage(`Failed to format range: ${error}`); } return doc.getText(); }); const fullRange = { start: doc.positionAt(0), end: doc.positionAt(doc.getText().length), }; return [vscode_languageserver_1.TextEdit.replace(fullRange, formattedText)]; } async onDocumentTypeFormatting(params) { this.logParams('onDocumentTypeFormatting', params); const { doc } = this.getDefaultsForPartialParams(params); if (!doc) return []; const formattedText = await (0, formatting_1.formatDocumentContent)(doc.getText()).catch(error => { startup_1.connection.console.error(`Formatting error: ${error}`); if (config_1.config.fish_lsp_show_client_popups) { startup_1.connection.window.showErrorMessage(`Failed to format range: ${error}`); } return doc.getText(); }); const fullRange = { start: doc.positionAt(0), end: doc.positionAt(doc.getText().length), }; return [vscode_languageserver_1.TextEdit.replace(fullRange, formattedText)]; } async onDocumentRangeFormatting(params) { this.logParams('onDocumentRangeFormatting', params); const { doc } = this.getDefaultsForPartialParams(params); if (!doc) return []; const range = params.range; const startOffset = doc.offsetAt(range.start); const endOffset = doc.offsetAt(range.end); const originalText = doc.getText(); const selectedText = doc.getText().slice(startOffset, endOffset).trimStart(); const allText = await (0, formatting_1.formatDocumentContent)(originalText).catch((error) => { logger_1.logger.error(`FormattingRange error: ${error}`); return selectedText; }); const formattedText = await (0, formatting_1.formatDocumentContent)(selectedText).catch(error => { logger_1.logger.error(`FormattingRange error: ${error}`, { input: selectedText, range: range, }); if (config_1.config.fish_lsp_show_client_popups) { startup_1.connection.window.showErrorMessage(`Failed to format range: ${params.textDocument.uri}`); } return selectedText; }); const newDoc = document_1.LspDocument.createTextDocumentItem(doc.uri, allText); const output = (0, translation_1.formatTextWithIndents)(newDoc, range.start.line, formattedText.trim()) + '\n'; return [ vscode_languageserver_1.TextEdit.replace(params.range, output), ]; } async onFoldingRanges(params) { this.logParams('onFoldingRanges', params); const { path, doc } = this.getDefaultsForPartialParams(params); if (!doc) { throw new Error(`The document should not be opened in the folding range, file: ${path}`); } const symbols = analyze_1.analyzer.getDocumentSymbols(doc.uri); const flatSymbols = (0, flatten_1.flattenNested)(...symbols); logger_1.logger.logPropertiesForEachObject(flatSymbols.filter((s) => s.kind === vscode_languageserver_1.SymbolKind.Function), 'name', 'range'); const folds = flatSymbols .filter((symbol) => symbol.kind === vscode_languageserver_1.SymbolKind.Function) .map((symbol) => symbol.toFoldingRange()); folds.forEach((fold) => logger_1.logger.log({ fold })); return folds; } async onInlayHints(params) { logger_1.logger.log({ params }); const { doc } = this.getDefaultsForPartialParams(params); if (!doc) return []; return (0, inlay_hints_1.getAllInlayHints)(analyze_1.analyzer, doc); } async onCodeLens(params) { logger_1.logger.log('onCodeLens', params); const path = (0, translation_1.uriToPath)(params.textDocument.uri); const doc = document_1.documents.get(path); if (!doc) return []; return (0, code_lens_1.getReferenceCountCodeLenses)(analyze_1.analyzer, doc); } onShowSignatureHelp(params) { this.logParams('onShowSignatureHelp', params); const { doc, path } = this.getDefaults(params); if (!doc || !path) return null; const { line, lineRootNode, lineLastNode } = analyze_1.analyzer.parseCurrentLine(doc, params.position); if (line.trim() === '') return null; const currentCmd = (0, node_types_1.findParentCommand)(lineLastNode); const aliasSignature = this.completionMap.allOfKinds('alias').find(a => a.label === currentCmd.text); if (aliasSignature) return (0, signature_1.getAliasedCompletionItemSignature)(aliasSignature); const varNode = (0, tree_sitter_1.getChildNodes)(lineRootNode).find(c => (0, node_types_1.isVariableDefinition)(c)); const lastCmd = (0, tree_sitter_1.getChildNodes)(lineRootNode).filter(c => (0, node_types_1.isCommand)(c)).pop(); logger_1.logger.log({ line, lastCmds: lastCmd?.text }); if (varNode && (line.startsWith('set') || line.startsWith('read')) && lastCmd?.text === lineRootNode.text.trim()) { const varName = varNode.text; const varDocs = snippets_1.PrebuiltDocumentationMap.getByName(varNode.text); if (!varDocs.length) return null; return { signatures: [ { label: varName, documentation: { kind: 'markdown', value: varDocs.map(d => d.description).join('\n'), }, }, ], activeSignature: 0, activeParameter: 0, }; } if ((0, signature_1.isRegexStringSignature)(line)) { const signature = (0, signature_1.getDefaultSignatures)(); logger_1.logger.log('signature', signature); const cursorLineOffset = line.length - lineLastNode.endIndex; const { activeParameter } = (0, signature_1.findActiveParameterStringRegex)(line, cursorLineOffset); signature.activeParameter = activeParameter; return signature; } const functionSignature = (0, signature_1.getFunctionSignatureHelp)(analyze_1.analyzer, lineLastNode, line, params.position); if (functionSignature) return functionSignature; return null; } clearDiagnostics(document) { startup_1.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] }); } analyzeDocument(document) { const { path, doc: foundDoc } = this.getDefaultsForPartialParams({ textDocument: document }); let analyzedDoc; if (!foundDoc) { const pathDoc = analyze_1.analyzer.analyzePath(path); if (pathDoc) { analyzedDoc = pathDoc; } else { logger_1.logger.log('analyzeDocument: document not found', { path }); return; } } else { analyzedDoc = analyze_1.analyzer.analyze(foundDoc); } const doc = analyzedDoc.document; const diagnostics = analyze_1.analyzer.getDiagnostics(doc.uri); logger_1.logger.log('Sending Diagnostics', { uri: doc.uri, diagnostics: diagnostics.map(d => d.code), }); startup_1.connection.sendDiagnostics({ uri: doc.uri, diagnostics }); workspace_manager_1.workspaceManager.handleUpdateDocument(doc); return { uri: document.uri, path: path, doc: doc, }; } logParams(methodName, ...params) { logger_1.logger.log({ handler: methodName, params }); } getDefaults(params) { const path = (0, translation_1.uriToPath)(params.textDocument.uri); const doc = document_1.documents.get(path); if (!doc || !path) return { path }; const root = analyze_1.analyzer.getRootNode(doc.uri); const current = analyze_1.analyzer.nodeAtPoint(doc.uri, params.position.line, params.position.character); return { doc, path, root, current }; } getDefaultsForPartialParams(params) { const path = (0, translation_1.uriToPath)(params.textDocument.uri); const doc = document_1.documents.get(path); const root = doc ? analyze_1.analyzer.getRootNode(doc.uri) : undefined; return { doc, path, root }; } } exports.default = FishServer;