UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

297 lines (296 loc) 12.8 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.ArparseOptions = void 0; exports.findArgparseOptions = findArgparseOptions; exports.findArgparseDefinitionNames = findArgparseDefinitionNames; exports.isArgparseVariableDefinitionName = isArgparseVariableDefinitionName; exports.convertNodeRangeWithPrecedingFlag = convertNodeRangeWithPrecedingFlag; exports.isGlobalArgparseDefinition = isGlobalArgparseDefinition; exports.getGlobalArgparseLocations = getGlobalArgparseLocations; exports.getArgparseDefinitionName = getArgparseDefinitionName; exports.isCompletionArgparseFlagWithCommandName = isCompletionArgparseFlagWithCommandName; exports.processArgparseCommand = processArgparseCommand; const node_types_1 = require("../utils/node-types"); const options_1 = require("./options"); const symbol_1 = require("./symbol"); const definition_scope_1 = require("../utils/definition-scope"); const tree_sitter_1 = require("../utils/tree-sitter"); const analyze_1 = require("../analyze"); const path_1 = __importStar(require("path")); const file_operations_1 = require("../utils/file-operations"); const translation_1 = require("../utils/translation"); const workspace_manager_1 = require("../utils/workspace-manager"); const logger_1 = require("../logger"); exports.ArparseOptions = [ options_1.Option.create('-n', '--name').withValue(), options_1.Option.create('-x', '--exclusive').withValue(), options_1.Option.create('-N', '--min-args').withValue(), options_1.Option.create('-X', '--max-args').withValue(), options_1.Option.create('-i', '--ignore-unknown'), options_1.Option.create('-s', '--stop-nonopt'), options_1.Option.create('-h', '--help'), ]; const isBefore = (a, b) => a.startIndex < b.startIndex; function findArgparseOptions(node) { if ((0, node_types_1.isCommandWithName)(node, 'argparse')) return undefined; const endChar = node.children.find(node => (0, node_types_1.isEndStdinCharacter)(node)); if (!endChar) return undefined; const nodes = node.childrenForFieldName('argument') .filter(n => !(0, node_types_1.isEscapeSequence)(n) && isBefore(n, endChar)) .filter(n => !(0, node_types_1.isVariableExpansion)(n) || n.type !== 'variable_name'); return (0, options_1.findOptions)(nodes, exports.ArparseOptions); } function findArgparseDefinitionNames(node) { if (!node || !(0, node_types_1.isCommandWithName)(node, 'argparse')) return []; const endChar = node.children.find(node => (0, node_types_1.isEndStdinCharacter)(node)); if (!endChar) return []; const nodes = node.childrenForFieldName('argument') .filter(n => !(0, node_types_1.isEscapeSequence)(n) && isBefore(n, endChar)) .filter(n => !(0, node_types_1.isVariableExpansion)(n) || n.type !== 'variable_name'); const { remaining } = (0, options_1.findOptions)(nodes, exports.ArparseOptions); return remaining; } function isArgparseVariableDefinitionName(node) { if (!node.parent || !(0, node_types_1.isCommandWithName)(node.parent, 'argparse')) return false; const children = findArgparseDefinitionNames(node.parent); return !!children.some(n => n.equals(node)); } function convertNodeRangeWithPrecedingFlag(node) { const range = (0, tree_sitter_1.getRange)(node); if (node.text.startsWith('_flag_')) { range.start = { line: range.start.line, character: range.start.character + 6, }; } return range; } function isGlobalArgparseDefinition(document, symbol) { if (!symbol.isArgparse() || !symbol.isFunction()) return false; let parent = symbol.parent; if (symbol.isFunction() && symbol.isGlobal()) { parent = symbol; } if (parent && parent?.isFunction()) { const functionName = parent.name; if (document.getAutoLoadName() !== functionName) { return false; } const filepath = document.getFilePath(); const workspaceDirectory = workspace_manager_1.workspaceManager.findContainingWorkspace(document.uri)?.path || (0, path_1.dirname)((0, path_1.dirname)(filepath)); const completionFile = document.getAutoloadType() === 'conf.d' || document.getAutoloadType() === 'config' ? document.getFilePath() : path_1.default.join(workspaceDirectory, 'completions', document.getFilename()); if (process.env.NODE_ENV !== 'test' && !file_operations_1.SyncFileHelper.isFile(completionFile)) { return false; } return analyze_1.analyzer.getFlatCompletionSymbols((0, translation_1.pathToUri)(completionFile)).length > 0; } return false; } function getGlobalArgparseLocations(document, symbol) { if (isGlobalArgparseDefinition(document, symbol)) { const filepath = (0, translation_1.uriToPath)(document.uri); const workspaceDirectory = workspace_manager_1.workspaceManager.findContainingWorkspace(document.uri)?.path || (0, path_1.dirname)((0, path_1.dirname)(filepath)); logger_1.logger.log(`Getting global argparse locations for symbol: ${symbol.name} in file: ${filepath}`, { filepath, workspaceDirectory, }); const completionFile = document.getAutoloadType() === 'conf.d' || document.getAutoloadType() === 'config' ? document.getFilePath() : path_1.default.join(workspaceDirectory, 'completions', document.getFilename()); if (process.env.NODE_ENV !== 'test' && !file_operations_1.SyncFileHelper.isFile(completionFile)) { logger_1.logger.debug({ env: 'test', message: `Completion file does not exist: ${completionFile}`, }); return []; } logger_1.logger.debug({ message: `Getting global argparse locations for symbol: ${symbol.name} in file: ${completionFile}`, }); const completionLocations = analyze_1.analyzer .getFlatCompletionSymbols((0, translation_1.pathToUri)(completionFile)) .filter(s => s.isNonEmpty()) .filter(s => s.equalsArgparse(symbol) || s.equalsCommand(symbol)) .map(s => s.toLocation()); logger_1.logger.log(`Found ${completionLocations.length} global argparse locations for symbol: ${symbol.name}`, 'HERE'); return completionLocations; } logger_1.logger.warning(`no global argparse locations found for symbol: ${symbol.name}`, 'HERE'); return []; } function getArgparseScopeModifier(document, _node) { const autoloadType = document.getAutoloadType(); switch (autoloadType) { case 'conf.d': case 'config': case 'functions': return 'local'; default: return 'local'; } } function getArgparseDefinitionName(node) { if (!node.parent || !(0, node_types_1.isCommandWithName)(node.parent, 'complete')) return ''; if (node.text) { const text = `_flag_${node.text}`; return text.replace(/-/, '_'); } return ''; } function isCompletionArgparseFlagWithCommandName(node, commandName, flagName, opts) { if (!node?.parent || !(0, node_types_1.isCommandWithName)(node.parent, 'complete')) return false; const parent = node.parent; if (opts?.discardIfContainsOptions) { for (const option of opts.discardIfContainsOptions) { if (parent.children.some(c => option.matches(c))) { return false; } } } let completeCmdName = !!parent.children.find(c => c.previousSibling && (0, options_1.isMatchingOption)(c.previousSibling, options_1.Option.create('-c', '--command')) && c.text === commandName); if (opts?.noCommandNameAllowed && !completeCmdName) { completeCmdName = !parent.children.some(c => c.previousSibling && (0, options_1.isMatchingOption)(c.previousSibling, options_1.Option.create('-c', '--command'))); } const option = flagName.length === 1 ? options_1.Option.create('-s', '--short') : options_1.Option.create('-l', '--long'); const completeFlagName = !!(node.previousSibling && option.equals(node.previousSibling) && node.text === flagName); return completeCmdName && completeFlagName; } function createSelectionRange(node, flags, flag, idx) { const range = (0, tree_sitter_1.getRange)(node); const text = node.text; const shortenedFlag = flag.replace(/^_flag_/, ''); if (flags.length === 2 && idx === 0) { if ((0, node_types_1.isString)(node)) { range.start = { line: range.start.line, character: range.start.character + 1, }; range.end = { line: range.start.line, character: range.start.character - 1, }; } return { start: range.start, end: { line: range.start.line, character: range.start.character + shortenedFlag.length, }, }; } else if (flags.length === 2 && idx === 1) { return { start: { line: range.start.line, character: range.start.character + text.indexOf('/') + 1, }, end: { line: range.end.line, character: range.start.character + text.indexOf('/') + 1 + shortenedFlag.length, }, }; } else if (flags.length === 1) { if ((0, node_types_1.isString)(node)) { return { start: { line: range.start.line, character: range.start.character + 1, }, end: { line: range.start.line, character: range.start.character + 1 + shortenedFlag.length, }, }; } else { return (0, tree_sitter_1.getRange)(node); } } return range; } function splitSlash(str) { const results = str.split('/') .map(s => s.trim().replace(/-/g, '_')); const maxResults = results.length < 2 ? results.length : 2; return results.slice(0, maxResults); } function getNames(flags) { return flags.map(flag => { return `_flag_${flag}`; }); } function processArgparseCommand(document, node, children = []) { const result = []; const modifier = getArgparseScopeModifier(document, node); const focuesedNodes = findArgparseDefinitionNames(node); for (const n of focuesedNodes) { let flagNames = n.text; if (!flagNames) continue; if ((0, node_types_1.isString)(n)) flagNames = flagNames.slice(1, -1); if (flagNames.includes('=')) flagNames = flagNames.slice(0, flagNames.indexOf('=')); const seenFlags = splitSlash(flagNames); const names = getNames(seenFlags); const flags = names.map((flagName, idx) => { const selectedRange = createSelectionRange(n, seenFlags, flagName, idx); return symbol_1.FishSymbol.fromObject({ name: flagName, node: node, focusedNode: n, fishKind: 'ARGPARSE', document: document, uri: document.uri, detail: n.text, range: (0, tree_sitter_1.getRange)(n), selectionRange: selectedRange, scope: definition_scope_1.DefinitionScope.create(node.parent, modifier), children, }).addAliasedNames(...names); }); result.push(...flags); } return result; }