From 31ba10c09703861377e4ccca02bc7717c5ee443b Mon Sep 17 00:00:00 2001 From: Ani Amirjanyan <56698448+anijanyan@users.noreply.github.com> Date: Tue, 20 Dec 2022 17:17:58 +0400 Subject: [PATCH] Do resolve (#14) * do resolve completion * conflicts resolve --- packages/ace-linters/language-provider.ts | 19 +++++++++-- packages/ace-linters/message-controller.ts | 27 ++++------------ packages/ace-linters/message-types.ts | 12 ++++++- packages/ace-linters/services/base-service.ts | 4 +++ .../ace-linters/services/css/css-service.ts | 14 +++++++- .../ace-linters/services/html/html-service.ts | 13 +++++++- .../ace-linters/services/json/json-service.ts | 9 +++++- .../services/language-service.d.ts | 2 ++ .../services/typescript/typescript-service.ts | 27 ++++++++++++---- .../type-converters/common-converters.ts | 7 +--- .../type-converters/typescript-converters.ts | 32 +++++++++++++------ .../type-converters/vscode-converters.ts | 25 +++++++++------ packages/ace-linters/web.worker.ts | 11 ++++--- 13 files changed, 140 insertions(+), 62 deletions(-) diff --git a/packages/ace-linters/language-provider.ts b/packages/ace-linters/language-provider.ts index 2c63b7b6..9ad48093 100644 --- a/packages/ace-linters/language-provider.ts +++ b/packages/ace-linters/language-provider.ts @@ -5,8 +5,8 @@ import {AceLinters} from "./services/language-service"; import Tooltip = AceLinters.Tooltip; import TextEdit = AceLinters.TextEdit; import {FormattingOptions} from "vscode-languageserver-types"; -import {CommonConverter} from "./type-converters/common-converters"; import {MarkDownConverter} from "./type-converters/converters"; +import {CommonConverter} from "./type-converters/common-converters"; let showdown = require('showdown'); @@ -152,10 +152,23 @@ export class LanguageProvider { { getCompletions: async (editor, session, pos, prefix, callback) => { this.doComplete((completions) => { - callback(null, CommonConverter.toCompletions(completions, this.$markdownConverter)); + callback(null, CommonConverter.normalizeRanges(completions)); }); }, - getDocTooltip(item: Ace.Completion) { + getDocTooltip: (item) => { + if (!item["isResolved"]) { + this.$message.doResolve(this.$fileName, item, (completion) => { + item["isResolved"] = true; + item.docText = completion.docText; + if (completion.docHTML) { + item.docHTML = completion.docHTML; + } else if (completion["docMarkdown"]) { + item.docHTML = CommonConverter.cleanHtml(this.$markdownConverter.makeHtml(completion["docMarkdown"])); + } + this.editor["completer"].updateDocTooltip(); + }) + } + return item; } } ]; diff --git a/packages/ace-linters/message-controller.ts b/packages/ace-linters/message-controller.ts index 5ee30e5c..14ef3a89 100644 --- a/packages/ace-linters/message-controller.ts +++ b/packages/ace-linters/message-controller.ts @@ -8,7 +8,7 @@ import { FormatMessage, GlobalOptionsMessage, HoverMessage, InitMessage, - MessageType, + ResolveCompletionMessage, ValidateMessage } from "./message-types"; import * as oop from "ace-code/src/lib/oop"; @@ -35,25 +35,8 @@ export class MessageController { this.$worker.onmessage = (e) => { let message = e.data; - let data = null; - switch (message.type as MessageType) { - case MessageType.format: - data = message.edits; - break; - case MessageType.complete: - data = message.completions; - break; - case MessageType.hover: - data = message.hover; - break; - case MessageType.validate: - data = message.annotations; - break; - default: - break; - } - this["_signal"](message.type + "-" + message.sessionId, data); + this["_signal"](message.type + "-" + message.sessionId, message.value); }; } @@ -65,10 +48,14 @@ export class MessageController { this.postMessage(new ValidateMessage(sessionId), callback); } - doComplete(sessionId: string, position: Ace.Point, callback?: (CompletionList) => void) { + doComplete(sessionId: string, position: Ace.Point, callback?: (completionList: Ace.Completion[]) => void) { this.postMessage(new CompleteMessage(sessionId, position), callback); } + doResolve(sessionId: string, completion: Ace.Completion, callback?: (completion: Ace.Completion) => void) { + this.postMessage(new ResolveCompletionMessage(sessionId, completion), callback); + } + format(sessionId: string, range: Ace.Range, format: FormattingOptions, callback?: (edits: AceLinters.TextEdit[]) => void) { this.postMessage(new FormatMessage(sessionId, range, format), callback); } diff --git a/packages/ace-linters/message-types.ts b/packages/ace-linters/message-types.ts index 2cf1fde8..01cd1145 100644 --- a/packages/ace-linters/message-types.ts +++ b/packages/ace-linters/message-types.ts @@ -48,6 +48,16 @@ export class CompleteMessage extends BaseMessage { } } +export class ResolveCompletionMessage extends BaseMessage { + type: MessageType = MessageType.resolveCompletion; + value: Ace.Completion; + + constructor(sessionId: string, value: Ace.Completion) { + super(sessionId); + this.value = value; + } +} + export class HoverMessage extends BaseMessage { type: MessageType = MessageType.hover; value: Ace.Point; @@ -128,5 +138,5 @@ export class GlobalOptionsMessage { } export enum MessageType { - init, format, complete, change, hover, validate, applyDelta, changeMode, changeOptions, dispose, globalOptions + init, format, complete, resolveCompletion, change, hover, validate, applyDelta, changeMode, changeOptions, dispose, globalOptions } \ No newline at end of file diff --git a/packages/ace-linters/services/base-service.ts b/packages/ace-linters/services/base-service.ts index 7acc7739..4a453ea9 100644 --- a/packages/ace-linters/services/base-service.ts +++ b/packages/ace-linters/services/base-service.ts @@ -115,4 +115,8 @@ export abstract class BaseService { return; } + + async resolveCompletion(sessionID: string, completion: Ace.Completion): Promise { + return; + } } \ No newline at end of file diff --git a/packages/ace-linters/services/css/css-service.ts b/packages/ace-linters/services/css/css-service.ts index 6cdb54b8..1b29f85f 100644 --- a/packages/ace-linters/services/css/css-service.ts +++ b/packages/ace-linters/services/css/css-service.ts @@ -1,6 +1,14 @@ import {LanguageService as VSLanguageService} from "vscode-css-languageservice"; import {Ace} from "ace-code"; -import {fromPoint, fromRange, toAceTextEdits, toAnnotations, toCompletions, toTooltip} from "../../type-converters/vscode-converters"; +import { + fromPoint, + fromRange, + toAceTextEdits, + toAnnotations, + toCompletions, + toResolvedCompletion, + toTooltip +} from "../../type-converters/vscode-converters"; import {CSSFormatConfiguration} from "vscode-css-languageservice/lib/umd/cssLanguageTypes"; import {BaseService} from "../base-service"; @@ -79,4 +87,8 @@ export class CssService extends BaseService { let completions = this.$service.doComplete(document, fromPoint(position), cssDocument); return toCompletions(completions); } + + async resolveCompletion(sessionID: string, completion: Ace.Completion): Promise { + return toResolvedCompletion(completion, completion["item"]); + } } \ No newline at end of file diff --git a/packages/ace-linters/services/html/html-service.ts b/packages/ace-linters/services/html/html-service.ts index af8a5f7c..6ab2e315 100644 --- a/packages/ace-linters/services/html/html-service.ts +++ b/packages/ace-linters/services/html/html-service.ts @@ -1,6 +1,13 @@ import {LanguageService as VSLanguageService} from "vscode-html-languageservice"; import {Ace} from "ace-code"; -import {fromPoint, fromRange, toAceTextEdits, toCompletions, toTooltip} from "../../type-converters/vscode-converters"; +import { + fromPoint, + fromRange, + toAceTextEdits, + toCompletions, + toResolvedCompletion, + toTooltip +} from "../../type-converters/vscode-converters"; import {HTMLFormatConfiguration} from "vscode-html-languageservice/lib/umd/htmlLanguageTypes"; import {BaseService} from "../base-service"; @@ -54,4 +61,8 @@ export class HtmlService extends BaseService { let completions = this.$service.doComplete(document, fromPoint(position), htmlDocument); return toCompletions(completions); } + + async resolveCompletion(sessionID: string, completion: Ace.Completion): Promise { + return toResolvedCompletion(completion, completion["item"]); + } } \ No newline at end of file diff --git a/packages/ace-linters/services/json/json-service.ts b/packages/ace-linters/services/json/json-service.ts index 871d6ae4..d9b70bc7 100644 --- a/packages/ace-linters/services/json/json-service.ts +++ b/packages/ace-linters/services/json/json-service.ts @@ -9,7 +9,7 @@ import { fromRange, toAceTextEdits, toAnnotations, - toCompletions, + toCompletions, toResolvedCompletion, toTooltip } from "../../type-converters/vscode-converters"; import {BaseService} from "../base-service"; @@ -97,6 +97,13 @@ export class JsonService extends BaseService { } let jsonDocument = this.$service.parseJSONDocument(document); let completions = await this.$service.doComplete(document, fromPoint(position), jsonDocument); + return toCompletions(completions); } + + async resolveCompletion(sessionID: string, completion: Ace.Completion): Promise { + let resolvedCompletion = await this.$service.doResolve(completion["item"]); + + return toResolvedCompletion(completion, resolvedCompletion); + } } \ No newline at end of file diff --git a/packages/ace-linters/services/language-service.d.ts b/packages/ace-linters/services/language-service.d.ts index 82f614ce..da62b4c6 100644 --- a/packages/ace-linters/services/language-service.d.ts +++ b/packages/ace-linters/services/language-service.d.ts @@ -20,6 +20,8 @@ export declare namespace AceLinters { doComplete(sessionID: string, position: Ace.Point): Promise; + resolveCompletion(sessionID: string, completion: Ace.Completion): Promise; + setValue(sessionID: string, value: string); applyDeltas(sessionID: string, delta: Ace.Delta[]); diff --git a/packages/ace-linters/services/typescript/typescript-service.ts b/packages/ace-linters/services/typescript/typescript-service.ts index 833e9fa4..8ea8905d 100644 --- a/packages/ace-linters/services/typescript/typescript-service.ts +++ b/packages/ace-linters/services/typescript/typescript-service.ts @@ -4,13 +4,13 @@ import * as ts from './lib/typescriptServices'; import {Diagnostic} from './lib/typescriptServices'; import {libFileMap} from "./lib/lib"; import { - fromTsDiagnostics, JsxEmit, + fromTsDiagnostics, ScriptTarget, - toAceTextEdits, + toAceTextEdits, toResolvedCompletion, toCompletions, toIndex, toTooltip, - toTsOffset + toTsOffset, JsxEmit } from "../../type-converters/typescript-converters"; import TsServiceOptions = AceLinters.TsServiceOptions; import {AceLinters} from "../language-service"; @@ -180,12 +180,27 @@ export class TypescriptService extends BaseService implements return fromTsDiagnostics([...syntacticDiagnostics, ...semanticDiagnostics], document); } - async doComplete(sessionID: string, position: Ace.Point) { + async doComplete(sessionID: string, point: Ace.Point) { let document = this.getDocument(sessionID); if (!document) { return null; } - let completions = this.$service.getCompletionsAtPosition(sessionID, toIndex(position, document), undefined); - return toCompletions(completions, document); + let position = toIndex(point, document); + let completions = this.$service.getCompletionsAtPosition(sessionID, position, undefined); + return toCompletions(completions, document, sessionID, position); + } + + async resolveCompletion(sessionID: string, completion: Ace.Completion): Promise { + let resolvedCompletion = this.$service.getCompletionEntryDetails( + sessionID, + completion["position"], + completion["entry"], + undefined, + undefined, + undefined, + undefined + ); + + return toResolvedCompletion(resolvedCompletion); } } \ No newline at end of file diff --git a/packages/ace-linters/type-converters/common-converters.ts b/packages/ace-linters/type-converters/common-converters.ts index d04eb421..a86b1a06 100644 --- a/packages/ace-linters/type-converters/common-converters.ts +++ b/packages/ace-linters/type-converters/common-converters.ts @@ -1,13 +1,8 @@ import {Ace, Range as AceRange} from "ace-code"; -import {MarkDownConverter} from "./converters"; export namespace CommonConverter { - export function toCompletions(completions: Ace.Completion[], markdownConverter: MarkDownConverter): Ace.Completion[] { + export function normalizeRanges(completions: Ace.Completion[]): Ace.Completion[] { return completions && completions.map((el) => { - if (el["docMarkdown"]) { - el["docHTML"] = cleanHtml(markdownConverter.makeHtml(el["docMarkdown"])); - el["docMarkdown"] = undefined; - } if (el["range"]) { el["range"] = toRange(el["range"]); } diff --git a/packages/ace-linters/type-converters/typescript-converters.ts b/packages/ace-linters/type-converters/typescript-converters.ts index 4ac74753..ae6a9475 100644 --- a/packages/ace-linters/type-converters/typescript-converters.ts +++ b/packages/ace-linters/type-converters/typescript-converters.ts @@ -1,4 +1,5 @@ import { + CompletionEntryDetails, CompletionInfo, Diagnostic, JSDocTagInfo, @@ -116,27 +117,38 @@ function tagToString(tag: JSDocTagInfo): string { return tagLabel; } -export function toCompletions(completionInfo: CompletionInfo, doc: Ace.Document): Ace.Completion[] { +export function toCompletions(completionInfo: CompletionInfo, doc: Ace.Document, fileName: string, position: number): Ace.Completion[] { return completionInfo && completionInfo.entries.map((entry) => { - let range; - if (entry.replacementSpan) { - const p1 = toPoint(entry.replacementSpan.start, doc); - const p2 = toPoint(entry.replacementSpan.start + entry.replacementSpan.length, doc); - range = CommonConverter.toRange({start: p1, end: p2}); - } - let completion = { meta: entry.kind, caption: entry.name, - range: range, value: "", snippet: entry.name, - score: parseInt(entry.sortText) + score: parseInt(entry.sortText), + position: position, + entry: entry.name }; + + if (entry.replacementSpan) { + const p1 = toPoint(entry.replacementSpan.start, doc); + const p2 = toPoint(entry.replacementSpan.start + entry.replacementSpan.length, doc); + completion["range"] = CommonConverter.toRange({start: p1, end: p2}); + } + return completion; }); } +export function toResolvedCompletion(entry: CompletionEntryDetails): Ace.Completion { + return { + meta: entry.kind, + caption: entry.name, + value: "", + snippet: entry.name, + docHTML: entry.displayParts.map((displayPart) => displayPart.text).join('') + }; +} + export enum ScriptKind { Unknown = 0, JS = 1, diff --git a/packages/ace-linters/type-converters/vscode-converters.ts b/packages/ace-linters/type-converters/vscode-converters.ts index 4992880c..751139bb 100644 --- a/packages/ace-linters/type-converters/vscode-converters.ts +++ b/packages/ace-linters/type-converters/vscode-converters.ts @@ -4,6 +4,7 @@ import { Diagnostic, InsertTextFormat, CompletionList, + CompletionItem, CompletionItemKind, Hover, MarkupContent, MarkedString, MarkupKind, TextEdit } from "vscode-languageserver-types"; import type {Ace} from "ace-code"; @@ -68,16 +69,10 @@ export function toCompletions(completionList: CompletionList): Ace.Completion[] command: command, range: range, value: "", - score: null + score: null, + item: item }; - let doc = fromMarkupContent(item.documentation); - if (doc) { - if (doc.type === CommonConverter.TooltipType.markdown) { - completion["docMarkdown"] = doc.text; - } else { - completion["docText"] = doc.text; - } - } + if (item.insertTextFormat == InsertTextFormat.Snippet) { completion["snippet"] = text; } else { @@ -88,6 +83,18 @@ export function toCompletions(completionList: CompletionList): Ace.Completion[] ); } +export function toResolvedCompletion(completion: Ace.Completion, item: CompletionItem): Ace.Completion { + let doc = fromMarkupContent(item.documentation); + if (doc) { + if (doc.type === CommonConverter.TooltipType.markdown) { + completion["docMarkdown"] = doc.text; + } else { + completion["docText"] = doc.text; + } + } + return completion; +} + export function getTextEditRange(textEdit?): Ace.Range | undefined { if (!textEdit) { return; diff --git a/packages/ace-linters/web.worker.ts b/packages/ace-linters/web.worker.ts index 990413d7..de87eaf1 100644 --- a/packages/ace-linters/web.worker.ts +++ b/packages/ace-linters/web.worker.ts @@ -13,10 +13,13 @@ ctx.onmessage = async (ev) => { }; switch (message["type"] as MessageType) { case MessageType.format: - postMessage["edits"] = manager.getServiceInstance(sessionID).format(sessionID, message.value, message.format); + postMessage["value"] = manager.getServiceInstance(sessionID).format(sessionID, message.value, message.format); break; case MessageType.complete: - postMessage["completions"] = await manager.getServiceInstance(sessionID).doComplete(sessionID, message.value); + postMessage["value"] = await manager.getServiceInstance(sessionID).doComplete(sessionID, message.value); + break; + case MessageType.resolveCompletion: + postMessage["value"] = await manager.getServiceInstance(sessionID).resolveCompletion(sessionID, message.value); break; case MessageType.change: manager.getServiceInstance(sessionID).setValue(sessionID, message.value); @@ -25,10 +28,10 @@ ctx.onmessage = async (ev) => { manager.getServiceInstance(sessionID).applyDeltas(sessionID, message.value); break; case MessageType.hover: - postMessage["hover"] = await manager.getServiceInstance(sessionID).doHover(sessionID, message.value); + postMessage["value"] = await manager.getServiceInstance(sessionID).doHover(sessionID, message.value); break; case MessageType.validate: - postMessage["annotations"] = await manager.getServiceInstance(sessionID).doValidation(sessionID); + postMessage["value"] = await manager.getServiceInstance(sessionID).doValidation(sessionID); break; case MessageType.init: //this should be first message await manager.addDocument(sessionID, message.value, message.mode, message.options);