fish-lsp
Version:
LSP implementation for fish/fish-shell
326 lines (325 loc) • 11.4 kB
JavaScript
"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;