- Hover provider showing entity information and type - Go-to-definition (F12) for entity references - Basic IFC file validation (ISO-10303-21 header check) - Entity parsing with regex-based detection - Proper CommonJS module system (avoiding ES module issues) This replaces the broken baseline from ifc-developer-tools which had: - Non-functional ES module configuration - Circular dependency issues - Parser crashes - Non-working PositionVisitor Built on Microsoft's LSP example template for a clean, maintainable foundation. Next: Add hierarchical entity dependency tree in hover tooltip."
1134 lines
42 KiB
JavaScript
1134 lines
42 KiB
JavaScript
"use strict";
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
* ------------------------------------------------------------------------------------------ */
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createConverter = void 0;
|
|
const code = require("vscode");
|
|
const ls = require("vscode-languageserver-protocol");
|
|
const Is = require("./utils/is");
|
|
const async = require("./utils/async");
|
|
const protocolCompletionItem_1 = require("./protocolCompletionItem");
|
|
const protocolCodeLens_1 = require("./protocolCodeLens");
|
|
const protocolDocumentLink_1 = require("./protocolDocumentLink");
|
|
const protocolCodeAction_1 = require("./protocolCodeAction");
|
|
const protocolDiagnostic_1 = require("./protocolDiagnostic");
|
|
const protocolCallHierarchyItem_1 = require("./protocolCallHierarchyItem");
|
|
const protocolTypeHierarchyItem_1 = require("./protocolTypeHierarchyItem");
|
|
const protocolWorkspaceSymbol_1 = require("./protocolWorkspaceSymbol");
|
|
const protocolInlayHint_1 = require("./protocolInlayHint");
|
|
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
|
|
var CodeBlock;
|
|
(function (CodeBlock) {
|
|
function is(value) {
|
|
let candidate = value;
|
|
return candidate && Is.string(candidate.language) && Is.string(candidate.value);
|
|
}
|
|
CodeBlock.is = is;
|
|
})(CodeBlock || (CodeBlock = {}));
|
|
function createConverter(uriConverter, trustMarkdown, supportHtml) {
|
|
const nullConverter = (value) => code.Uri.parse(value);
|
|
const _uriConverter = uriConverter || nullConverter;
|
|
function asUri(value) {
|
|
return _uriConverter(value);
|
|
}
|
|
function asDocumentSelector(selector) {
|
|
const result = [];
|
|
for (const filter of selector) {
|
|
if (typeof filter === 'string') {
|
|
result.push(filter);
|
|
}
|
|
else if (vscode_languageserver_protocol_1.NotebookCellTextDocumentFilter.is(filter)) {
|
|
// We first need to check for the notebook cell filter since a TextDocumentFilter would
|
|
// match both (e.g. the notebook is optional).
|
|
if (typeof filter.notebook === 'string') {
|
|
result.push({ notebookType: filter.notebook, language: filter.language });
|
|
}
|
|
else {
|
|
const notebookType = filter.notebook.notebookType ?? '*';
|
|
result.push({ notebookType: notebookType, scheme: filter.notebook.scheme, pattern: filter.notebook.pattern, language: filter.language });
|
|
}
|
|
}
|
|
else if (vscode_languageserver_protocol_1.TextDocumentFilter.is(filter)) {
|
|
result.push({ language: filter.language, scheme: filter.scheme, pattern: filter.pattern });
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
async function asDiagnostics(diagnostics, token) {
|
|
return async.map(diagnostics, asDiagnostic, token);
|
|
}
|
|
function asDiagnosticsSync(diagnostics) {
|
|
const result = new Array(diagnostics.length);
|
|
for (let i = 0; i < diagnostics.length; i++) {
|
|
result[i] = asDiagnostic(diagnostics[i]);
|
|
}
|
|
return result;
|
|
}
|
|
function asDiagnostic(diagnostic) {
|
|
let result = new protocolDiagnostic_1.ProtocolDiagnostic(asRange(diagnostic.range), diagnostic.message, asDiagnosticSeverity(diagnostic.severity), diagnostic.data);
|
|
if (diagnostic.code !== undefined) {
|
|
if (typeof diagnostic.code === 'string' || typeof diagnostic.code === 'number') {
|
|
if (ls.CodeDescription.is(diagnostic.codeDescription)) {
|
|
result.code = {
|
|
value: diagnostic.code,
|
|
target: asUri(diagnostic.codeDescription.href)
|
|
};
|
|
}
|
|
else {
|
|
result.code = diagnostic.code;
|
|
}
|
|
}
|
|
else if (protocolDiagnostic_1.DiagnosticCode.is(diagnostic.code)) {
|
|
// This is for backwards compatibility of a proposed API.
|
|
// We should remove this at some point.
|
|
result.hasDiagnosticCode = true;
|
|
const diagnosticCode = diagnostic.code;
|
|
result.code = {
|
|
value: diagnosticCode.value,
|
|
target: asUri(diagnosticCode.target)
|
|
};
|
|
}
|
|
}
|
|
if (diagnostic.source) {
|
|
result.source = diagnostic.source;
|
|
}
|
|
if (diagnostic.relatedInformation) {
|
|
result.relatedInformation = asRelatedInformation(diagnostic.relatedInformation);
|
|
}
|
|
if (Array.isArray(diagnostic.tags)) {
|
|
result.tags = asDiagnosticTags(diagnostic.tags);
|
|
}
|
|
return result;
|
|
}
|
|
function asRelatedInformation(relatedInformation) {
|
|
const result = new Array(relatedInformation.length);
|
|
for (let i = 0; i < relatedInformation.length; i++) {
|
|
const info = relatedInformation[i];
|
|
result[i] = new code.DiagnosticRelatedInformation(asLocation(info.location), info.message);
|
|
}
|
|
return result;
|
|
}
|
|
function asDiagnosticTags(tags) {
|
|
if (!tags) {
|
|
return undefined;
|
|
}
|
|
let result = [];
|
|
for (let tag of tags) {
|
|
let converted = asDiagnosticTag(tag);
|
|
if (converted !== undefined) {
|
|
result.push(converted);
|
|
}
|
|
}
|
|
return result.length > 0 ? result : undefined;
|
|
}
|
|
function asDiagnosticTag(tag) {
|
|
switch (tag) {
|
|
case ls.DiagnosticTag.Unnecessary:
|
|
return code.DiagnosticTag.Unnecessary;
|
|
case ls.DiagnosticTag.Deprecated:
|
|
return code.DiagnosticTag.Deprecated;
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
function asPosition(value) {
|
|
return value ? new code.Position(value.line, value.character) : undefined;
|
|
}
|
|
function asRange(value) {
|
|
return value ? new code.Range(value.start.line, value.start.character, value.end.line, value.end.character) : undefined;
|
|
}
|
|
async function asRanges(items, token) {
|
|
return async.map(items, (range) => {
|
|
return new code.Range(range.start.line, range.start.character, range.end.line, range.end.character);
|
|
}, token);
|
|
}
|
|
function asDiagnosticSeverity(value) {
|
|
if (value === undefined || value === null) {
|
|
return code.DiagnosticSeverity.Error;
|
|
}
|
|
switch (value) {
|
|
case ls.DiagnosticSeverity.Error:
|
|
return code.DiagnosticSeverity.Error;
|
|
case ls.DiagnosticSeverity.Warning:
|
|
return code.DiagnosticSeverity.Warning;
|
|
case ls.DiagnosticSeverity.Information:
|
|
return code.DiagnosticSeverity.Information;
|
|
case ls.DiagnosticSeverity.Hint:
|
|
return code.DiagnosticSeverity.Hint;
|
|
}
|
|
return code.DiagnosticSeverity.Error;
|
|
}
|
|
function asHoverContent(value) {
|
|
if (Is.string(value)) {
|
|
return asMarkdownString(value);
|
|
}
|
|
else if (CodeBlock.is(value)) {
|
|
let result = asMarkdownString();
|
|
return result.appendCodeblock(value.value, value.language);
|
|
}
|
|
else if (Array.isArray(value)) {
|
|
let result = [];
|
|
for (let element of value) {
|
|
let item = asMarkdownString();
|
|
if (CodeBlock.is(element)) {
|
|
item.appendCodeblock(element.value, element.language);
|
|
}
|
|
else {
|
|
item.appendMarkdown(element);
|
|
}
|
|
result.push(item);
|
|
}
|
|
return result;
|
|
}
|
|
else {
|
|
return asMarkdownString(value);
|
|
}
|
|
}
|
|
function asDocumentation(value) {
|
|
if (Is.string(value)) {
|
|
return value;
|
|
}
|
|
else {
|
|
switch (value.kind) {
|
|
case ls.MarkupKind.Markdown:
|
|
return asMarkdownString(value.value);
|
|
case ls.MarkupKind.PlainText:
|
|
return value.value;
|
|
default:
|
|
return `Unsupported Markup content received. Kind is: ${value.kind}`;
|
|
}
|
|
}
|
|
}
|
|
function asMarkdownString(value) {
|
|
let result;
|
|
if (value === undefined || typeof value === 'string') {
|
|
result = new code.MarkdownString(value);
|
|
}
|
|
else {
|
|
switch (value.kind) {
|
|
case ls.MarkupKind.Markdown:
|
|
result = new code.MarkdownString(value.value);
|
|
break;
|
|
case ls.MarkupKind.PlainText:
|
|
result = new code.MarkdownString();
|
|
result.appendText(value.value);
|
|
break;
|
|
default:
|
|
result = new code.MarkdownString();
|
|
result.appendText(`Unsupported Markup content received. Kind is: ${value.kind}`);
|
|
break;
|
|
}
|
|
}
|
|
result.isTrusted = trustMarkdown;
|
|
result.supportHtml = supportHtml;
|
|
return result;
|
|
}
|
|
function asHover(hover) {
|
|
if (!hover) {
|
|
return undefined;
|
|
}
|
|
return new code.Hover(asHoverContent(hover.contents), asRange(hover.range));
|
|
}
|
|
async function asCompletionResult(value, allCommitCharacters, token) {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return async.map(value, (item) => asCompletionItem(item, allCommitCharacters), token);
|
|
}
|
|
const list = value;
|
|
const { defaultRange, commitCharacters } = getCompletionItemDefaults(list, allCommitCharacters);
|
|
const converted = await async.map(list.items, (item) => {
|
|
return asCompletionItem(item, commitCharacters, defaultRange, list.itemDefaults?.insertTextMode, list.itemDefaults?.insertTextFormat, list.itemDefaults?.data);
|
|
}, token);
|
|
return new code.CompletionList(converted, list.isIncomplete);
|
|
}
|
|
function getCompletionItemDefaults(list, allCommitCharacters) {
|
|
const rangeDefaults = list.itemDefaults?.editRange;
|
|
const commitCharacters = list.itemDefaults?.commitCharacters ?? allCommitCharacters;
|
|
return ls.Range.is(rangeDefaults)
|
|
? { defaultRange: asRange(rangeDefaults), commitCharacters }
|
|
: rangeDefaults !== undefined
|
|
? { defaultRange: { inserting: asRange(rangeDefaults.insert), replacing: asRange(rangeDefaults.replace) }, commitCharacters }
|
|
: { defaultRange: undefined, commitCharacters };
|
|
}
|
|
function asCompletionItemKind(value) {
|
|
// Protocol item kind is 1 based, codes item kind is zero based.
|
|
if (ls.CompletionItemKind.Text <= value && value <= ls.CompletionItemKind.TypeParameter) {
|
|
return [value - 1, undefined];
|
|
}
|
|
return [code.CompletionItemKind.Text, value];
|
|
}
|
|
function asCompletionItemTag(tag) {
|
|
switch (tag) {
|
|
case ls.CompletionItemTag.Deprecated:
|
|
return code.CompletionItemTag.Deprecated;
|
|
}
|
|
return undefined;
|
|
}
|
|
function asCompletionItemTags(tags) {
|
|
if (tags === undefined || tags === null) {
|
|
return [];
|
|
}
|
|
const result = [];
|
|
for (const tag of tags) {
|
|
const converted = asCompletionItemTag(tag);
|
|
if (converted !== undefined) {
|
|
result.push(converted);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function asCompletionItem(item, defaultCommitCharacters, defaultRange, defaultInsertTextMode, defaultInsertTextFormat, defaultData) {
|
|
const tags = asCompletionItemTags(item.tags);
|
|
const label = asCompletionItemLabel(item);
|
|
const result = new protocolCompletionItem_1.default(label);
|
|
if (item.detail) {
|
|
result.detail = item.detail;
|
|
}
|
|
if (item.documentation) {
|
|
result.documentation = asDocumentation(item.documentation);
|
|
result.documentationFormat = Is.string(item.documentation) ? '$string' : item.documentation.kind;
|
|
}
|
|
if (item.filterText) {
|
|
result.filterText = item.filterText;
|
|
}
|
|
const insertText = asCompletionInsertText(item, defaultRange, defaultInsertTextFormat);
|
|
if (insertText) {
|
|
result.insertText = insertText.text;
|
|
result.range = insertText.range;
|
|
result.fromEdit = insertText.fromEdit;
|
|
}
|
|
if (Is.number(item.kind)) {
|
|
let [itemKind, original] = asCompletionItemKind(item.kind);
|
|
result.kind = itemKind;
|
|
if (original) {
|
|
result.originalItemKind = original;
|
|
}
|
|
}
|
|
if (item.sortText) {
|
|
result.sortText = item.sortText;
|
|
}
|
|
if (item.additionalTextEdits) {
|
|
result.additionalTextEdits = asTextEditsSync(item.additionalTextEdits);
|
|
}
|
|
const commitCharacters = item.commitCharacters !== undefined
|
|
? Is.stringArray(item.commitCharacters) ? item.commitCharacters : undefined
|
|
: defaultCommitCharacters;
|
|
if (commitCharacters) {
|
|
result.commitCharacters = commitCharacters.slice();
|
|
}
|
|
if (item.command) {
|
|
result.command = asCommand(item.command);
|
|
}
|
|
if (item.deprecated === true || item.deprecated === false) {
|
|
result.deprecated = item.deprecated;
|
|
if (item.deprecated === true) {
|
|
tags.push(code.CompletionItemTag.Deprecated);
|
|
}
|
|
}
|
|
if (item.preselect === true || item.preselect === false) {
|
|
result.preselect = item.preselect;
|
|
}
|
|
const data = item.data ?? defaultData;
|
|
if (data !== undefined) {
|
|
result.data = data;
|
|
}
|
|
if (tags.length > 0) {
|
|
result.tags = tags;
|
|
}
|
|
const insertTextMode = item.insertTextMode ?? defaultInsertTextMode;
|
|
if (insertTextMode !== undefined) {
|
|
result.insertTextMode = insertTextMode;
|
|
if (insertTextMode === ls.InsertTextMode.asIs) {
|
|
result.keepWhitespace = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function asCompletionItemLabel(item) {
|
|
if (ls.CompletionItemLabelDetails.is(item.labelDetails)) {
|
|
return {
|
|
label: item.label,
|
|
detail: item.labelDetails.detail,
|
|
description: item.labelDetails.description
|
|
};
|
|
}
|
|
else {
|
|
return item.label;
|
|
}
|
|
}
|
|
function asCompletionInsertText(item, defaultRange, defaultInsertTextFormat) {
|
|
const insertTextFormat = item.insertTextFormat ?? defaultInsertTextFormat;
|
|
if (item.textEdit !== undefined || defaultRange !== undefined) {
|
|
const [range, newText] = item.textEdit !== undefined
|
|
? getCompletionRangeAndText(item.textEdit)
|
|
: [defaultRange, item.textEditText ?? item.label];
|
|
if (insertTextFormat === ls.InsertTextFormat.Snippet) {
|
|
return { text: new code.SnippetString(newText), range: range, fromEdit: true };
|
|
}
|
|
else {
|
|
return { text: newText, range: range, fromEdit: true };
|
|
}
|
|
}
|
|
else if (item.insertText) {
|
|
if (insertTextFormat === ls.InsertTextFormat.Snippet) {
|
|
return { text: new code.SnippetString(item.insertText), fromEdit: false };
|
|
}
|
|
else {
|
|
return { text: item.insertText, fromEdit: false };
|
|
}
|
|
}
|
|
else {
|
|
return undefined;
|
|
}
|
|
}
|
|
function getCompletionRangeAndText(value) {
|
|
if (ls.InsertReplaceEdit.is(value)) {
|
|
return [{ inserting: asRange(value.insert), replacing: asRange(value.replace) }, value.newText];
|
|
}
|
|
else {
|
|
return [asRange(value.range), value.newText];
|
|
}
|
|
}
|
|
function asTextEdit(edit) {
|
|
if (!edit) {
|
|
return undefined;
|
|
}
|
|
return new code.TextEdit(asRange(edit.range), edit.newText);
|
|
}
|
|
async function asTextEdits(items, token) {
|
|
if (!items) {
|
|
return undefined;
|
|
}
|
|
return async.map(items, asTextEdit, token);
|
|
}
|
|
function asTextEditsSync(items) {
|
|
if (!items) {
|
|
return undefined;
|
|
}
|
|
const result = new Array(items.length);
|
|
for (let i = 0; i < items.length; i++) {
|
|
result[i] = asTextEdit(items[i]);
|
|
}
|
|
return result;
|
|
}
|
|
async function asSignatureHelp(item, token) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
let result = new code.SignatureHelp();
|
|
if (Is.number(item.activeSignature)) {
|
|
result.activeSignature = item.activeSignature;
|
|
}
|
|
else {
|
|
// activeSignature was optional in the past
|
|
result.activeSignature = 0;
|
|
}
|
|
if (Is.number(item.activeParameter)) {
|
|
result.activeParameter = item.activeParameter;
|
|
}
|
|
else {
|
|
// activeParameter was optional in the past
|
|
result.activeParameter = 0;
|
|
}
|
|
if (item.signatures) {
|
|
result.signatures = await asSignatureInformations(item.signatures, token);
|
|
}
|
|
return result;
|
|
}
|
|
async function asSignatureInformations(items, token) {
|
|
return async.mapAsync(items, asSignatureInformation, token);
|
|
}
|
|
async function asSignatureInformation(item, token) {
|
|
let result = new code.SignatureInformation(item.label);
|
|
if (item.documentation !== undefined) {
|
|
result.documentation = asDocumentation(item.documentation);
|
|
}
|
|
if (item.parameters !== undefined) {
|
|
result.parameters = await asParameterInformations(item.parameters, token);
|
|
}
|
|
if (item.activeParameter !== undefined) {
|
|
result.activeParameter = item.activeParameter;
|
|
}
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
function asParameterInformations(items, token) {
|
|
return async.map(items, asParameterInformation, token);
|
|
}
|
|
function asParameterInformation(item) {
|
|
let result = new code.ParameterInformation(item.label);
|
|
if (item.documentation) {
|
|
result.documentation = asDocumentation(item.documentation);
|
|
}
|
|
return result;
|
|
}
|
|
function asLocation(item) {
|
|
return item ? new code.Location(_uriConverter(item.uri), asRange(item.range)) : undefined;
|
|
}
|
|
async function asDeclarationResult(item, token) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
return asLocationResult(item, token);
|
|
}
|
|
async function asDefinitionResult(item, token) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
return asLocationResult(item, token);
|
|
}
|
|
function asLocationLink(item) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
let result = {
|
|
targetUri: _uriConverter(item.targetUri),
|
|
targetRange: asRange(item.targetRange),
|
|
originSelectionRange: asRange(item.originSelectionRange),
|
|
targetSelectionRange: asRange(item.targetSelectionRange)
|
|
};
|
|
if (!result.targetSelectionRange) {
|
|
throw new Error(`targetSelectionRange must not be undefined or null`);
|
|
}
|
|
return result;
|
|
}
|
|
async function asLocationResult(item, token) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
if (Is.array(item)) {
|
|
if (item.length === 0) {
|
|
return [];
|
|
}
|
|
else if (ls.LocationLink.is(item[0])) {
|
|
const links = item;
|
|
return async.map(links, asLocationLink, token);
|
|
}
|
|
else {
|
|
const locations = item;
|
|
return async.map(locations, asLocation, token);
|
|
}
|
|
}
|
|
else if (ls.LocationLink.is(item)) {
|
|
return [asLocationLink(item)];
|
|
}
|
|
else {
|
|
return asLocation(item);
|
|
}
|
|
}
|
|
async function asReferences(values, token) {
|
|
if (!values) {
|
|
return undefined;
|
|
}
|
|
return async.map(values, asLocation, token);
|
|
}
|
|
async function asDocumentHighlights(values, token) {
|
|
if (!values) {
|
|
return undefined;
|
|
}
|
|
return async.map(values, asDocumentHighlight, token);
|
|
}
|
|
function asDocumentHighlight(item) {
|
|
let result = new code.DocumentHighlight(asRange(item.range));
|
|
if (Is.number(item.kind)) {
|
|
result.kind = asDocumentHighlightKind(item.kind);
|
|
}
|
|
return result;
|
|
}
|
|
function asDocumentHighlightKind(item) {
|
|
switch (item) {
|
|
case ls.DocumentHighlightKind.Text:
|
|
return code.DocumentHighlightKind.Text;
|
|
case ls.DocumentHighlightKind.Read:
|
|
return code.DocumentHighlightKind.Read;
|
|
case ls.DocumentHighlightKind.Write:
|
|
return code.DocumentHighlightKind.Write;
|
|
}
|
|
return code.DocumentHighlightKind.Text;
|
|
}
|
|
async function asSymbolInformations(values, token) {
|
|
if (!values) {
|
|
return undefined;
|
|
}
|
|
return async.map(values, asSymbolInformation, token);
|
|
}
|
|
function asSymbolKind(item) {
|
|
if (item <= ls.SymbolKind.TypeParameter) {
|
|
// Symbol kind is one based in the protocol and zero based in code.
|
|
return item - 1;
|
|
}
|
|
return code.SymbolKind.Property;
|
|
}
|
|
function asSymbolTag(value) {
|
|
switch (value) {
|
|
case ls.SymbolTag.Deprecated:
|
|
return code.SymbolTag.Deprecated;
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
function asSymbolTags(items) {
|
|
if (items === undefined || items === null) {
|
|
return undefined;
|
|
}
|
|
const result = [];
|
|
for (const item of items) {
|
|
const converted = asSymbolTag(item);
|
|
if (converted !== undefined) {
|
|
result.push(converted);
|
|
}
|
|
}
|
|
return result.length === 0 ? undefined : result;
|
|
}
|
|
function asSymbolInformation(item) {
|
|
const data = item.data;
|
|
const location = item.location;
|
|
const result = location.range === undefined || data !== undefined
|
|
? new protocolWorkspaceSymbol_1.default(item.name, asSymbolKind(item.kind), item.containerName ?? '', location.range === undefined ? _uriConverter(location.uri) : new code.Location(_uriConverter(item.location.uri), asRange(location.range)), data)
|
|
: new code.SymbolInformation(item.name, asSymbolKind(item.kind), item.containerName ?? '', new code.Location(_uriConverter(item.location.uri), asRange(location.range)));
|
|
fillTags(result, item);
|
|
return result;
|
|
}
|
|
async function asDocumentSymbols(values, token) {
|
|
if (values === undefined || values === null) {
|
|
return undefined;
|
|
}
|
|
return async.map(values, asDocumentSymbol, token);
|
|
}
|
|
function asDocumentSymbol(value) {
|
|
let result = new code.DocumentSymbol(value.name, value.detail || '', asSymbolKind(value.kind), asRange(value.range), asRange(value.selectionRange));
|
|
fillTags(result, value);
|
|
if (value.children !== undefined && value.children.length > 0) {
|
|
let children = [];
|
|
for (let child of value.children) {
|
|
children.push(asDocumentSymbol(child));
|
|
}
|
|
result.children = children;
|
|
}
|
|
return result;
|
|
}
|
|
function fillTags(result, value) {
|
|
result.tags = asSymbolTags(value.tags);
|
|
if (value.deprecated) {
|
|
if (!result.tags) {
|
|
result.tags = [code.SymbolTag.Deprecated];
|
|
}
|
|
else {
|
|
if (!result.tags.includes(code.SymbolTag.Deprecated)) {
|
|
result.tags = result.tags.concat(code.SymbolTag.Deprecated);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function asCommand(item) {
|
|
let result = { title: item.title, command: item.command };
|
|
if (item.arguments) {
|
|
result.arguments = item.arguments;
|
|
}
|
|
return result;
|
|
}
|
|
async function asCommands(items, token) {
|
|
if (!items) {
|
|
return undefined;
|
|
}
|
|
return async.map(items, asCommand, token);
|
|
}
|
|
const kindMapping = new Map();
|
|
kindMapping.set(ls.CodeActionKind.Empty, code.CodeActionKind.Empty);
|
|
kindMapping.set(ls.CodeActionKind.QuickFix, code.CodeActionKind.QuickFix);
|
|
kindMapping.set(ls.CodeActionKind.Refactor, code.CodeActionKind.Refactor);
|
|
kindMapping.set(ls.CodeActionKind.RefactorExtract, code.CodeActionKind.RefactorExtract);
|
|
kindMapping.set(ls.CodeActionKind.RefactorInline, code.CodeActionKind.RefactorInline);
|
|
kindMapping.set(ls.CodeActionKind.RefactorRewrite, code.CodeActionKind.RefactorRewrite);
|
|
kindMapping.set(ls.CodeActionKind.Source, code.CodeActionKind.Source);
|
|
kindMapping.set(ls.CodeActionKind.SourceOrganizeImports, code.CodeActionKind.SourceOrganizeImports);
|
|
function asCodeActionKind(item) {
|
|
if (item === undefined || item === null) {
|
|
return undefined;
|
|
}
|
|
let result = kindMapping.get(item);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
let parts = item.split('.');
|
|
result = code.CodeActionKind.Empty;
|
|
for (let part of parts) {
|
|
result = result.append(part);
|
|
}
|
|
return result;
|
|
}
|
|
function asCodeActionKinds(items) {
|
|
if (items === undefined || items === null) {
|
|
return undefined;
|
|
}
|
|
return items.map(kind => asCodeActionKind(kind));
|
|
}
|
|
async function asCodeAction(item, token) {
|
|
if (item === undefined || item === null) {
|
|
return undefined;
|
|
}
|
|
let result = new protocolCodeAction_1.default(item.title, item.data);
|
|
if (item.kind !== undefined) {
|
|
result.kind = asCodeActionKind(item.kind);
|
|
}
|
|
if (item.diagnostics !== undefined) {
|
|
result.diagnostics = asDiagnosticsSync(item.diagnostics);
|
|
}
|
|
if (item.edit !== undefined) {
|
|
result.edit = await asWorkspaceEdit(item.edit, token);
|
|
}
|
|
if (item.command !== undefined) {
|
|
result.command = asCommand(item.command);
|
|
}
|
|
if (item.isPreferred !== undefined) {
|
|
result.isPreferred = item.isPreferred;
|
|
}
|
|
if (item.disabled !== undefined) {
|
|
result.disabled = { reason: item.disabled.reason };
|
|
}
|
|
return result;
|
|
}
|
|
function asCodeActionResult(items, token) {
|
|
return async.mapAsync(items, async (item) => {
|
|
if (ls.Command.is(item)) {
|
|
return asCommand(item);
|
|
}
|
|
else {
|
|
return asCodeAction(item, token);
|
|
}
|
|
}, token);
|
|
}
|
|
function asCodeLens(item) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
let result = new protocolCodeLens_1.default(asRange(item.range));
|
|
if (item.command) {
|
|
result.command = asCommand(item.command);
|
|
}
|
|
if (item.data !== undefined && item.data !== null) {
|
|
result.data = item.data;
|
|
}
|
|
return result;
|
|
}
|
|
async function asCodeLenses(items, token) {
|
|
if (!items) {
|
|
return undefined;
|
|
}
|
|
return async.map(items, asCodeLens, token);
|
|
}
|
|
async function asWorkspaceEdit(item, token) {
|
|
if (!item) {
|
|
return undefined;
|
|
}
|
|
const sharedMetadata = new Map();
|
|
if (item.changeAnnotations !== undefined) {
|
|
const changeAnnotations = item.changeAnnotations;
|
|
await async.forEach(Object.keys(changeAnnotations), (key) => {
|
|
const metaData = asWorkspaceEditEntryMetadata(changeAnnotations[key]);
|
|
sharedMetadata.set(key, metaData);
|
|
}, token);
|
|
}
|
|
const asMetadata = (annotation) => {
|
|
if (annotation === undefined) {
|
|
return undefined;
|
|
}
|
|
else {
|
|
return sharedMetadata.get(annotation);
|
|
}
|
|
};
|
|
const result = new code.WorkspaceEdit();
|
|
if (item.documentChanges) {
|
|
const documentChanges = item.documentChanges;
|
|
await async.forEach(documentChanges, (change) => {
|
|
if (ls.CreateFile.is(change)) {
|
|
result.createFile(_uriConverter(change.uri), change.options, asMetadata(change.annotationId));
|
|
}
|
|
else if (ls.RenameFile.is(change)) {
|
|
result.renameFile(_uriConverter(change.oldUri), _uriConverter(change.newUri), change.options, asMetadata(change.annotationId));
|
|
}
|
|
else if (ls.DeleteFile.is(change)) {
|
|
result.deleteFile(_uriConverter(change.uri), change.options, asMetadata(change.annotationId));
|
|
}
|
|
else if (ls.TextDocumentEdit.is(change)) {
|
|
const uri = _uriConverter(change.textDocument.uri);
|
|
for (const edit of change.edits) {
|
|
if (ls.AnnotatedTextEdit.is(edit)) {
|
|
result.replace(uri, asRange(edit.range), edit.newText, asMetadata(edit.annotationId));
|
|
}
|
|
else {
|
|
result.replace(uri, asRange(edit.range), edit.newText);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
throw new Error(`Unknown workspace edit change received:\n${JSON.stringify(change, undefined, 4)}`);
|
|
}
|
|
}, token);
|
|
}
|
|
else if (item.changes) {
|
|
const changes = item.changes;
|
|
await async.forEach(Object.keys(changes), (key) => {
|
|
result.set(_uriConverter(key), asTextEditsSync(changes[key]));
|
|
}, token);
|
|
}
|
|
return result;
|
|
}
|
|
function asWorkspaceEditEntryMetadata(annotation) {
|
|
if (annotation === undefined) {
|
|
return undefined;
|
|
}
|
|
return { label: annotation.label, needsConfirmation: !!annotation.needsConfirmation, description: annotation.description };
|
|
}
|
|
function asDocumentLink(item) {
|
|
let range = asRange(item.range);
|
|
let target = item.target ? asUri(item.target) : undefined;
|
|
// target must be optional in DocumentLink
|
|
let link = new protocolDocumentLink_1.default(range, target);
|
|
if (item.tooltip !== undefined) {
|
|
link.tooltip = item.tooltip;
|
|
}
|
|
if (item.data !== undefined && item.data !== null) {
|
|
link.data = item.data;
|
|
}
|
|
return link;
|
|
}
|
|
async function asDocumentLinks(items, token) {
|
|
if (!items) {
|
|
return undefined;
|
|
}
|
|
return async.map(items, asDocumentLink, token);
|
|
}
|
|
function asColor(color) {
|
|
return new code.Color(color.red, color.green, color.blue, color.alpha);
|
|
}
|
|
function asColorInformation(ci) {
|
|
return new code.ColorInformation(asRange(ci.range), asColor(ci.color));
|
|
}
|
|
async function asColorInformations(colorInformation, token) {
|
|
if (!colorInformation) {
|
|
return undefined;
|
|
}
|
|
return async.map(colorInformation, asColorInformation, token);
|
|
}
|
|
function asColorPresentation(cp) {
|
|
let presentation = new code.ColorPresentation(cp.label);
|
|
presentation.additionalTextEdits = asTextEditsSync(cp.additionalTextEdits);
|
|
if (cp.textEdit) {
|
|
presentation.textEdit = asTextEdit(cp.textEdit);
|
|
}
|
|
return presentation;
|
|
}
|
|
async function asColorPresentations(colorPresentations, token) {
|
|
if (!colorPresentations) {
|
|
return undefined;
|
|
}
|
|
return async.map(colorPresentations, asColorPresentation, token);
|
|
}
|
|
function asFoldingRangeKind(kind) {
|
|
if (kind) {
|
|
switch (kind) {
|
|
case ls.FoldingRangeKind.Comment:
|
|
return code.FoldingRangeKind.Comment;
|
|
case ls.FoldingRangeKind.Imports:
|
|
return code.FoldingRangeKind.Imports;
|
|
case ls.FoldingRangeKind.Region:
|
|
return code.FoldingRangeKind.Region;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
function asFoldingRange(r) {
|
|
return new code.FoldingRange(r.startLine, r.endLine, asFoldingRangeKind(r.kind));
|
|
}
|
|
async function asFoldingRanges(foldingRanges, token) {
|
|
if (!foldingRanges) {
|
|
return undefined;
|
|
}
|
|
return async.map(foldingRanges, asFoldingRange, token);
|
|
}
|
|
function asSelectionRange(selectionRange) {
|
|
return new code.SelectionRange(asRange(selectionRange.range), selectionRange.parent ? asSelectionRange(selectionRange.parent) : undefined);
|
|
}
|
|
async function asSelectionRanges(selectionRanges, token) {
|
|
if (!Array.isArray(selectionRanges)) {
|
|
return [];
|
|
}
|
|
return async.map(selectionRanges, asSelectionRange, token);
|
|
}
|
|
function asInlineValue(inlineValue) {
|
|
if (ls.InlineValueText.is(inlineValue)) {
|
|
return new code.InlineValueText(asRange(inlineValue.range), inlineValue.text);
|
|
}
|
|
else if (ls.InlineValueVariableLookup.is(inlineValue)) {
|
|
return new code.InlineValueVariableLookup(asRange(inlineValue.range), inlineValue.variableName, inlineValue.caseSensitiveLookup);
|
|
}
|
|
else {
|
|
return new code.InlineValueEvaluatableExpression(asRange(inlineValue.range), inlineValue.expression);
|
|
}
|
|
}
|
|
async function asInlineValues(inlineValues, token) {
|
|
if (!Array.isArray(inlineValues)) {
|
|
return [];
|
|
}
|
|
return async.map(inlineValues, asInlineValue, token);
|
|
}
|
|
async function asInlayHint(value, token) {
|
|
const label = typeof value.label === 'string'
|
|
? value.label
|
|
: await async.map(value.label, asInlayHintLabelPart, token);
|
|
const result = new protocolInlayHint_1.default(asPosition(value.position), label);
|
|
if (value.kind !== undefined) {
|
|
result.kind = value.kind;
|
|
}
|
|
if (value.textEdits !== undefined) {
|
|
result.textEdits = await asTextEdits(value.textEdits, token);
|
|
}
|
|
if (value.tooltip !== undefined) {
|
|
result.tooltip = asTooltip(value.tooltip);
|
|
}
|
|
if (value.paddingLeft !== undefined) {
|
|
result.paddingLeft = value.paddingLeft;
|
|
}
|
|
if (value.paddingRight !== undefined) {
|
|
result.paddingRight = value.paddingRight;
|
|
}
|
|
if (value.data !== undefined) {
|
|
result.data = value.data;
|
|
}
|
|
return result;
|
|
}
|
|
function asInlayHintLabelPart(part) {
|
|
const result = new code.InlayHintLabelPart(part.value);
|
|
if (part.location !== undefined) {
|
|
result.location = asLocation(part.location);
|
|
}
|
|
if (part.tooltip !== undefined) {
|
|
result.tooltip = asTooltip(part.tooltip);
|
|
}
|
|
if (part.command !== undefined) {
|
|
result.command = asCommand(part.command);
|
|
}
|
|
return result;
|
|
}
|
|
function asTooltip(value) {
|
|
if (typeof value === 'string') {
|
|
return value;
|
|
}
|
|
return asMarkdownString(value);
|
|
}
|
|
async function asInlayHints(values, token) {
|
|
if (!Array.isArray(values)) {
|
|
return undefined;
|
|
}
|
|
return async.mapAsync(values, asInlayHint, token);
|
|
}
|
|
function asCallHierarchyItem(item) {
|
|
if (item === null) {
|
|
return undefined;
|
|
}
|
|
const result = new protocolCallHierarchyItem_1.default(asSymbolKind(item.kind), item.name, item.detail || '', asUri(item.uri), asRange(item.range), asRange(item.selectionRange), item.data);
|
|
if (item.tags !== undefined) {
|
|
result.tags = asSymbolTags(item.tags);
|
|
}
|
|
return result;
|
|
}
|
|
async function asCallHierarchyItems(items, token) {
|
|
if (items === null) {
|
|
return undefined;
|
|
}
|
|
return async.map(items, asCallHierarchyItem, token);
|
|
}
|
|
async function asCallHierarchyIncomingCall(item, token) {
|
|
return new code.CallHierarchyIncomingCall(asCallHierarchyItem(item.from), await asRanges(item.fromRanges, token));
|
|
}
|
|
async function asCallHierarchyIncomingCalls(items, token) {
|
|
if (items === null) {
|
|
return undefined;
|
|
}
|
|
return async.mapAsync(items, asCallHierarchyIncomingCall, token);
|
|
}
|
|
async function asCallHierarchyOutgoingCall(item, token) {
|
|
return new code.CallHierarchyOutgoingCall(asCallHierarchyItem(item.to), await asRanges(item.fromRanges, token));
|
|
}
|
|
async function asCallHierarchyOutgoingCalls(items, token) {
|
|
if (items === null) {
|
|
return undefined;
|
|
}
|
|
return async.mapAsync(items, asCallHierarchyOutgoingCall, token);
|
|
}
|
|
async function asSemanticTokens(value, _token) {
|
|
if (value === undefined || value === null) {
|
|
return undefined;
|
|
}
|
|
return new code.SemanticTokens(new Uint32Array(value.data), value.resultId);
|
|
}
|
|
function asSemanticTokensEdit(value) {
|
|
return new code.SemanticTokensEdit(value.start, value.deleteCount, value.data !== undefined ? new Uint32Array(value.data) : undefined);
|
|
}
|
|
async function asSemanticTokensEdits(value, _token) {
|
|
if (value === undefined || value === null) {
|
|
return undefined;
|
|
}
|
|
return new code.SemanticTokensEdits(value.edits.map(asSemanticTokensEdit), value.resultId);
|
|
}
|
|
function asSemanticTokensLegend(value) {
|
|
return value;
|
|
}
|
|
async function asLinkedEditingRanges(value, token) {
|
|
if (value === null || value === undefined) {
|
|
return undefined;
|
|
}
|
|
return new code.LinkedEditingRanges(await asRanges(value.ranges, token), asRegularExpression(value.wordPattern));
|
|
}
|
|
function asRegularExpression(value) {
|
|
if (value === null || value === undefined) {
|
|
return undefined;
|
|
}
|
|
return new RegExp(value);
|
|
}
|
|
function asTypeHierarchyItem(item) {
|
|
if (item === null) {
|
|
return undefined;
|
|
}
|
|
let result = new protocolTypeHierarchyItem_1.default(asSymbolKind(item.kind), item.name, item.detail || '', asUri(item.uri), asRange(item.range), asRange(item.selectionRange), item.data);
|
|
if (item.tags !== undefined) {
|
|
result.tags = asSymbolTags(item.tags);
|
|
}
|
|
return result;
|
|
}
|
|
async function asTypeHierarchyItems(items, token) {
|
|
if (items === null) {
|
|
return undefined;
|
|
}
|
|
return async.map(items, asTypeHierarchyItem, token);
|
|
}
|
|
function asGlobPattern(pattern) {
|
|
if (Is.string(pattern)) {
|
|
return pattern;
|
|
}
|
|
if (ls.RelativePattern.is(pattern)) {
|
|
if (ls.URI.is(pattern.baseUri)) {
|
|
return new code.RelativePattern(asUri(pattern.baseUri), pattern.pattern);
|
|
}
|
|
else if (ls.WorkspaceFolder.is(pattern.baseUri)) {
|
|
const workspaceFolder = code.workspace.getWorkspaceFolder(asUri(pattern.baseUri.uri));
|
|
return workspaceFolder !== undefined ? new code.RelativePattern(workspaceFolder, pattern.pattern) : undefined;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
async function asInlineCompletionResult(value, token) {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return async.map(value, (item) => asInlineCompletionItem(item), token);
|
|
}
|
|
const list = value;
|
|
const converted = await async.map(list.items, (item) => {
|
|
return asInlineCompletionItem(item);
|
|
}, token);
|
|
return new code.InlineCompletionList(converted);
|
|
}
|
|
function asInlineCompletionItem(item) {
|
|
let insertText;
|
|
if (typeof item.insertText === 'string') {
|
|
insertText = item.insertText;
|
|
}
|
|
else {
|
|
insertText = new code.SnippetString(item.insertText.value);
|
|
}
|
|
let command = undefined;
|
|
if (item.command) {
|
|
command = asCommand(item.command);
|
|
}
|
|
const inlineCompletionItem = new code.InlineCompletionItem(insertText, asRange(item.range), command);
|
|
if (item.filterText) {
|
|
inlineCompletionItem.filterText = item.filterText;
|
|
}
|
|
return inlineCompletionItem;
|
|
}
|
|
return {
|
|
asUri,
|
|
asDocumentSelector,
|
|
asDiagnostics,
|
|
asDiagnostic,
|
|
asRange,
|
|
asRanges,
|
|
asPosition,
|
|
asDiagnosticSeverity,
|
|
asDiagnosticTag,
|
|
asHover,
|
|
asCompletionResult,
|
|
asCompletionItem,
|
|
asTextEdit,
|
|
asTextEdits,
|
|
asSignatureHelp,
|
|
asSignatureInformations,
|
|
asSignatureInformation,
|
|
asParameterInformations,
|
|
asParameterInformation,
|
|
asDeclarationResult,
|
|
asDefinitionResult,
|
|
asLocation,
|
|
asReferences,
|
|
asDocumentHighlights,
|
|
asDocumentHighlight,
|
|
asDocumentHighlightKind,
|
|
asSymbolKind,
|
|
asSymbolTag,
|
|
asSymbolTags,
|
|
asSymbolInformations,
|
|
asSymbolInformation,
|
|
asDocumentSymbols,
|
|
asDocumentSymbol,
|
|
asCommand,
|
|
asCommands,
|
|
asCodeAction,
|
|
asCodeActionKind,
|
|
asCodeActionKinds,
|
|
asCodeActionResult,
|
|
asCodeLens,
|
|
asCodeLenses,
|
|
asWorkspaceEdit,
|
|
asDocumentLink,
|
|
asDocumentLinks,
|
|
asFoldingRangeKind,
|
|
asFoldingRange,
|
|
asFoldingRanges,
|
|
asColor,
|
|
asColorInformation,
|
|
asColorInformations,
|
|
asColorPresentation,
|
|
asColorPresentations,
|
|
asSelectionRange,
|
|
asSelectionRanges,
|
|
asInlineValue,
|
|
asInlineValues,
|
|
asInlayHint,
|
|
asInlayHints,
|
|
asSemanticTokensLegend,
|
|
asSemanticTokens,
|
|
asSemanticTokensEdit,
|
|
asSemanticTokensEdits,
|
|
asCallHierarchyItem,
|
|
asCallHierarchyItems,
|
|
asCallHierarchyIncomingCall,
|
|
asCallHierarchyIncomingCalls,
|
|
asCallHierarchyOutgoingCall,
|
|
asCallHierarchyOutgoingCalls,
|
|
asLinkedEditingRanges: asLinkedEditingRanges,
|
|
asTypeHierarchyItem,
|
|
asTypeHierarchyItems,
|
|
asGlobPattern,
|
|
asInlineCompletionResult,
|
|
asInlineCompletionItem
|
|
};
|
|
}
|
|
exports.createConverter = createConverter;
|