UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

357 lines (356 loc) 16.6 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.commandBin = void 0; exports.startServer = startServer; //'use strict' const commander_cli_subcommands_1 = require("./utils/commander-cli-subcommands"); const node_1 = require("vscode-languageserver/node"); const commander_1 = require("commander"); const server_1 = __importDefault(require("./server")); const get_lsp_completions_1 = require("./utils/get-lsp-completions"); const logger_1 = require("./logger"); const config_1 = require("./config"); function startServer() { // Create a connection for the server. // The connection uses stdin/stdout for communication. const connection = (0, node_1.createConnection)(new node_1.StreamMessageReader(process.stdin), new node_1.StreamMessageWriter(process.stdout)); connection.onInitialize(async (params) => { // connection.console.log(`Initialized server FISH-LSP with ${JSON.stringify(params, null, 2)}`); const server = await server_1.default.create(connection, params); server.register(connection); return server.initialize(params); }); connection.listen(); (0, logger_1.createServerLogger)(config_1.config.fish_lsp_logfile, true, connection.console); logger_1.logger.log('Starting FISH-LSP server'); logger_1.logger.log('Server started with the following handlers:', config_1.configHandlers); logger_1.logger.log('Server started with the following config:', config_1.config); } async function timeOperation(operation, label) { const start = performance.now(); try { const result = await operation(); const end = performance.now(); const duration = end - start; (0, logger_1.logToStdoutJoined)(`${label}:`.padEnd(55), `${duration.toFixed(2)}ms`.padStart(10)); return result; } catch (error) { const end = performance.now(); const duration = end - start; (0, logger_1.logToStdout)(`${label} failed after ${duration.toFixed(2)}ms`); throw error; } } async function timeServerStartup() { // define a local server instance let server; // 1. Time server creation and startup await timeOperation(async () => { const connection = (0, node_1.createConnection)(new node_1.StreamMessageReader(process.stdin), new node_1.StreamMessageWriter(process.stdout)); // connection.console.log('Starting FISH-LSP server'); const startupParams = { processId: process.pid, rootUri: process.cwd(), capabilities: {}, }; server = await server_1.default.create(connection, startupParams); server.register(connection); server.initialize(startupParams); connection.listen(); return server; }, 'Server Start Time'); // 2. Time server initialization and background analysis await timeOperation(async () => { await server?.startBackgroundAnalysis(); }, 'Background Analysis Time'); // 3. Log the number of files indexed (0, logger_1.logToStdoutJoined)('Total Files Indexed: '.padEnd(55), `${server?.analyzer.amountIndexed} files`.padStart(10)); // 4. Log the directories indexed const all_indexed = config_1.config.fish_lsp_all_indexed_paths; (0, logger_1.logToStdoutJoined)("Indexed Files in '$fish_lsp_all_indexed_paths':".padEnd(55), `${all_indexed.length} paths`.padStart(10)); const maxItemLen = all_indexed.reduce((max, item) => Math.max(max, item.length), 0); const startStr = ' '.repeat(3); config_1.config.fish_lsp_all_indexed_paths.forEach((item, idx) => (0, logger_1.logToStdoutJoined)(`${startStr}$fish_lsp_all_indexed_paths[${idx + 1}] `.padEnd(64 - maxItemLen), `|${item}|`.padStart(maxItemLen + 2))); } /** * creates local 'commandBin' used for commander.js */ const createFishLspBin = () => { const bin = new commander_1.Command('fish-lsp') .description(`Description:\n${commander_cli_subcommands_1.FishLspHelp.description || 'fish-lsp command output'}`) .helpOption('-h, --help', 'show the relevant help info. Use `--help-all` for comprehensive documentation of all commands and flags. Other `--help-*` flags are also available.') .version(commander_cli_subcommands_1.PackageVersion, '-v, --version', 'output the version number') .enablePositionalOptions(true) .configureHelp({ showGlobalOptions: false, commandUsage: (_) => commander_cli_subcommands_1.FishLspHelp.usage, }) .showSuggestionAfterError(true) .showHelpAfterError(true) .addHelpText('after', commander_cli_subcommands_1.FishLspHelp.after); return bin; }; // start adding options to the command exports.commandBin = createFishLspBin(); // hidden global options exports.commandBin .addOption(new commander_1.Option('--help-man', 'show special manpage output').hideHelp(true)) .addOption(new commander_1.Option('--help-all', 'show all help info').hideHelp(true)) .addOption(new commander_1.Option('--help-short', 'show mini help info').hideHelp(true)) .action(opt => { if (opt.helpMan) { const { path: _path, content } = (0, commander_cli_subcommands_1.FishLspManPage)(); (0, logger_1.logToStdout)(content.join('\n').trim()); } else if (opt.helpAll) { const globalOpts = exports.commandBin.options.concat(new commander_1.Option('-h, --help', 'show help')); const subCommands = exports.commandBin.commands.map((cmd) => { return [ ` ${cmd.name()} ${cmd.usage()}\t${cmd.summary()}`, cmd.options.map(o => ` ${o.flags}\t\t${o.description}`).join('\n'), '' ].join('\n'); }); (0, logger_1.logToStdout)(['NAME:', 'fish-lsp - an lsp for the fish shell language', '', 'USAGE: ', commander_cli_subcommands_1.FishLspHelp.beforeAll, '', 'DESCRIPTION:', ' ' + exports.commandBin.description().split('\n').slice(1).join('\n').trim(), '', 'OPTIONS:', ' ' + globalOpts.map(o => ' ' + o.flags + '\t' + o.description).join('\n').trim(), '', 'SUBCOMMANDS:', subCommands.join('\n'), '', 'EXAMPLES:', commander_cli_subcommands_1.FishLspHelp.after.split('\n').slice(2).join('\n'), ].join('\n').trim()); } else if (opt.helpShort) { (0, logger_1.logToStdout)([ 'Usage: fish-lsp ', exports.commandBin.usage().split('\n').slice(0, 1), '', exports.commandBin.description(), ].join('\n')); } return; }); // START exports.commandBin.command('start [TOGGLE]') .summary('subcmd to start the lsp using stdin/stdout') .description('start the language server for a connection to a client') .option('--dump', 'stop lsp & show the startup options being read') .option('--enable <string...>', 'enable the startup option') .option('--disable <string...>', 'disable the startup option') .addHelpText('afterAll', [ '', 'STRINGS FOR \'--enable/--disable\':', `(${config_1.validHandlers.map((opt, index) => { return index < config_1.validHandlers.length - 1 && index > 0 && index % 5 === 0 ? `${opt},\n` : index < config_1.validHandlers.length - 1 ? `${opt},` : opt; }).join(' ')})`, '', 'Examples:', '\tfish-lsp start --disable hover # only disable the hover feature', '\tfish-lsp start --disable complete logging index hover --show', '\tfish-lsp start --enable --disable logging complete codeAction', ].join('\n')) .action(() => { // NOTE: `config` is a global object, already initialized. Here, we are updating its // values passed from the shell environment, and then possibly overriding them with // the command line args. // use the `config` object's shell environment values to update the handlers (0, config_1.updateHandlers)(config_1.config.fish_lsp_enabled_handlers, true); (0, config_1.updateHandlers)(config_1.config.fish_lsp_disabled_handlers, false); // override `configHandlers` with command line args const { enabled, disabled, dumpCmd } = (0, commander_cli_subcommands_1.accumulateStartupOptions)(exports.commandBin.args); (0, config_1.updateHandlers)(enabled, true); (0, config_1.updateHandlers)(disabled, false); config_1.Config.fixPopups(enabled, disabled); // Dump the configHandlers, if requested from the command line. This stops the server. if (dumpCmd) { (0, logger_1.log)(JSON.stringify({ handlers: config_1.configHandlers }, null, 2)); (0, logger_1.log)(JSON.stringify({ config: config_1.config }, null, 2)); process.exit(0); } /* config needs to be used in `startServer()` below */ startServer(); }); // LOGGER exports.commandBin.command('logger') .summary('test the logger by displaying it') .option('-s, --show', 'show the logger and don\'t edit it') .option('-c, --clear', 'clear the logger') .option('-d, --date', 'write the date') .option('-q, --quiet', 'silence logging') .option('--config', 'show the logger config') .action(args => { const logger = (0, logger_1.createServerLogger)(config_1.config.fish_lsp_logfile, false); const objArgs = Object.getOwnPropertyNames(args); const argsQueue = objArgs; let currentArg = ''; while (argsQueue.length !== 0) { currentArg = argsQueue.shift() || ''; if (currentArg === 'clear') logger.clearLogFile(); if (currentArg === 'quiet') logger.toggleSilence(); if (currentArg === 'date') logger.log((0, commander_cli_subcommands_1.getBuildTimeString)()); if (currentArg === 'config') (0, logger_1.logToStdout)(JSON.stringify(logger.getLoggingOpts())); if (currentArg === 'show') break; } if (!args.show) return; logger.showLogfileText(); return; }); // INFO exports.commandBin.command('info') .summary('show the build info of fish-lsp') .option('--bin', 'show the path of the fish-lsp executable') .option('--repo', 'show the path of the entire fish-lsp repo') .option('--time', 'show the path of the entire fish-lsp repo') .option('--lsp-version', 'show the lsp version') .option('--capabilities', 'show the lsp capabilities') .option('--man-file', 'show the man file path') .option('--logs-file', 'show the logs.txt file path') .option('--more', 'show the build time of the fish-lsp executable') .option('--time-startup', 'time the startup of the fish-lsp executable') .action(async (args) => { const capabilities = (0, commander_cli_subcommands_1.BuildCapabilityString)() .split('\n') .map(line => ` ${line}`).join('\n'); if (args.timeStartup) { await timeServerStartup(); process.exit(0); } if (args.bin || args.repo) { const logPath = args.bin ? commander_cli_subcommands_1.PathObj.bin : commander_cli_subcommands_1.PathObj.repo; const wpath = args.bin ? 'BINARY' : 'REPOSITORY'; (0, logger_1.logToStdout)(wpath + ' ' + (0, commander_cli_subcommands_1.smallFishLogo)()); (0, logger_1.logToStdout)(logPath); process.exit(0); } if (args.time) { (0, logger_1.logToStdout)(`Build Time: ${(0, commander_cli_subcommands_1.getBuildTimeString)()}`); process.exit(0); } if (args.capabilities) { (0, logger_1.logToStdout)(`Capabilities:\n${capabilities}`); process.exit(0); } if (args.lspVersion) { (0, logger_1.logToStdout)(`LSP Version: ${commander_cli_subcommands_1.PackageLspVersion}`); process.exit(0); } if (args.manFile) { (0, logger_1.logToStdout)(commander_cli_subcommands_1.PathObj.manFile); process.exit(0); } if (args.logsFile) { (0, logger_1.logToStdout)(config_1.config.fish_lsp_logfile); process.exit(0); } (0, logger_1.logToStdout)(`Repository: ${commander_cli_subcommands_1.PathObj.repo}`); (0, logger_1.logToStdout)(`Version: ${commander_cli_subcommands_1.PackageVersion}`); (0, logger_1.logToStdout)(`Build Time: ${(0, commander_cli_subcommands_1.getBuildTimeString)()}`); (0, logger_1.logToStdout)(`Install Type: ${(0, commander_cli_subcommands_1.isPkgBinary)() ? 'standalone executable' : 'local build'}`); (0, logger_1.logToStdout)(`Node Version: ${process.version}`); (0, logger_1.logToStdout)(`LSP Version: ${commander_cli_subcommands_1.PackageLspVersion}`); (0, logger_1.logToStdout)(`Binary File: ${commander_cli_subcommands_1.PathObj.bin}`); (0, logger_1.logToStdout)(`Man File: ${commander_cli_subcommands_1.PathObj.manFile}`); (0, logger_1.logToStdout)(`Log File: ${config_1.config.fish_lsp_logfile}`); (0, logger_1.logToStdout)('CAPABILITIES:'); (0, logger_1.logToStdout)(capabilities); process.exit(0); }); // URL exports.commandBin.command('url') .summary('show a helpful url related to the fish-lsp') .description('show the related url to the fish-lsp') .option('--repo, --git', 'show the github repo') .option('--npm', 'show the npm package url') .option('--homepage', 'show the homepage') .option('--contributions', 'show the contributions url') .option('--wiki', 'show the github wiki') .option('--issues, --report', 'show the issues page') .option('--discussions', 'show the discussions page') .option('--clients-repo', 'show the clients configuration repo') .option('--sources', 'show a list of helpful sources') .action(args => { const amount = Object.keys(args).length; if (amount === 0) (0, logger_1.logToStdout)('https://fish-lsp.dev'); Object.keys(args).forEach(key => (0, logger_1.logToStdout)(commander_cli_subcommands_1.SourcesDict[key]?.toString() || '')); process.exit(0); }); // COMPLETE exports.commandBin.command('complete') .summary('generate completions file for ~/.config/fish/completions') .option('--names', 'show the feature names of the completions') .option('--toggles', 'show the feature names of the completions') .option('--fish', 'show fish script') .option('--features', 'show features') .description('copy completions output to fish-lsp completions file') .action(args => { if (args.names) { exports.commandBin.commands.forEach(cmd => (0, logger_1.logToStdout)(cmd.name() + '\t' + cmd.summary())); process.exit(0); } else if (args.toggles) { exports.commandBin.commands.forEach(cmd => { (0, logger_1.logToStdout)(cmd.name() + '\t' + cmd.summary()); Object.entries(cmd.opts()).forEach(opt => (0, logger_1.logToStdout)('--' + opt[0])); }); process.exit(0); } else if (args.fish) { (0, logger_1.logToStdout)((0, get_lsp_completions_1.buildFishLspCompletions)(exports.commandBin)); process.exit(0); } else if (args.features) { Object.entries(config_1.configHandlers).forEach((name) => (0, logger_1.logToStdout)(name.toString())); process.exit(0); } (0, logger_1.logToStdout)((0, get_lsp_completions_1.buildFishLspCompletions)(exports.commandBin)); process.exit(0); }); // ENV exports.commandBin.command('env') .summary('generate fish shell env variables to be used by lsp') .description('generate fish-lsp env variables') .option('-c, --create', 'build initial fish-lsp env variables') .option('-s, --show', 'show the current fish-lsp env variables') .option('--no-comments', 'skip comments in output') .option('--no-global', 'use local env variables') .option('--no-local', 'do not use local scope for variables') .option('--no-export', 'don\'t export the variables') .action(args => { if (args.show) { (0, config_1.showJsonSchemaShellScript)(args.comments, args.global, args.local, args.export); process.exit(0); } (0, config_1.generateJsonSchemaShellScript)(args.comments, args.global, args.local, args.export); }); /** * ADD HELP MESSAGE WHEN NO SUBCOMMAND IS GIVEN */ // if (process.argv.length <= 2 && process.env['NODE_TEST'] !== 'test') { // process.argv.push('--help') // } /** * PARSE THE SUBCOMMAND/OPTION */ exports.commandBin.parse();