UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

326 lines (325 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HoverFromCompletion = void 0; exports.enrichToMarkdown = enrichToMarkdown; exports.enrichToCodeBlockMarkdown = enrichToCodeBlockMarkdown; exports.enrichWildcard = enrichWildcard; exports.enrichCommandArg = enrichCommandArg; exports.enrichCommandWithFlags = enrichCommandWithFlags; exports.handleSourceArgumentHover = handleSourceArgumentHover; exports.enrichToPlainText = enrichToPlainText; exports.documentationHoverProvider = documentationHoverProvider; exports.documentationHoverProviderForBuiltIns = documentationHoverProviderForBuiltIns; exports.documentationHoverCommandArg = documentationHoverCommandArg; exports.forwardSubCommandCollect = forwardSubCommandCollect; exports.forwardArgCommandCollect = forwardArgCommandCollect; const node_1 = require("vscode-languageserver-protocol/node"); const exec_1 = require("./utils/exec"); const tree_sitter_1 = require("./utils/tree-sitter"); const markdown_builder_1 = require("./utils/markdown-builder"); const source_1 = require("./parsing/source"); function enrichToMarkdown(doc) { return { kind: node_1.MarkupKind.Markdown, value: [ doc, ].join(), }; } function enrichToCodeBlockMarkdown(doc, filetype = 'fish') { return { kind: node_1.MarkupKind.Markdown, value: [ '```' + filetype, doc.trim(), '```', ].join('\n'), }; } function enrichWildcard(label, documentation, examples) { const exampleStr = ['---']; for (const [cmd, desc] of examples) { exampleStr.push(`__${cmd}__ - ${desc}`); } return { kind: node_1.MarkupKind.Markdown, value: [ `_${label}_ ${documentation}`, '---', exampleStr.join('\n'), ].join('\n'), }; } function enrichCommandArg(doc) { const [_first, ...after] = doc.split('\t'); const first = _first?.trim() || ''; const second = after?.join('\t').trim() || ''; const arg = '__' + first + '__'; const desc = '_' + second + '_'; const enrichedDoc = [ arg, desc, ].join(' '); return enrichToMarkdown(enrichedDoc); } function enrichCommandWithFlags(command, description, flags) { const title = description ? `(${markdown_builder_1.md.bold(command)}) ${description}` : markdown_builder_1.md.bold(command); const flagLines = flags.map(line => line.split('\t')) .map(line => `${markdown_builder_1.md.bold(line.at(0))} ${markdown_builder_1.md.italic(line.slice(1).join(' '))}`); const result = []; result.push(title); if (flags.length > 0) { result.push(markdown_builder_1.md.separator()); result.push(flagLines.join(markdown_builder_1.md.newline())); } return enrichToMarkdown(result.join(markdown_builder_1.md.newline())); } function handleSourceArgumentHover(analyzer, current) { const sourceExpanded = (0, source_1.getExpandedSourcedFilenameNode)(current); if (!sourceExpanded) return null; const sourceDoc = analyzer.getDocumentFromPath(sourceExpanded); if (!sourceDoc) { analyzer.analyzePath(sourceExpanded); } return { contents: enrichToMarkdown([ `${markdown_builder_1.md.boldItalic('SOURCE')} - ${markdown_builder_1.md.italic('https://fishshell.com/docs/current/cmds/source.html')}`, markdown_builder_1.md.separator(), `${markdown_builder_1.md.codeBlock('fish', [ 'source ' + current.text, sourceExpanded && sourceExpanded !== current.text ? `# source ${sourceExpanded}` : undefined, ].filter(Boolean).join('\n'))}`, markdown_builder_1.md.separator(), markdown_builder_1.md.codeBlock('fish', sourceDoc.getText()), ].join(markdown_builder_1.md.newline())), }; } function enrichToPlainText(doc) { return { kind: node_1.MarkupKind.PlainText, value: doc.trim(), }; } async function documentationHoverProvider(cmd) { const cmdDocs = await (0, exec_1.execCommandDocs)(cmd); const cmdType = await (0, exec_1.execCommandType)(cmd); if (!cmdDocs) { return null; } else { return { contents: cmdType === 'command' ? enrichToCodeBlockMarkdown(cmdDocs, 'man') : enrichToCodeBlockMarkdown(cmdDocs, 'fish'), }; } } async function documentationHoverProviderForBuiltIns(cmd) { const cmdDocs = await (0, exec_1.execCommandDocs)(cmd); if (!cmdDocs) { return null; } const splitDocs = cmdDocs.split('\n'); const startIndex = splitDocs.findIndex((line) => line.trim() === 'NAME'); return { contents: { kind: node_1.MarkupKind.Markdown, value: [ `__${cmd.toUpperCase()}__ - _https://fishshell.com/docs/current/cmds/${cmd.trim()}.html_`, '___', '```man', splitDocs.slice(startIndex).join('\n'), '```', ].join('\n'), }, }; } function commandStringHelper(cmd) { const cmdArray = cmd.split(' ', 1); return cmdArray.length > 1 ? '___' + cmdArray[0] + '___' + ' ' + cmdArray[1] : '___' + cmdArray[0] + '___'; } function documentationHoverCommandArg(root, cmp) { let text = ''; const argsArray = [...cmp.args.keys()]; for (const node of (0, tree_sitter_1.getChildNodes)(root)) { const nodeText = (0, tree_sitter_1.getNodeText)(node); if (nodeText.startsWith('-') && argsArray.includes(nodeText)) { text += '\n' + '_' + nodeText + '_ ' + cmp.args.get(nodeText); } } const cmd = commandStringHelper(cmp.command.trim()); return { contents: enrichToMarkdown([ cmd, '---', text.trim(), ].join('\n')), }; } function forwardSubCommandCollect(rootNode) { const stringToComplete = []; for (const curr of rootNode.children) { if (curr.text.startsWith('-') && curr.text.startsWith('$')) { break; } else { stringToComplete.push(curr.text); } } return stringToComplete; } function forwardArgCommandCollect(rootNode) { const stringToComplete = []; const _currentNode = rootNode.children; for (const curr of rootNode.children) { if (curr.text.startsWith('-') && curr.text.startsWith('$')) { stringToComplete.push(curr.text); } else { continue; } } return stringToComplete; } function getFlagString(arr) { return '__' + arr[0] + '__' + ' ' + arr[1] + '\n'; } class HoverFromCompletion { currentNode; commandNode; commandString = ''; entireCommandString = ''; completions = []; oldOptions = false; flagsGiven = []; constructor(commandNode, currentNode) { this.currentNode = currentNode; this.commandNode = commandNode; this.commandString = commandNode.child(0)?.text || ''; this.entireCommandString = commandNode.text || ''; this.flagsGiven = this.entireCommandString .split(' ').slice(1) .filter(flag => flag.startsWith('-')) .map(flag => flag.split('=')[0]) || []; } async checkForSubCommands() { const spaceCmps = await (0, exec_1.execCompleteSpace)(this.commandString); if (spaceCmps.length === 0) { return this.commandString; } const cmdArr = this.commandNode.text.split(' ').slice(1); let i = 0; while (i < cmdArr.length) { const argStr = cmdArr[i].trim(); if (!argStr.startsWith('-') && spaceCmps.includes(argStr)) { this.commandString += ' ' + argStr.toString(); } else if (argStr.includes('-')) { break; } i++; } return this.commandString; } isSubCommand() { const currentNodeText = this.currentNode.text; if (currentNodeText.startsWith('-') || currentNodeText.startsWith("'") || currentNodeText.startsWith('"')) { return false; } const cmdArr = this.commandString.split(' '); if (cmdArr.length > 1) { return cmdArr.includes(currentNodeText); } return false; } hasOldStyleFlags() { for (const cmpArr of this.completions) { if (cmpArr[0]?.startsWith('--')) { continue; } else if (cmpArr[0]?.startsWith('-') && cmpArr[0]?.length > 2) { return true; } } return false; } reparseFlags() { const shortFlagsHandled = []; for (const flag of this.flagsGiven) { if (flag.startsWith('--')) { shortFlagsHandled.push(flag); } else if (flag.startsWith('-') && flag.length > 2) { const splitShortFlags = flag.split('').slice(1).map(str => '-' + str); shortFlagsHandled.push(...splitShortFlags); } } return shortFlagsHandled; } async buildCompletions() { this.commandString = await this.checkForSubCommands(); const preBuiltCompletions = await (0, exec_1.execCompleteCmdArgs)(this.commandString); for (const cmp of preBuiltCompletions) { this.completions.push(cmp.split('\t')); } return this.completions; } findCompletion(flag) { for (const flagArr of this.completions) { if (flagArr[0] === flag) { return flagArr; } } return null; } async checkForHoverDoc() { const cmd = await (0, exec_1.documentCommandDescription)(this.commandString); const cmdArr = cmd.trim().split(' '); const cmdStrLen = this.commandString.split(' ').length; const boldText = '__' + cmdArr.slice(0, cmdStrLen).join(' ') + '__'; const otherText = ' ' + cmdArr.slice(cmdStrLen).join(' '); return boldText + otherText; } async generateForFlags() { let text = ''; this.completions = await this.buildCompletions(); this.oldOptions = this.hasOldStyleFlags(); const cmd = await this.checkForHoverDoc(); if (!this.oldOptions) { this.flagsGiven = this.reparseFlags(); } for (const flag of this.flagsGiven) { const found = this.findCompletion(flag); if (found) { text += getFlagString(found); } } return { contents: enrichToMarkdown([ cmd, '---', text.trim(), ].join('\n')), }; } async generateForSubcommand() { return await documentationHoverProvider(this.commandString); } async generate() { this.commandString = await this.checkForSubCommands(); if (this.isSubCommand()) { const output = await documentationHoverProvider(this.commandString); if (output) { return output; } } else { return await this.generateForFlags(); } return; } } exports.HoverFromCompletion = HoverFromCompletion;