UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

227 lines (226 loc) 8.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.containsRange = containsRange; exports.precedesRange = precedesRange; exports.canRenamePosition = canRenamePosition; exports.getRenameSymbolType = getRenameSymbolType; exports.getRenameLocations = getRenameLocations; exports.getReferenceLocations = getReferenceLocations; exports.getRenameFiles = getRenameFiles; exports.getRenameWorkspaceEdit = getRenameWorkspaceEdit; exports.findDefinitionSymbols = findDefinitionSymbols; const document_symbol_1 = require("./document-symbol"); const vscode_languageserver_1 = require("vscode-languageserver"); const tree_sitter_1 = require("./utils/tree-sitter"); const node_types_1 = require("./utils/node-types"); function containsRange(range, otherRange) { if (otherRange.start.line < range.start.line || otherRange.end.line < range.start.line) { return false; } if (otherRange.start.line > range.end.line || otherRange.end.line > range.end.line) { return false; } if (otherRange.start.line === range.start.line && otherRange.start.character < range.start.character) { return false; } if (otherRange.end.line === range.end.line && otherRange.end.character > range.end.character) { return false; } return true; } function precedesRange(before, after) { if (before.start.line < after.start.line) { return true; } if (before.start.line === after.start.line && before.start.character < after.start.character) { return true; } return false; } function canRenamePosition(analyzer, document, position) { return !!analyzer.findDocumentSymbol(document, position); } function getRenameSymbolType(analyzer, document, position) { const symbol = analyzer.findDocumentSymbol(document, position); if (!symbol) { return 'local'; } if ((0, document_symbol_1.isGlobalSymbol)(symbol) || (0, document_symbol_1.isUniversalSymbol)(symbol)) { return 'global'; } return 'local'; } function findLocations(uri, nodes, matchName) { const equalRanges = (a, b) => { return (a.start.line === b.start.line && a.start.character === b.start.character && a.end.line === b.end.line && a.end.character === b.end.character); }; const matchingNames = nodes.filter(node => node.text === matchName); const uniqueRanges = []; matchingNames.forEach(node => { const range = (0, tree_sitter_1.getRange)(node); if (uniqueRanges.some(u => equalRanges(u, range))) { return; } uniqueRanges.push(range); }); return uniqueRanges.map(range => vscode_languageserver_1.Location.create(uri, range)); } function findLocalLocations(analyzer, document, position) { const symbol = findDefinitionSymbols(analyzer, document, position).pop(); if (!symbol) { return []; } const nodesToSearch = (0, tree_sitter_1.getChildNodes)(symbol.scope.scopeNode); return findLocations(document.uri, nodesToSearch, symbol.name); } function removeLocalSymbols(matchSymbol, nodes, symbols) { const name = matchSymbol.name; const matchingSymbols = (0, document_symbol_1.filterLocalSymbols)(symbols.filter(symbol => symbol.name === name)).map(symbol => symbol.scope.scopeNode); const matchingNodes = nodes.filter(node => node.text === name); if (matchingSymbols.length === 0 || matchSymbol.kind === vscode_languageserver_1.SymbolKind.Function) { return matchingNodes; } return matchingNodes.filter((node) => { if (matchingSymbols.some(scopeNode => containsRange((0, tree_sitter_1.getRange)(scopeNode), (0, tree_sitter_1.getRange)(node)))) { return false; } return true; }); } function findGlobalLocations(analyzer, document, position) { const locations = []; const symbol = analyzer.findDocumentSymbol(document, position); if (!symbol) { return []; } const uris = analyzer.cache.uris(); for (const uri of uris) { const doc = analyzer.getDocument(uri); if (!doc.isAutoloaded()) { continue; } const rootNode = analyzer.getRootNode(doc); const toSearchNodes = removeLocalSymbols(symbol, (0, tree_sitter_1.getChildNodes)(rootNode), analyzer.cache.getFlatDocumentSymbols(uri)); const newLocations = findLocations(uri, toSearchNodes, symbol.name); locations.push(...newLocations); } return locations; } function getRenameLocations(analyzer, document, position) { if (!canRenamePosition(analyzer, document, position)) { return []; } const renameScope = getRenameSymbolType(analyzer, document, position); switch (renameScope) { case 'local': return findLocalLocations(analyzer, document, position); case 'global': return findGlobalLocations(analyzer, document, position); default: return []; } } function getReferenceLocations(analyzer, document, position) { const node = analyzer.nodeAtPoint(document.uri, position.line, position.character); if (!node) return []; const symbol = analyzer.getDefinition(document, position); if (symbol) { const doc = analyzer.getDocument(symbol.uri); const { scopeTag } = symbol.scope; switch (scopeTag) { case 'global': case 'universal': return findGlobalLocations(analyzer, doc, symbol.selectionRange.start); case 'local': default: return findLocalLocations(analyzer, document, symbol.selectionRange.start); } } if ((0, node_types_1.isCommandName)(node)) { const uris = analyzer.cache.uris(); const locations = []; for (const uri of uris) { const doc = analyzer.getDocument(uri); const rootNode = analyzer.getRootNode(doc); const nodes = (0, tree_sitter_1.getChildNodes)(rootNode).filter(n => (0, node_types_1.isCommandName)(n)); const newLocations = findLocations(uri, nodes, node.text); locations.push(...newLocations); } return locations; } return []; } const createRenameFile = (oldUri, newUri) => { return { kind: 'rename', oldUri, newUri, }; }; function getRenameFiles(analyzer, document, position, newName) { const renameFiles = []; const symbol = analyzer.findDocumentSymbol(document, position); if (!symbol) { return null; } if (symbol.kind !== vscode_languageserver_1.SymbolKind.Function) { return null; } if ((0, document_symbol_1.symbolIsImmutable)(symbol)) { return null; } if (symbol.scope.scopeTag === 'global') { analyzer.getExistingAutoloadedFiles(symbol.name).forEach(uri => { const newUri = uri.replace(symbol.name, newName); renameFiles.push(createRenameFile(uri, newUri)); }); } return renameFiles; } function getRenameWorkspaceEdit(analyzer, document, position, newName) { const locations = getRenameLocations(analyzer, document, position); if (!locations || locations.length === 0) { return null; } const changes = {}; for (const location of locations) { const uri = location.uri; const edits = changes[uri] || []; edits.push(vscode_languageserver_1.TextEdit.replace(location.range, newName)); changes[uri] = edits; } const documentChanges = getRenameFiles(analyzer, document, position, newName); if (documentChanges && documentChanges.length > 0) { return { changes, documentChanges }; } return { changes }; } function findDefinitionSymbols(analyzer, document, position) { const symbols = []; const localSymbols = analyzer.getFlatDocumentSymbols(document.uri); const toFind = analyzer.nodeAtPoint(document.uri, position.line, position.character); if (!toFind) { return []; } const localSymbol = analyzer.findDocumentSymbol(document, position); if (localSymbol) { symbols.push(localSymbol); } else { const toAdd = localSymbols.filter((s) => { const variableBefore = s.kind === vscode_languageserver_1.SymbolKind.Variable ? precedesRange(s.selectionRange, (0, tree_sitter_1.getRange)(toFind)) : true; return (s.name === toFind.text && containsRange((0, tree_sitter_1.getRange)(s.scope.scopeNode), (0, tree_sitter_1.getRange)(toFind)) && variableBefore); }); symbols.push(...toAdd); } if (!symbols.length) { symbols.push(...analyzer.globalSymbols.find(toFind.text)); } return symbols; }