fish-lsp
Version:
LSP implementation for fish/fish-shell
357 lines (356 loc) • 16.6 kB
JavaScript
;
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();