UNPKG

javascript-typescript-langserver

Version:

Implementation of the Language Server Protocol for JavaScript and TypeScript

229 lines 9.74 kB
"use strict"; 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