UNPKG

typescript-language-server

Version:

Language Server Protocol (LSP) implementation for TypeScript using tsserver

257 lines 8.65 kB
/* * 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 * as lsp from 'vscode-languageserver'; import vscodeUri from 'vscode-uri'; import { Position } from './utils/typeConverters.js'; const RE_PATHSEP_WINDOWS = /\\/g; export function uriToPath(stringUri) { // Vim may send `zipfile:` URIs which tsserver with Yarn v2+ hook can handle. Keep as-is. // Example: zipfile:///foo/bar/baz.zip::path/to/module if (stringUri.startsWith('zipfile:')) { return stringUri; } const uri = vscodeUri.URI.parse(stringUri); if (uri.scheme !== 'file') { return undefined; } return normalizeFsPath(uri.fsPath); } export function pathToUri(filepath, documents) { // Yarn v2+ hooks tsserver and sends `zipfile:` URIs for Vim. Keep as-is. // Example: zipfile:///foo/bar/baz.zip::path/to/module if (filepath.startsWith('zipfile:')) { return filepath; } const fileUri = vscodeUri.URI.file(filepath); const normalizedFilepath = normalizePath(fileUri.fsPath); const document = documents && documents.get(normalizedFilepath); return document ? document.uri : fileUri.toString(); } /** * Normalizes the file system path. * * On systems other than Windows it should be an no-op. * * On Windows, an input path in a format like "C:/path/file.ts" * will be normalized to "c:/path/file.ts". */ export function normalizePath(filePath) { const fsPath = vscodeUri.URI.file(filePath).fsPath; return normalizeFsPath(fsPath); } /** * Normalizes the path obtained through the "fsPath" property of the URI module. */ export function normalizeFsPath(fsPath) { return fsPath.replace(RE_PATHSEP_WINDOWS, '/'); } function currentVersion(filepath, documents) { const fileUri = vscodeUri.URI.file(filepath); const normalizedFilepath = normalizePath(fileUri.fsPath); const document = documents && documents.get(normalizedFilepath); return document ? document.version : null; } export function toLocation(fileSpan, documents) { return { uri: pathToUri(fileSpan.file, documents), range: { start: Position.fromLocation(fileSpan.start), end: Position.fromLocation(fileSpan.end), }, }; } const symbolKindsMapping = { 'enum member': lsp.SymbolKind.Constant, 'JSX attribute': lsp.SymbolKind.Property, 'local class': lsp.SymbolKind.Class, 'local function': lsp.SymbolKind.Function, 'local var': lsp.SymbolKind.Variable, 'type parameter': lsp.SymbolKind.Variable, alias: lsp.SymbolKind.Variable, class: lsp.SymbolKind.Class, const: lsp.SymbolKind.Constant, constructor: lsp.SymbolKind.Constructor, enum: lsp.SymbolKind.Enum, field: lsp.SymbolKind.Field, file: lsp.SymbolKind.File, function: lsp.SymbolKind.Function, getter: lsp.SymbolKind.Method, interface: lsp.SymbolKind.Interface, let: lsp.SymbolKind.Variable, method: lsp.SymbolKind.Method, module: lsp.SymbolKind.Module, parameter: lsp.SymbolKind.Variable, property: lsp.SymbolKind.Property, setter: lsp.SymbolKind.Method, var: lsp.SymbolKind.Variable, }; export function toSymbolKind(tspKind) { return symbolKindsMapping[tspKind] || lsp.SymbolKind.Variable; } function toDiagnosticSeverity(category) { switch (category) { case 'error': return lsp.DiagnosticSeverity.Error; case 'warning': return lsp.DiagnosticSeverity.Warning; case 'suggestion': return lsp.DiagnosticSeverity.Hint; default: return lsp.DiagnosticSeverity.Error; } } export function toDiagnostic(diagnostic, documents, features) { const lspDiagnostic = { range: { start: Position.fromLocation(diagnostic.start), end: Position.fromLocation(diagnostic.end), }, message: diagnostic.text, severity: toDiagnosticSeverity(diagnostic.category), code: diagnostic.code, source: diagnostic.source || 'typescript', relatedInformation: asRelatedInformation(diagnostic.relatedInformation, documents), }; if (features.diagnosticsTagSupport) { lspDiagnostic.tags = getDiagnosticTags(diagnostic); } return lspDiagnostic; } function getDiagnosticTags(diagnostic) { const tags = []; if (diagnostic.reportsUnnecessary) { tags.push(lsp.DiagnosticTag.Unnecessary); } if (diagnostic.reportsDeprecated) { tags.push(lsp.DiagnosticTag.Deprecated); } return tags; } function asRelatedInformation(info, documents) { if (!info) { return undefined; } const result = []; for (const item of info) { const span = item.span; if (span) { result.push(lsp.DiagnosticRelatedInformation.create(toLocation(span, documents), item.message)); } } return result; } export function toTextEdit(edit) { return { range: { start: Position.fromLocation(edit.start), end: Position.fromLocation(edit.end), }, newText: edit.newText, }; } export function toTextDocumentEdit(change, documents) { return { textDocument: { uri: pathToUri(change.fileName, documents), version: currentVersion(change.fileName, documents), }, edits: change.textChanges.map(c => toTextEdit(c)), }; } export function toDocumentHighlight(item) { return item.highlightSpans.map(i => { return { kind: toDocumentHighlightKind(i.kind), range: { start: Position.fromLocation(i.start), end: Position.fromLocation(i.end), }, }; }); } // copied because the protocol module is not available at runtime (js version). var HighlightSpanKind; (function (HighlightSpanKind) { HighlightSpanKind["none"] = "none"; HighlightSpanKind["definition"] = "definition"; HighlightSpanKind["reference"] = "reference"; HighlightSpanKind["writtenReference"] = "writtenReference"; })(HighlightSpanKind || (HighlightSpanKind = {})); function toDocumentHighlightKind(kind) { switch (kind) { case HighlightSpanKind.definition: return lsp.DocumentHighlightKind.Write; case HighlightSpanKind.reference: case HighlightSpanKind.writtenReference: return lsp.DocumentHighlightKind.Read; default: return lsp.DocumentHighlightKind.Text; } } export function asDocumentation(data) { let value = ''; if (data.documentation) { value += asPlainText(data.documentation); } if (data.tags) { const tagsDocumentation = asTagsDocumentation(data.tags); if (tagsDocumentation) { value += '\n\n' + tagsDocumentation; } } return value.length ? { kind: lsp.MarkupKind.Markdown, value, } : undefined; } export function asTagsDocumentation(tags) { return tags.map(asTagDocumentation).join(' \n\n'); } export function asTagDocumentation(tag) { switch (tag.name) { case 'param': { if (!tag.text) { break; } const text = asPlainText(tag.text); const body = text.split(/^([\w.]+)\s*-?\s*/); if (body && body.length === 3) { const param = body[1]; const doc = body[2]; const label = `*@${tag.name}* \`${param}\``; if (!doc) { return label; } return label + (doc.match(/\r\n|\n/g) ? ' \n' + doc : ` — ${doc}`); } break; } } // Generic tag const label = `*@${tag.name}*`; const text = asTagBodyText(tag); if (!text) { return label; } return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` — ${text}`); } export function asTagBodyText(tag) { if (!tag.text) { return undefined; } const text = asPlainText(tag.text); switch (tag.name) { case 'example': case 'default': // Convert to markdown code block if it not already one if (text.match(/^\s*[~`]{3}/g)) { return text; } return '```\n' + text + '\n```'; } return text; } export function asPlainText(parts) { if (typeof parts === 'string') { return parts; } return parts.map(part => part.text).join(''); } //# sourceMappingURL=protocol-translation.js.map