javascript-typescript-langserver
Version:
Implementation of the Language Server Protocol for JavaScript and TypeScript
229 lines • 9.74 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const ts = require("typescript");
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const memfs_1 = require("./memfs");
const util_1 = require("./util");
/**
* Returns a SymbolDescriptor for a ts.DefinitionInfo
*/
function definitionInfoToSymbolDescriptor(info, rootPath) {
const rootUnixPath = util_1.toUnixPath(rootPath);
const symbolDescriptor = {
kind: info.kind || '',
name: info.name || '',
containerKind: info.containerKind || '',
containerName: info.containerName || '',
filePath: info.fileName,
};
// If the symbol is an external module representing a file, set name to the file path
if (info.kind === ts.ScriptElementKind.moduleElement && info.name && /[\\\/]/.test(info.name)) {
symbolDescriptor.name = '"' + info.fileName.replace(/(?:\.d)?\.tsx?$/, '') + '"';
}
// If the symbol itself is not a module and there is no containerKind
// then the container is an external module named by the file name (without file extension)
if (info.kind !== ts.ScriptElementKind.moduleElement && !info.containerKind && !info.containerName) {
symbolDescriptor.containerName = '"' + info.fileName.replace(/(?:\.d)?\.tsx?$/, '') + '"';
symbolDescriptor.containerKind = ts.ScriptElementKind.moduleElement;
}
normalizeSymbolDescriptorPaths(symbolDescriptor, rootUnixPath);
return symbolDescriptor;
}
exports.definitionInfoToSymbolDescriptor = definitionInfoToSymbolDescriptor;
/**
* Transforms definition's file name to URI. If definition belongs to the in-memory TypeScript library,
* returns git://github.com/Microsoft/TypeScript URL, otherwise returns file:// one
*/
function locationUri(filePath) {
if (memfs_1.isTypeScriptLibrary(filePath)) {
return 'git://github.com/Microsoft/TypeScript?v' + ts.version + '#lib/' + filePath.split(/[\/\\]/g).pop();
}
return util_1.path2uri(filePath);
}
exports.locationUri = locationUri;
/**
* Returns an LSP SymbolInformation for a TypeScript NavigateToItem
*
* @param rootPath The workspace rootPath to remove from symbol names and containerNames
*/
function navigateToItemToSymbolInformation(item, program, rootPath) {
const sourceFile = program.getSourceFile(item.fileName);
if (!sourceFile) {
throw new Error(`Source file ${item.fileName} does not exist`);
}
const symbolInformation = {
name: item.name ? item.name.replace(rootPath, '') : '',
kind: stringtoSymbolKind(item.kind),
location: {
uri: locationUri(sourceFile.fileName),
range: {
start: ts.getLineAndCharacterOfPosition(sourceFile, item.textSpan.start),
end: ts.getLineAndCharacterOfPosition(sourceFile, item.textSpan.start + item.textSpan.length),
},
},
};
if (item.containerName) {
symbolInformation.containerName = item.containerName.replace(rootPath, '');
}
return symbolInformation;
}
exports.navigateToItemToSymbolInformation = navigateToItemToSymbolInformation;
/**
* Returns an LSP SymbolKind for a TypeScript ScriptElementKind
*/
function stringtoSymbolKind(kind) {
switch (kind) {
case 'module':
return vscode_languageserver_types_1.SymbolKind.Module;
case 'class':
return vscode_languageserver_types_1.SymbolKind.Class;
case 'local class':
return vscode_languageserver_types_1.SymbolKind.Class;
case 'interface':
return vscode_languageserver_types_1.SymbolKind.Interface;
case 'enum':
return vscode_languageserver_types_1.SymbolKind.Enum;
case 'enum member':
return vscode_languageserver_types_1.SymbolKind.Constant;
case 'var':
return vscode_languageserver_types_1.SymbolKind.Variable;
case 'local var':
return vscode_languageserver_types_1.SymbolKind.Variable;
case 'function':
return vscode_languageserver_types_1.SymbolKind.Function;
case 'local function':
return vscode_languageserver_types_1.SymbolKind.Function;
case 'method':
return vscode_languageserver_types_1.SymbolKind.Method;
case 'getter':
return vscode_languageserver_types_1.SymbolKind.Method;
case 'setter':
return vscode_languageserver_types_1.SymbolKind.Method;
case 'property':
return vscode_languageserver_types_1.SymbolKind.Property;
case 'constructor':
return vscode_languageserver_types_1.SymbolKind.Constructor;
case 'parameter':
return vscode_languageserver_types_1.SymbolKind.Variable;
case 'type parameter':
return vscode_languageserver_types_1.SymbolKind.Variable;
case 'alias':
return vscode_languageserver_types_1.SymbolKind.Variable;
case 'let':
return vscode_languageserver_types_1.SymbolKind.Variable;
case 'const':
return vscode_languageserver_types_1.SymbolKind.Constant;
case 'JSX attribute':
return vscode_languageserver_types_1.SymbolKind.Property;
// case 'script'
// case 'keyword'
// case 'type'
// case 'call'
// case 'index'
// case 'construct'
// case 'primitive type'
// case 'label'
// case 'directory'
// case 'external module name'
// case 'external module name'
default:
return vscode_languageserver_types_1.SymbolKind.Variable;
}
}
exports.stringtoSymbolKind = stringtoSymbolKind;
/**
* Returns an LSP SymbolInformation for a TypeScript NavigationTree node
*/
function navigationTreeToSymbolInformation(tree, parent, sourceFile, rootPath) {
const span = tree.spans[0];
if (!span) {
throw new Error('NavigationTree has no TextSpan');
}
const symbolInformation = {
name: tree.text ? tree.text.replace(rootPath, '') : '',
kind: stringtoSymbolKind(tree.kind),
location: {
uri: locationUri(sourceFile.fileName),
range: {
start: ts.getLineAndCharacterOfPosition(sourceFile, span.start),
end: ts.getLineAndCharacterOfPosition(sourceFile, span.start + span.length),
},
},
};
if (parent && navigationTreeIsSymbol(parent) && parent.text) {
symbolInformation.containerName = parent.text.replace(rootPath, '');
}
return symbolInformation;
}
exports.navigationTreeToSymbolInformation = navigationTreeToSymbolInformation;
/**
* Returns a SymbolDescriptor for a TypeScript NavigationTree node
*/
function navigationTreeToSymbolDescriptor(tree, parent, filePath, rootPath) {
const symbolDescriptor = {
kind: tree.kind,
name: tree.text ? tree.text.replace(rootPath, '') : '',
containerKind: '',
containerName: '',
filePath,
};
if (parent && navigationTreeIsSymbol(parent)) {
symbolDescriptor.containerKind = parent.kind;
symbolDescriptor.containerName = parent.text;
}
// If the symbol is an external module representing a file, set name to the file path
if (tree.kind === ts.ScriptElementKind.moduleElement && !tree.text) {
symbolDescriptor.name = '"' + filePath.replace(/(?:\.d)?\.tsx?$/, '') + '"';
}
// If the symbol itself is not a module and there is no containerKind
// then the container is an external module named by the file name (without file extension)
if (symbolDescriptor.kind !== ts.ScriptElementKind.moduleElement && !symbolDescriptor.containerKind) {
if (!symbolDescriptor.containerName) {
symbolDescriptor.containerName = '"' + filePath.replace(/(?:\.d)?\.tsx?$/, '') + '"';
}
symbolDescriptor.containerKind = ts.ScriptElementKind.moduleElement;
}
normalizeSymbolDescriptorPaths(symbolDescriptor, rootPath);
return symbolDescriptor;
}
exports.navigationTreeToSymbolDescriptor = navigationTreeToSymbolDescriptor;
/**
* Walks a NaviationTree and emits items with a node and its parent node (if exists)
*/
function* walkNavigationTree(tree, parent) {
yield { tree, parent };
for (const childItem of tree.childItems || []) {
yield* walkNavigationTree(childItem, tree);
}
}
exports.walkNavigationTree = walkNavigationTree;
/**
* Returns true if the NavigationTree node describes a proper symbol and not a e.g. a category like `<global>`
*/
function navigationTreeIsSymbol(tree) {
// Categories start with (, [, or <
if (/^[<\(\[]/.test(tree.text)) {
return false;
}
// Magic words
if (['default', 'constructor', 'new()'].indexOf(tree.text) >= 0) {
return false;
}
return true;
}
exports.navigationTreeIsSymbol = navigationTreeIsSymbol;
/**
* Makes paths relative to the passed rootPath and strips `node_modules` out of paths
*/
function normalizeSymbolDescriptorPaths(symbol, rootPath) {
for (const key of ['name', 'containerName', 'filePath']) {
// Make all paths that may occur in module names relative to the workspace rootPath
symbol[key] = symbol[key].replace(rootPath, '');
// Remove node_modules part from a module name
// The SymbolDescriptor will be used in the defining repo, where the symbol file path will never contain node_modules
// It may contain the package name though if the repo is a monorepo with multiple packages
const regExp = /[^"]*node_modules\//;
symbol[key] = symbol[key].replace(regExp, '');
}
}
//# sourceMappingURL=symbols.js.map
;