typescript-language-server
Version:
Language Server Protocol (LSP) implementation for TypeScript using tsserver
165 lines • 5.81 kB
JavaScript
/*
* Copyright (C) 2017, 2018 TypeFox and others.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/
import { platform } from 'node:os';
import * as path from 'node:path';
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import deepmerge from 'deepmerge';
import * as lsp from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { normalizePath, pathToUri } from './protocol-translation.js';
import { LspServer } from './lsp-server.js';
import { ConsoleLogger } from './logger.js';
import { TypeScriptVersionProvider } from './tsServer/versionProvider.js';
const CONSOLE_LOG_LEVEL = ConsoleLogger.toMessageTypeLevel(process.env.CONSOLE_LOG_LEVEL);
export const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url));
const DEFAULT_TEST_CLIENT_CAPABILITIES = {
textDocument: {
completion: {
completionItem: {
insertReplaceSupport: true,
snippetSupport: true,
labelDetailsSupport: true,
},
},
documentSymbol: {
hierarchicalDocumentSymbolSupport: true,
},
publishDiagnostics: {
tagSupport: {
valueSet: [
lsp.DiagnosticTag.Unnecessary,
lsp.DiagnosticTag.Deprecated,
],
},
},
moniker: {},
},
};
const DEFAULT_TEST_CLIENT_INITIALIZATION_OPTIONS = {
plugins: [],
preferences: {
allowIncompleteCompletions: true,
allowRenameOfImportPath: true,
allowTextChangesInNewFiles: true,
displayPartsForJSDoc: true,
generateReturnInDocTemplate: true,
includeAutomaticOptionalChainCompletions: true,
includeCompletionsForImportStatements: true,
includeCompletionsForModuleExports: true,
includeCompletionsWithClassMemberSnippets: true,
includeCompletionsWithInsertText: true,
includeCompletionsWithSnippetText: true,
jsxAttributeCompletionStyle: 'auto',
providePrefixAndSuffixTextForRename: true,
},
};
export function uri(...components) {
const resolved = filePath(...components);
return pathToUri(resolved, undefined);
}
export function filePath(...components) {
return normalizePath(path.resolve(PACKAGE_ROOT, 'test-data', ...components));
}
export function readContents(path) {
return fs.readFileSync(path, 'utf-8').toString();
}
export function positionAt(document, idx) {
const doc = TextDocument.create(document.uri, document.languageId, document.version, document.text);
const pos = doc.positionAt(idx);
return {
line: pos.line,
character: pos.character,
};
}
export function position(document, match) {
return positionAt(document, document.text.indexOf(match));
}
export function positionAfter(document, match) {
return positionAt(document, document.text.indexOf(match) + match.length);
}
export function lastPosition(document, match) {
return positionAt(document, document.text.lastIndexOf(match));
}
export function toPlatformEOL(text) {
if (platform() === 'win32') {
return text.replace(/(?!\r)\n/g, '\r\n');
}
return text;
}
export class TestLspClient {
constructor(options, logger) {
this.options = options;
this.logger = logger;
this.workspaceEditsListener = null;
}
async createProgressReporter(_token, _workDoneProgress) {
const reporter = new class {
begin(_title, _percentage, _message, _cancellable) { }
report(_message) { }
done() { }
};
return reporter;
}
async withProgress(_options, task) {
const progress = await this.createProgressReporter();
return await task(progress);
}
publishDiagnostics(args) {
return this.options.publishDiagnostics(args);
}
showErrorMessage(message) {
this.logger.error(`[showErrorMessage] ${message}`);
}
logMessage(args) {
this.logger.log('logMessage', JSON.stringify(args));
}
addApplyWorkspaceEditListener(listener) {
this.workspaceEditsListener = listener;
}
async applyWorkspaceEdit(args) {
if (this.workspaceEditsListener) {
this.workspaceEditsListener(args);
}
return { applied: true };
}
rename() {
throw new Error('unsupported');
}
}
export class TestLspServer extends LspServer {
constructor() {
super(...arguments);
this.workspaceEdits = [];
}
}
export async function createServer(options) {
const typescriptVersionProvider = new TypeScriptVersionProvider();
const bundled = typescriptVersionProvider.bundledVersion();
const logger = new ConsoleLogger(CONSOLE_LOG_LEVEL);
const lspClient = new TestLspClient(options, logger);
const server = new TestLspServer({
logger,
tsserverPath: bundled.tsServerPath,
tsserverLogVerbosity: options.tsserverLogVerbosity,
tsserverLogFile: path.resolve(PACKAGE_ROOT, 'tsserver.log'),
lspClient,
});
lspClient.addApplyWorkspaceEditListener(args => {
server.workspaceEdits.push(args);
});
await server.initialize({
rootPath: undefined,
rootUri: options.rootUri,
processId: 42,
capabilities: deepmerge(DEFAULT_TEST_CLIENT_CAPABILITIES, options.clientCapabilitiesOverride || {}),
initializationOptions: DEFAULT_TEST_CLIENT_INITIALIZATION_OPTIONS,
workspaceFolders: null,
});
return server;
}
//# sourceMappingURL=test-utils.js.map