fish-lsp
Version:
LSP implementation for fish/fish-shell
212 lines (211 loc) • 9.46 kB
JavaScript
;
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();
}