UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

209 lines (208 loc) 9.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findAllMissingArgparseFlags = findAllMissingArgparseFlags; const analyze_1 = require("../analyze"); const logger_1 = require("../logger"); const tree_sitter_1 = require("../utils/tree-sitter"); const complete_1 = require("../parsing/complete"); const flatten_1 = require("../utils/flatten"); const Locations = __importStar(require("../utils/locations")); const translation_1 = require("../utils/translation"); const code_action_handler_1 = require("../code-actions/code-action-handler"); function findAllMissingArgparseFlags(document) { const fishSymbols = []; const completionSymbols = []; const diagnostics = []; if (document.isFunction()) { const result = findMissingArgparseFlagsWithExistingCompletions(document); completionSymbols.push(...result); } if (document.isAutoloadedWithPotentialCompletions()) { const result = findMissingCompletionsWithExistingArgparse(document); fishSymbols.push(...result); } if (completionSymbols.length === 0 && fishSymbols.length === 0) { logger_1.logger.debug(`No missing argparse flags found in document: ${document.uri}`); return []; } if (completionSymbols.length > 0) { for (const completionGroup of completionSymbols) { const diag = createCompletionDiagnostic(completionGroup, analyze_1.analyzer.getFlatDocumentSymbols(document.uri)); if (diag) { const toAdd = diag.filter(d => !diagnostics.some(existing => (0, code_action_handler_1.equalDiagnostics)(existing, d))); diagnostics.push(...toAdd); } } } if (fishSymbols.length > 0) { for (const symbol of fishSymbols) { const diag = createArgparseDiagnostic(symbol, document); if (diag) { const toAdd = diag.filter(d => !diagnostics.some(existing => (0, code_action_handler_1.equalDiagnostics)(existing, d))); diagnostics.push(...toAdd); } } } return diagnostics; } function findMissingArgparseFlagsWithExistingCompletions(document) { const missingCompletions = []; const symbols = analyze_1.analyzer.getFlatDocumentSymbols(document.uri); const globalSymbols = symbols.filter(s => s.isGlobal() && s.isFunction()); const argparseSymbols = (0, flatten_1.flattenNested)(...globalSymbols).filter(s => s.fishKind === 'ARGPARSE'); for (const symbol of argparseSymbols) { const completionLocations = analyze_1.analyzer.getImplementation(document, symbol.toPosition()) .filter(loc => !symbol.equalsLocation(loc)); if (completionLocations.length === 0) continue; for (const location of completionLocations) { const cSymbols = analyze_1.analyzer .getFlatCompletionSymbols(location.uri) .filter(s => s.isNonEmpty()); if (cSymbols.length === 0) continue; const grouped = (0, complete_1.groupCompletionSymbolsTogether)(...cSymbols); const result = (0, complete_1.getGroupedCompletionSymbolsAsArgparse)(grouped, argparseSymbols); if (result.length > 0) { missingCompletions.push(...result); } } } return missingCompletions; } function findMissingCompletionsWithExistingArgparse(document) { const missingCompletions = []; const completionSymbols = analyze_1.analyzer.getFlatCompletionSymbols(document.uri).filter(s => s.isNonEmpty()); const implementationLocations = []; completionSymbols.forEach(s => { const pos = s.toPosition(); if (!pos) return; const results = analyze_1.analyzer.getImplementation(document, pos) .filter(loc => !Locations.Location.equals(loc, s.toLocation())); if (results.length === 0) return; implementationLocations.push(...results); }); const grouped = (0, complete_1.groupCompletionSymbolsTogether)(...completionSymbols); if (grouped.length === 0) return missingCompletions; for (const location of implementationLocations) { const cSymbols = analyze_1.analyzer.getFlatDocumentSymbols(location.uri) .filter(s => s.fishKind === 'ARGPARSE'); if (cSymbols.length === 0) continue; for (const symbol of cSymbols) { if (grouped.some(group => group.some(s => s.equalsArgparse(symbol)))) { continue; } missingCompletions.push(symbol); } } return missingCompletions; } function createCompletionDiagnostic(completionGroup, symbols) { const diagnostics = []; const focusedSymbol = symbols.find(s => s.isFunction() && s.isGlobal() && completionGroup.every(c => c.commandName === s.name)); if (!focusedSymbol) { logger_1.logger.warning(`No focused location found for completion group: ${completionGroup.map(c => c.text).join(', ')}`, 'HERE'); return null; } const hasArgparse = (0, flatten_1.flattenNested)(focusedSymbol).find(l => l.fishKind === 'ARGPARSE'); const focusedNode = hasArgparse ? hasArgparse.node.firstNamedChild : focusedSymbol.node.firstChild?.nextSibling; if (!focusedNode) { logger_1.logger.warning(`No focused node found for completion group: ${completionGroup.map(c => c.text).join(', ')}`); return null; } const joinedGroup = completionGroup.map(c => c.toArgparseOpt()).join('/'); const firstCompletion = completionGroup.find(c => c.isNonEmpty()); const firstCompletionDoc = firstCompletion.document; const prettyPath = (0, translation_1.uriToReadablePath)(firstCompletionDoc.uri); diagnostics.push({ message: `Add missing \`argparse ${joinedGroup}\` from completion in '${prettyPath}'`, severity: 1, source: 'fish-lsp', code: 4008, range: (0, tree_sitter_1.getRange)(focusedNode), data: { node: focusedNode, }, }); const joinedGroupUsage = completionGroup.map((item, idx) => { if (idx === 0) { return item.toUsage(); } return item.toFlag(); }).join('/'); diagnostics.push({ message: `Remove the unused completion \`${joinedGroupUsage}\` in '${prettyPath}'`, severity: 1, source: 'fish-lsp', code: 4009, range: (0, tree_sitter_1.getRange)(firstCompletion.parent), data: { node: firstCompletion.parent, }, }); return diagnostics; } function createArgparseDiagnostic(symbol, document) { const diagnostics = []; const focusedNode = symbol.focusedNode; if (!focusedNode) { logger_1.logger.warning(`No focused node found for symbol: ${symbol.name}`); return []; } const prettyPath = (0, translation_1.uriToReadablePath)(document.uri); diagnostics.push({ message: `remove unused \`argparse ${symbol.argparseFlagName}\` in '${prettyPath}'`, severity: 1, source: 'fish-lsp', code: 4009, range: (0, tree_sitter_1.getRange)(focusedNode), data: { type: 'argparse removal', node: focusedNode, }, }); const completionLocation = analyze_1.analyzer.getImplementation(document, symbol.toPosition()) .find(loc => !Locations.Location.equals(loc, symbol.toLocation())); if (completionLocation) { const prettyCompletionPath = (0, translation_1.uriToReadablePath)(completionLocation.uri); diagnostics.push({ message: `Add missing \`${symbol.parent.name + ' ' + symbol.argparseFlag}\` completion in '${prettyCompletionPath}'`, severity: 1, source: 'fish-lsp', code: 4008, range: (0, tree_sitter_1.getRange)(focusedNode), data: { type: 'argparse addition', node: focusedNode, }, }); } return diagnostics; }