UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

212 lines (211 loc) 9.46 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.connection = void 0; exports.createConnectionType = createConnectionType; exports.startServer = startServer; exports.timeOperation = timeOperation; exports.timeServerStartup = timeServerStartup; const path = __importStar(require("path")); const os = __importStar(require("os")); const net = __importStar(require("net")); const server_1 = __importDefault(require("../server")); const logger_1 = require("../logger"); const config_1 = require("../config"); const translation_1 = require("./translation"); const commander_cli_subcommands_1 = require("./commander-cli-subcommands"); const node_1 = require("vscode-languageserver/node"); const workspace_manager_1 = require("./workspace-manager"); const workspace_1 = require("./workspace"); const file_operations_1 = require("./file-operations"); function createConnectionType(opts) { if (opts.stdio) return 'stdio'; if (opts.nodeIpc) return 'node-ipc'; if (opts.socket) return 'socket'; return 'stdio'; } function createLspConnection(connectionType = 'stdio', options = {}) { let server; switch (connectionType) { case 'node-ipc': exports.connection = (0, node_1.createConnection)(node_1.ProposedFeatures.all); break; case 'socket': if (!options.port) { logger_1.logger.log('Socket connection requires a port number'); process.exit(1); } server = net.createServer((socket) => { exports.connection = (0, node_1.createConnection)(new node_1.StreamMessageReader(socket), new node_1.StreamMessageWriter(socket)); setupServerWithConnection(exports.connection); }); server.listen(options.port); logger_1.logger.log(`Server listening on port ${options.port}`); break; case 'stdio': default: exports.connection = (0, node_1.createConnection)(new node_1.StreamMessageReader(process.stdin), new node_1.StreamMessageWriter(process.stdout)); break; } } function setupServerWithConnection(connection) { connection.onInitialize(async (params) => { const { initializeResult } = await server_1.default.create(connection, params); return initializeResult; }); connection.listen(); (0, logger_1.createServerLogger)(config_1.config.fish_lsp_log_file, 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); } function startServer(connectionType = 'stdio', options = {}) { createLspConnection(connectionType, options); if (connectionType === 'socket' || !exports.connection) { return; } setupServerWithConnection(exports.connection); } async function timeOperation(operation, label) { const start = performance.now(); try { const result = await operation(); const end = performance.now(); const duration = end - start; logger_1.logger.logToStdoutJoined(`${label}:`.padEnd(75), `${duration.toFixed(2)}ms`.padStart(10)); return result; } catch (error) { const end = performance.now(); const duration = end - start; logger_1.logger.logToStdout(`${label} failed after ${duration.toFixed(2)}ms`); throw error; } } async function timeServerStartup() { let server; const title = 'fish-lsp'.padStart(43).padEnd(42); logger_1.logger.logToStdoutJoined(`${title}\n\n`, ' NOTE: a normal server instance will only start one of these workspaces\n\n', ' if you frequently find yourself working inside a relatively large \n', ' workspaces, please consider using the provided environment variable\n\n', '`set -gx fish_lsp_max_background_files`'.padStart(58), '\n'); logger_1.logger.logToStdout('-'.repeat(85)); await timeOperation(async () => { const connection = (0, node_1.createConnection)(new node_1.StreamMessageReader(process.stdin), new node_1.StreamMessageWriter(process.stdout)); const startUri = path.join(os.homedir(), '.config', 'fish'); const startupParams = { processId: process.pid, rootUri: (0, translation_1.pathToUri)(startUri), clientInfo: { name: 'fish-lsp info --time-startup', version: commander_cli_subcommands_1.PackageVersion, }, initializationOptions: { fish_lsp_all_indexed_paths: config_1.config.fish_lsp_all_indexed_paths, fish_lsp_max_background_files: config_1.config.fish_lsp_max_background_files, }, workspaceFolders: [], capabilities: { workspace: { workspaceFolders: true, }, }, }; ({ server } = await server_1.default.create(connection, startupParams)); connection.listen(); (0, logger_1.createServerLogger)(config_1.config.fish_lsp_log_file, connection.console); return server; }, 'Server Start Time'); let all = 0; const items = {}; workspace_manager_1.workspaceManager.clear(); for (const pathLike of config_1.config.fish_lsp_all_indexed_paths) { const fullPath = file_operations_1.SyncFileHelper.expandEnvVars(pathLike); const workspace = workspace_1.Workspace.syncCreateFromUri((0, translation_1.pathToUri)(fullPath)); if (!workspace) { logger_1.logger.logToStderr(`Failed to create workspace for path: ${pathLike}`); continue; } workspace_manager_1.workspaceManager.add(workspace); } await timeOperation(async () => { const result = await workspace_manager_1.workspaceManager.analyzePendingDocuments(); if (result) { all = result.totalDocuments; for (const [path, uris] of Object.entries(result.items)) { items[path] = uris.length; } } }, 'Background Analysis Time'); logger_1.logger.logToStdoutJoined('Total Files Indexed: '.padEnd(75), `${all} files`.padStart(10)); const all_indexed = config_1.config.fish_lsp_all_indexed_paths; logger_1.logger.logToStdoutJoined("Indexed Files in '$fish_lsp_all_indexed_paths':".padEnd(65), `${all_indexed.length} paths`.padStart(20)); Object.keys(items).forEach((item, idx) => { const text = item.length > 55 ? '...' + item.slice(item.length - 52) : item; const output = formatColumns([` [${idx + 1}]`, `| ${text} |`, `${items[item]?.toString() || 0} files`], [6, -59, -10], 85); logger_1.logger.logToStdout(output); }); logger_1.logger.logToStdout('-'.repeat(85)); } function formatColumns(text, widths, maxLen = 85) { const extraSpace = new Array().fill(10, text.length - widths.length); const fixedWidths = widths.length < text.length ? [...widths, ...extraSpace] : Array.from(widths); let maxWidth = 0; fixedWidths.map(Math.abs).forEach(num => maxWidth += num); let i = 0; let remainingWidth = maxLen; const result = []; const textLength = text.length - 1; const isLast = (i) => i === textLength; while (text.length > 0) { const currentText = text.shift(); const widthItem = fixedWidths.shift(); const width = Math.abs(widthItem); const isLastItem = isLast(i); const isRightAligned = widthItem < 0 || isLastItem; const content = currentText.length > width ? currentText.substring(0, width - 1) + '…' : currentText; let paddedContent = content; if (isRightAligned) { paddedContent = content.padStart(width) + ' '; } else { paddedContent = ' ' + content.padEnd(width); } remainingWidth -= paddedContent.length; if (isLastItem && remainingWidth > 0) { paddedContent = ' ' + content.padStart(remainingWidth + width); } result.push(paddedContent); i++; } return result.join('').trimEnd(); }