From 1c2358bdf99b38594d8932e26abba769b76bbe42 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 17 Nov 2021 10:56:06 +0100 Subject: [PATCH] Extract a common `CompletionAdapter` --- src/common/lspLanguageFeatures.ts | 251 +++++++++++++++++++++++++++++- src/css/cssMode.ts | 2 +- src/css/languageFeatures.ts | 199 ++--------------------- src/html/htmlMode.ts | 4 +- src/html/languageFeatures.ts | 238 ++-------------------------- src/json/jsonMode.ts | 2 +- src/json/languageFeatures.ts | 242 ++-------------------------- 7 files changed, 282 insertions(+), 656 deletions(-) diff --git a/src/common/lspLanguageFeatures.ts b/src/common/lspLanguageFeatures.ts index d61dbeeb..eae88394 100644 --- a/src/common/lspLanguageFeatures.ts +++ b/src/common/lspLanguageFeatures.ts @@ -29,12 +29,12 @@ export interface ILanguageWorkerWithDiagnostics { } export class DiagnosticsAdapter { - protected _disposables: IDisposable[] = []; - private _listener: { [uri: string]: IDisposable } = Object.create(null); + protected readonly _disposables: IDisposable[] = []; + private readonly _listener: { [uri: string]: IDisposable } = Object.create(null); constructor( - private _languageId: string, - protected _worker: WorkerAccessor, + private readonly _languageId: string, + protected readonly _worker: WorkerAccessor, configChangeEvent: IEvent ) { const onModelAdd = (model: editor.IModel): void => { @@ -97,7 +97,7 @@ export class DiagnosticsAdapter { public dispose(): void { this._disposables.forEach((d) => d && d.dispose()); - this._disposables = []; + this._disposables.length = 0; } private _doValidate(resource: Uri, languageId: string): void { @@ -149,3 +149,244 @@ function toDiagnostics(resource: Uri, diag: lsTypes.Diagnostic): editor.IMarkerD } //#endregion + +//#region CompletionAdapter + +export interface ILanguageWorkerWithCompletions { + doComplete(uri: string, position: lsTypes.Position): Promise; +} + +export class CompletionAdapter + implements languages.CompletionItemProvider +{ + constructor( + private readonly _worker: WorkerAccessor, + private readonly _triggerCharacters: string[] + ) {} + + public get triggerCharacters(): string[] { + return this._triggerCharacters; + } + + provideCompletionItems( + model: editor.IReadOnlyModel, + position: Position, + context: languages.CompletionContext, + token: CancellationToken + ): Promise { + const resource = model.uri; + + return this._worker(resource) + .then((worker) => { + return worker.doComplete(resource.toString(), fromPosition(position)); + }) + .then((info) => { + if (!info) { + return; + } + const wordInfo = model.getWordUntilPosition(position); + const wordRange = new Range( + position.lineNumber, + wordInfo.startColumn, + position.lineNumber, + wordInfo.endColumn + ); + + const items: languages.CompletionItem[] = info.items.map((entry) => { + const item: languages.CompletionItem = { + label: entry.label, + insertText: entry.insertText || entry.label, + sortText: entry.sortText, + filterText: entry.filterText, + documentation: entry.documentation, + detail: entry.detail, + command: toCommand(entry.command), + range: wordRange, + kind: toCompletionItemKind(entry.kind) + }; + if (entry.textEdit) { + if (isInsertReplaceEdit(entry.textEdit)) { + item.range = { + insert: toRange(entry.textEdit.insert), + replace: toRange(entry.textEdit.replace) + }; + } else { + item.range = toRange(entry.textEdit.range); + } + item.insertText = entry.textEdit.newText; + } + if (entry.additionalTextEdits) { + item.additionalTextEdits = + entry.additionalTextEdits.map(toTextEdit); + } + if (entry.insertTextFormat === lsTypes.InsertTextFormat.Snippet) { + item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; + } + return item; + }); + + return { + isIncomplete: info.isIncomplete, + suggestions: items + }; + }); + } +} + +export function fromPosition(position: Position): lsTypes.Position; +export function fromPosition(position: undefined): undefined; +export function fromPosition(position: Position | undefined): lsTypes.Position | undefined; +export function fromPosition(position: Position | undefined): lsTypes.Position | undefined { + if (!position) { + return void 0; + } + return { character: position.column - 1, line: position.lineNumber - 1 }; +} + +export function fromRange(range: IRange): lsTypes.Range; +export function fromRange(range: undefined): undefined; +export function fromRange(range: IRange | undefined): lsTypes.Range | undefined; +export function fromRange(range: IRange | undefined): lsTypes.Range | undefined { + if (!range) { + return void 0; + } + return { + start: { + line: range.startLineNumber - 1, + character: range.startColumn - 1 + }, + end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } + }; +} +export function toRange(range: lsTypes.Range): Range; +export function toRange(range: undefined): undefined; +export function toRange(range: lsTypes.Range | undefined): Range | undefined; +export function toRange(range: lsTypes.Range | undefined): Range | undefined { + if (!range) { + return void 0; + } + return new Range( + range.start.line + 1, + range.start.character + 1, + range.end.line + 1, + range.end.character + 1 + ); +} + +function isInsertReplaceEdit( + edit: lsTypes.TextEdit | lsTypes.InsertReplaceEdit +): edit is lsTypes.InsertReplaceEdit { + return ( + typeof (edit).insert !== 'undefined' && + typeof (edit).replace !== 'undefined' + ); +} + +function toCompletionItemKind(kind: number | undefined): languages.CompletionItemKind { + const mItemKind = languages.CompletionItemKind; + + switch (kind) { + case lsTypes.CompletionItemKind.Text: + return mItemKind.Text; + case lsTypes.CompletionItemKind.Method: + return mItemKind.Method; + case lsTypes.CompletionItemKind.Function: + return mItemKind.Function; + case lsTypes.CompletionItemKind.Constructor: + return mItemKind.Constructor; + case lsTypes.CompletionItemKind.Field: + return mItemKind.Field; + case lsTypes.CompletionItemKind.Variable: + return mItemKind.Variable; + case lsTypes.CompletionItemKind.Class: + return mItemKind.Class; + case lsTypes.CompletionItemKind.Interface: + return mItemKind.Interface; + case lsTypes.CompletionItemKind.Module: + return mItemKind.Module; + case lsTypes.CompletionItemKind.Property: + return mItemKind.Property; + case lsTypes.CompletionItemKind.Unit: + return mItemKind.Unit; + case lsTypes.CompletionItemKind.Value: + return mItemKind.Value; + case lsTypes.CompletionItemKind.Enum: + return mItemKind.Enum; + case lsTypes.CompletionItemKind.Keyword: + return mItemKind.Keyword; + case lsTypes.CompletionItemKind.Snippet: + return mItemKind.Snippet; + case lsTypes.CompletionItemKind.Color: + return mItemKind.Color; + case lsTypes.CompletionItemKind.File: + return mItemKind.File; + case lsTypes.CompletionItemKind.Reference: + return mItemKind.Reference; + } + return mItemKind.Property; +} + +function fromCompletionItemKind(kind: languages.CompletionItemKind): lsTypes.CompletionItemKind { + const mItemKind = languages.CompletionItemKind; + + switch (kind) { + case mItemKind.Text: + return lsTypes.CompletionItemKind.Text; + case mItemKind.Method: + return lsTypes.CompletionItemKind.Method; + case mItemKind.Function: + return lsTypes.CompletionItemKind.Function; + case mItemKind.Constructor: + return lsTypes.CompletionItemKind.Constructor; + case mItemKind.Field: + return lsTypes.CompletionItemKind.Field; + case mItemKind.Variable: + return lsTypes.CompletionItemKind.Variable; + case mItemKind.Class: + return lsTypes.CompletionItemKind.Class; + case mItemKind.Interface: + return lsTypes.CompletionItemKind.Interface; + case mItemKind.Module: + return lsTypes.CompletionItemKind.Module; + case mItemKind.Property: + return lsTypes.CompletionItemKind.Property; + case mItemKind.Unit: + return lsTypes.CompletionItemKind.Unit; + case mItemKind.Value: + return lsTypes.CompletionItemKind.Value; + case mItemKind.Enum: + return lsTypes.CompletionItemKind.Enum; + case mItemKind.Keyword: + return lsTypes.CompletionItemKind.Keyword; + case mItemKind.Snippet: + return lsTypes.CompletionItemKind.Snippet; + case mItemKind.Color: + return lsTypes.CompletionItemKind.Color; + case mItemKind.File: + return lsTypes.CompletionItemKind.File; + case mItemKind.Reference: + return lsTypes.CompletionItemKind.Reference; + } + return lsTypes.CompletionItemKind.Property; +} + +export function toTextEdit(textEdit: lsTypes.TextEdit): languages.TextEdit; +export function toTextEdit(textEdit: undefined): undefined; +export function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined; +export function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined { + if (!textEdit) { + return void 0; + } + return { + range: toRange(textEdit.range), + text: textEdit.newText + }; +} + +function toCommand(c: lsTypes.Command | undefined): languages.Command | undefined { + return c && c.command === 'editor.action.triggerSuggest' + ? { id: c.command, title: c.title, arguments: c.arguments } + : undefined; +} + +//#endregion diff --git a/src/css/cssMode.ts b/src/css/cssMode.ts index 6e28ab08..7d8d3b46 100644 --- a/src/css/cssMode.ts +++ b/src/css/cssMode.ts @@ -29,7 +29,7 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable { providers.push( languages.registerCompletionItemProvider( languageId, - new languageFeatures.CompletionAdapter(worker) + new languageFeatures.CSSCompletionAdapter(worker) ) ); } diff --git a/src/css/languageFeatures.ts b/src/css/languageFeatures.ts index d6dbe63a..1b957c00 100644 --- a/src/css/languageFeatures.ts +++ b/src/css/languageFeatures.ts @@ -12,207 +12,30 @@ import { IMarkdownString, Uri, Position, - IRange, - Range, CancellationToken } from '../fillers/monaco-editor-core'; -import { DiagnosticsAdapter } from '../common/lspLanguageFeatures'; +import { + DiagnosticsAdapter, + fromPosition, + toRange, + toTextEdit, + fromRange, + CompletionAdapter +} from '../common/lspLanguageFeatures'; export interface WorkerAccessor { (first: Uri, ...more: Uri[]): Promise; } -// --- diagnostics --- --- - export class CSSDiagnosticsAdapter extends DiagnosticsAdapter { constructor(languageId: string, worker: WorkerAccessor, defaults: LanguageServiceDefaults) { super(languageId, worker, defaults.onDidChange); } } -// --- completion ------ - -function fromPosition(position: Position): lsTypes.Position; -function fromPosition(position: undefined): undefined; -function fromPosition(position: Position | undefined): lsTypes.Position | undefined; -function fromPosition(position: Position | undefined): lsTypes.Position | undefined { - if (!position) { - return void 0; - } - return { character: position.column - 1, line: position.lineNumber - 1 }; -} - -function fromRange(range: IRange): lsTypes.Range; -function fromRange(range: undefined): undefined; -function fromRange(range: IRange | undefined): lsTypes.Range | undefined; -function fromRange(range: IRange | undefined): lsTypes.Range | undefined { - if (!range) { - return void 0; - } - return { - start: { - line: range.startLineNumber - 1, - character: range.startColumn - 1 - }, - end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } - }; -} -function toRange(range: lsTypes.Range): Range; -function toRange(range: undefined): undefined; -function toRange(range: lsTypes.Range | undefined): Range | undefined; -function toRange(range: lsTypes.Range | undefined): Range | undefined { - if (!range) { - return void 0; - } - return new Range( - range.start.line + 1, - range.start.character + 1, - range.end.line + 1, - range.end.character + 1 - ); -} - -function isInsertReplaceEdit( - edit: lsTypes.TextEdit | lsTypes.InsertReplaceEdit -): edit is lsTypes.InsertReplaceEdit { - return ( - typeof (edit).insert !== 'undefined' && - typeof (edit).replace !== 'undefined' - ); -} - -function toCompletionItemKind(kind: number | undefined): languages.CompletionItemKind { - const mItemKind = languages.CompletionItemKind; - - switch (kind) { - case lsTypes.CompletionItemKind.Text: - return mItemKind.Text; - case lsTypes.CompletionItemKind.Method: - return mItemKind.Method; - case lsTypes.CompletionItemKind.Function: - return mItemKind.Function; - case lsTypes.CompletionItemKind.Constructor: - return mItemKind.Constructor; - case lsTypes.CompletionItemKind.Field: - return mItemKind.Field; - case lsTypes.CompletionItemKind.Variable: - return mItemKind.Variable; - case lsTypes.CompletionItemKind.Class: - return mItemKind.Class; - case lsTypes.CompletionItemKind.Interface: - return mItemKind.Interface; - case lsTypes.CompletionItemKind.Module: - return mItemKind.Module; - case lsTypes.CompletionItemKind.Property: - return mItemKind.Property; - case lsTypes.CompletionItemKind.Unit: - return mItemKind.Unit; - case lsTypes.CompletionItemKind.Value: - return mItemKind.Value; - case lsTypes.CompletionItemKind.Enum: - return mItemKind.Enum; - case lsTypes.CompletionItemKind.Keyword: - return mItemKind.Keyword; - case lsTypes.CompletionItemKind.Snippet: - return mItemKind.Snippet; - case lsTypes.CompletionItemKind.Color: - return mItemKind.Color; - case lsTypes.CompletionItemKind.File: - return mItemKind.File; - case lsTypes.CompletionItemKind.Reference: - return mItemKind.Reference; - } - return mItemKind.Property; -} - -function toTextEdit(textEdit: lsTypes.TextEdit): languages.TextEdit; -function toTextEdit(textEdit: undefined): undefined; -function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined; -function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined { - if (!textEdit) { - return void 0; - } - return { - range: toRange(textEdit.range), - text: textEdit.newText - }; -} - -function toCommand(c: lsTypes.Command | undefined): languages.Command | undefined { - return c && c.command === 'editor.action.triggerSuggest' - ? { id: c.command, title: c.title, arguments: c.arguments } - : undefined; -} - -export class CompletionAdapter implements languages.CompletionItemProvider { - constructor(private _worker: WorkerAccessor) {} - - public get triggerCharacters(): string[] { - return ['/', '-', ':']; - } - - provideCompletionItems( - model: editor.IReadOnlyModel, - position: Position, - context: languages.CompletionContext, - token: CancellationToken - ): Promise { - const resource = model.uri; - - return this._worker(resource) - .then((worker) => { - return worker.doComplete(resource.toString(), fromPosition(position)); - }) - .then((info) => { - if (!info) { - return; - } - const wordInfo = model.getWordUntilPosition(position); - const wordRange = new Range( - position.lineNumber, - wordInfo.startColumn, - position.lineNumber, - wordInfo.endColumn - ); - - const items: languages.CompletionItem[] = info.items.map((entry) => { - const item: languages.CompletionItem = { - label: entry.label, - insertText: entry.insertText || entry.label, - sortText: entry.sortText, - filterText: entry.filterText, - documentation: entry.documentation, - detail: entry.detail, - command: toCommand(entry.command), - range: wordRange, - kind: toCompletionItemKind(entry.kind) - }; - if (entry.textEdit) { - if (isInsertReplaceEdit(entry.textEdit)) { - item.range = { - insert: toRange(entry.textEdit.insert), - replace: toRange(entry.textEdit.replace) - }; - } else { - item.range = toRange(entry.textEdit.range); - } - item.insertText = entry.textEdit.newText; - } - if (entry.additionalTextEdits) { - item.additionalTextEdits = - entry.additionalTextEdits.map(toTextEdit); - } - if (entry.insertTextFormat === lsTypes.InsertTextFormat.Snippet) { - item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; - } - return item; - }); - - return { - isIncomplete: info.isIncomplete, - suggestions: items - }; - }); +export class CSSCompletionAdapter extends CompletionAdapter { + constructor(worker: WorkerAccessor) { + super(worker, ['/', '-', ':']); } } diff --git a/src/html/htmlMode.ts b/src/html/htmlMode.ts index 47647a3a..9ec7a11f 100644 --- a/src/html/htmlMode.ts +++ b/src/html/htmlMode.ts @@ -21,7 +21,7 @@ export function setupMode1(defaults: LanguageServiceDefaults): void { // all modes languages.registerCompletionItemProvider( languageId, - new languageFeatures.CompletionAdapter(worker) + new languageFeatures.HTMLCompletionAdapter(worker) ); languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker)); @@ -77,7 +77,7 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable { providers.push( languages.registerCompletionItemProvider( languageId, - new languageFeatures.CompletionAdapter(worker) + new languageFeatures.HTMLCompletionAdapter(worker) ) ); } diff --git a/src/html/languageFeatures.ts b/src/html/languageFeatures.ts index 4819be36..0ff3766f 100644 --- a/src/html/languageFeatures.ts +++ b/src/html/languageFeatures.ts @@ -11,242 +11,24 @@ import { IMarkdownString, Uri, Position, - IRange, Range, CancellationToken } from '../fillers/monaco-editor-core'; +import { + fromPosition, + toRange, + toTextEdit, + fromRange, + CompletionAdapter +} from '../common/lspLanguageFeatures'; export interface WorkerAccessor { (...more: Uri[]): Promise; } -// --- completion ------ - -function fromPosition(position: Position): lsTypes.Position; -function fromPosition(position: undefined): undefined; -function fromPosition(position: Position | undefined): lsTypes.Position | undefined; -function fromPosition(position: Position | undefined): lsTypes.Position | undefined { - if (!position) { - return void 0; - } - return { character: position.column - 1, line: position.lineNumber - 1 }; -} - -function fromRange(range: IRange): lsTypes.Range; -function fromRange(range: undefined): undefined; -function fromRange(range: IRange | undefined): lsTypes.Range | undefined; -function fromRange(range: IRange | undefined): lsTypes.Range | undefined { - if (!range) { - return void 0; - } - return { - start: { - line: range.startLineNumber - 1, - character: range.startColumn - 1 - }, - end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } - }; -} -function toRange(range: lsTypes.Range): Range; -function toRange(range: undefined): undefined; -function toRange(range: lsTypes.Range | undefined): Range | undefined; -function toRange(range: lsTypes.Range | undefined): Range | undefined { - if (!range) { - return void 0; - } - return new Range( - range.start.line + 1, - range.start.character + 1, - range.end.line + 1, - range.end.character + 1 - ); -} - -function isInsertReplaceEdit( - edit: lsTypes.TextEdit | lsTypes.InsertReplaceEdit -): edit is lsTypes.InsertReplaceEdit { - return ( - typeof (edit).insert !== 'undefined' && - typeof (edit).replace !== 'undefined' - ); -} - -function toCompletionItemKind(kind: number | undefined): languages.CompletionItemKind { - const mItemKind = languages.CompletionItemKind; - - switch (kind) { - case lsTypes.CompletionItemKind.Text: - return mItemKind.Text; - case lsTypes.CompletionItemKind.Method: - return mItemKind.Method; - case lsTypes.CompletionItemKind.Function: - return mItemKind.Function; - case lsTypes.CompletionItemKind.Constructor: - return mItemKind.Constructor; - case lsTypes.CompletionItemKind.Field: - return mItemKind.Field; - case lsTypes.CompletionItemKind.Variable: - return mItemKind.Variable; - case lsTypes.CompletionItemKind.Class: - return mItemKind.Class; - case lsTypes.CompletionItemKind.Interface: - return mItemKind.Interface; - case lsTypes.CompletionItemKind.Module: - return mItemKind.Module; - case lsTypes.CompletionItemKind.Property: - return mItemKind.Property; - case lsTypes.CompletionItemKind.Unit: - return mItemKind.Unit; - case lsTypes.CompletionItemKind.Value: - return mItemKind.Value; - case lsTypes.CompletionItemKind.Enum: - return mItemKind.Enum; - case lsTypes.CompletionItemKind.Keyword: - return mItemKind.Keyword; - case lsTypes.CompletionItemKind.Snippet: - return mItemKind.Snippet; - case lsTypes.CompletionItemKind.Color: - return mItemKind.Color; - case lsTypes.CompletionItemKind.File: - return mItemKind.File; - case lsTypes.CompletionItemKind.Reference: - return mItemKind.Reference; - } - return mItemKind.Property; -} - -function fromCompletionItemKind(kind: languages.CompletionItemKind): lsTypes.CompletionItemKind { - const mItemKind = languages.CompletionItemKind; - - switch (kind) { - case mItemKind.Text: - return lsTypes.CompletionItemKind.Text; - case mItemKind.Method: - return lsTypes.CompletionItemKind.Method; - case mItemKind.Function: - return lsTypes.CompletionItemKind.Function; - case mItemKind.Constructor: - return lsTypes.CompletionItemKind.Constructor; - case mItemKind.Field: - return lsTypes.CompletionItemKind.Field; - case mItemKind.Variable: - return lsTypes.CompletionItemKind.Variable; - case mItemKind.Class: - return lsTypes.CompletionItemKind.Class; - case mItemKind.Interface: - return lsTypes.CompletionItemKind.Interface; - case mItemKind.Module: - return lsTypes.CompletionItemKind.Module; - case mItemKind.Property: - return lsTypes.CompletionItemKind.Property; - case mItemKind.Unit: - return lsTypes.CompletionItemKind.Unit; - case mItemKind.Value: - return lsTypes.CompletionItemKind.Value; - case mItemKind.Enum: - return lsTypes.CompletionItemKind.Enum; - case mItemKind.Keyword: - return lsTypes.CompletionItemKind.Keyword; - case mItemKind.Snippet: - return lsTypes.CompletionItemKind.Snippet; - case mItemKind.Color: - return lsTypes.CompletionItemKind.Color; - case mItemKind.File: - return lsTypes.CompletionItemKind.File; - case mItemKind.Reference: - return lsTypes.CompletionItemKind.Reference; - } - return lsTypes.CompletionItemKind.Property; -} - -function toTextEdit(textEdit: lsTypes.TextEdit): languages.TextEdit; -function toTextEdit(textEdit: undefined): undefined; -function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined; -function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined { - if (!textEdit) { - return void 0; - } - return { - range: toRange(textEdit.range), - text: textEdit.newText - }; -} - -function toCommand(c: lsTypes.Command | undefined): languages.Command | undefined { - return c && c.command === 'editor.action.triggerSuggest' - ? { id: c.command, title: c.title, arguments: c.arguments } - : undefined; -} - -export class CompletionAdapter implements languages.CompletionItemProvider { - constructor(private _worker: WorkerAccessor) {} - - public get triggerCharacters(): string[] { - return ['.', ':', '<', '"', '=', '/']; - } - - provideCompletionItems( - model: editor.IReadOnlyModel, - position: Position, - context: languages.CompletionContext, - token: CancellationToken - ): Promise { - const resource = model.uri; - - return this._worker(resource) - .then((worker) => { - return worker.doComplete(resource.toString(), fromPosition(position)); - }) - .then((info) => { - if (!info) { - return; - } - const wordInfo = model.getWordUntilPosition(position); - const wordRange = new Range( - position.lineNumber, - wordInfo.startColumn, - position.lineNumber, - wordInfo.endColumn - ); - - const items: languages.CompletionItem[] = info.items.map((entry) => { - const item: languages.CompletionItem = { - label: entry.label, - insertText: entry.insertText || entry.label, - sortText: entry.sortText, - filterText: entry.filterText, - documentation: entry.documentation, - detail: entry.detail, - command: toCommand(entry.command), - range: wordRange, - kind: toCompletionItemKind(entry.kind) - }; - if (entry.textEdit) { - if (isInsertReplaceEdit(entry.textEdit)) { - item.range = { - insert: toRange(entry.textEdit.insert), - replace: toRange(entry.textEdit.replace) - }; - } else { - item.range = toRange(entry.textEdit.range); - } - item.insertText = entry.textEdit.newText; - } - if (entry.additionalTextEdits) { - item.additionalTextEdits = - entry.additionalTextEdits.map(toTextEdit); - } - if (entry.insertTextFormat === lsTypes.InsertTextFormat.Snippet) { - item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; - } - return item; - }); - - return { - isIncomplete: info.isIncomplete, - suggestions: items - }; - }); +export class HTMLCompletionAdapter extends CompletionAdapter { + constructor(worker: WorkerAccessor) { + super(worker, ['.', ':', '<', '"', '=', '/']); } } diff --git a/src/json/jsonMode.ts b/src/json/jsonMode.ts index 43b73d73..767bb550 100644 --- a/src/json/jsonMode.ts +++ b/src/json/jsonMode.ts @@ -46,7 +46,7 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable { providers.push( languages.registerCompletionItemProvider( languageId, - new languageFeatures.CompletionAdapter(worker) + new languageFeatures.JSONCompletionAdapter(worker) ) ); } diff --git a/src/json/languageFeatures.ts b/src/json/languageFeatures.ts index a633b554..1670135b 100644 --- a/src/json/languageFeatures.ts +++ b/src/json/languageFeatures.ts @@ -12,18 +12,22 @@ import { IMarkdownString, Uri, Position, - IRange, Range, CancellationToken } from '../fillers/monaco-editor-core'; -import { DiagnosticsAdapter } from '../common/lspLanguageFeatures'; +import { + DiagnosticsAdapter, + fromPosition, + toRange, + toTextEdit, + fromRange, + CompletionAdapter +} from '../common/lspLanguageFeatures'; export interface WorkerAccessor { (...more: Uri[]): Promise; } -// --- diagnostics --- --- - export class JSONDiagnosticsAdapter extends DiagnosticsAdapter { constructor(languageId: string, worker: WorkerAccessor, defaults: LanguageServiceDefaults) { super(languageId, worker, defaults.onDidChange); @@ -47,233 +51,9 @@ export class JSONDiagnosticsAdapter extends DiagnosticsAdapter { } } -// --- completion ------ - -function fromPosition(position: Position): lsTypes.Position; -function fromPosition(position: undefined): undefined; -function fromPosition(position: Position | undefined): lsTypes.Position | undefined; -function fromPosition(position: Position | undefined): lsTypes.Position | undefined { - if (!position) { - return void 0; - } - return { character: position.column - 1, line: position.lineNumber - 1 }; -} - -function fromRange(range: IRange): lsTypes.Range; -function fromRange(range: undefined): undefined; -function fromRange(range: IRange | undefined): lsTypes.Range | undefined; -function fromRange(range: IRange | undefined): lsTypes.Range | undefined { - if (!range) { - return void 0; - } - return { - start: { - line: range.startLineNumber - 1, - character: range.startColumn - 1 - }, - end: { line: range.endLineNumber - 1, character: range.endColumn - 1 } - }; -} -function toRange(range: lsTypes.Range): Range; -function toRange(range: undefined): undefined; -function toRange(range: lsTypes.Range | undefined): Range | undefined; -function toRange(range: lsTypes.Range | undefined): Range | undefined { - if (!range) { - return void 0; - } - return new Range( - range.start.line + 1, - range.start.character + 1, - range.end.line + 1, - range.end.character + 1 - ); -} - -function isInsertReplaceEdit( - edit: lsTypes.TextEdit | lsTypes.InsertReplaceEdit -): edit is lsTypes.InsertReplaceEdit { - return ( - typeof (edit).insert !== 'undefined' && - typeof (edit).replace !== 'undefined' - ); -} - -function toCompletionItemKind(kind: number | undefined): languages.CompletionItemKind { - const mItemKind = languages.CompletionItemKind; - - switch (kind) { - case lsTypes.CompletionItemKind.Text: - return mItemKind.Text; - case lsTypes.CompletionItemKind.Method: - return mItemKind.Method; - case lsTypes.CompletionItemKind.Function: - return mItemKind.Function; - case lsTypes.CompletionItemKind.Constructor: - return mItemKind.Constructor; - case lsTypes.CompletionItemKind.Field: - return mItemKind.Field; - case lsTypes.CompletionItemKind.Variable: - return mItemKind.Variable; - case lsTypes.CompletionItemKind.Class: - return mItemKind.Class; - case lsTypes.CompletionItemKind.Interface: - return mItemKind.Interface; - case lsTypes.CompletionItemKind.Module: - return mItemKind.Module; - case lsTypes.CompletionItemKind.Property: - return mItemKind.Property; - case lsTypes.CompletionItemKind.Unit: - return mItemKind.Unit; - case lsTypes.CompletionItemKind.Value: - return mItemKind.Value; - case lsTypes.CompletionItemKind.Enum: - return mItemKind.Enum; - case lsTypes.CompletionItemKind.Keyword: - return mItemKind.Keyword; - case lsTypes.CompletionItemKind.Snippet: - return mItemKind.Snippet; - case lsTypes.CompletionItemKind.Color: - return mItemKind.Color; - case lsTypes.CompletionItemKind.File: - return mItemKind.File; - case lsTypes.CompletionItemKind.Reference: - return mItemKind.Reference; - } - return mItemKind.Property; -} - -function fromCompletionItemKind(kind: languages.CompletionItemKind): lsTypes.CompletionItemKind { - const mItemKind = languages.CompletionItemKind; - - switch (kind) { - case mItemKind.Text: - return lsTypes.CompletionItemKind.Text; - case mItemKind.Method: - return lsTypes.CompletionItemKind.Method; - case mItemKind.Function: - return lsTypes.CompletionItemKind.Function; - case mItemKind.Constructor: - return lsTypes.CompletionItemKind.Constructor; - case mItemKind.Field: - return lsTypes.CompletionItemKind.Field; - case mItemKind.Variable: - return lsTypes.CompletionItemKind.Variable; - case mItemKind.Class: - return lsTypes.CompletionItemKind.Class; - case mItemKind.Interface: - return lsTypes.CompletionItemKind.Interface; - case mItemKind.Module: - return lsTypes.CompletionItemKind.Module; - case mItemKind.Property: - return lsTypes.CompletionItemKind.Property; - case mItemKind.Unit: - return lsTypes.CompletionItemKind.Unit; - case mItemKind.Value: - return lsTypes.CompletionItemKind.Value; - case mItemKind.Enum: - return lsTypes.CompletionItemKind.Enum; - case mItemKind.Keyword: - return lsTypes.CompletionItemKind.Keyword; - case mItemKind.Snippet: - return lsTypes.CompletionItemKind.Snippet; - case mItemKind.Color: - return lsTypes.CompletionItemKind.Color; - case mItemKind.File: - return lsTypes.CompletionItemKind.File; - case mItemKind.Reference: - return lsTypes.CompletionItemKind.Reference; - } - return lsTypes.CompletionItemKind.Property; -} - -function toTextEdit(textEdit: lsTypes.TextEdit): languages.TextEdit; -function toTextEdit(textEdit: undefined): undefined; -function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined; -function toTextEdit(textEdit: lsTypes.TextEdit | undefined): languages.TextEdit | undefined { - if (!textEdit) { - return void 0; - } - return { - range: toRange(textEdit.range), - text: textEdit.newText - }; -} - -function toCommand(c: lsTypes.Command | undefined): languages.Command | undefined { - return c && c.command === 'editor.action.triggerSuggest' - ? { id: c.command, title: c.title, arguments: c.arguments } - : undefined; -} - -export class CompletionAdapter implements languages.CompletionItemProvider { - constructor(private _worker: WorkerAccessor) {} - - public get triggerCharacters(): string[] { - return [' ', ':', '"']; - } - - provideCompletionItems( - model: editor.IReadOnlyModel, - position: Position, - context: languages.CompletionContext, - token: CancellationToken - ): Promise { - const resource = model.uri; - - return this._worker(resource) - .then((worker) => { - return worker.doComplete(resource.toString(), fromPosition(position)); - }) - .then((info) => { - if (!info) { - return; - } - const wordInfo = model.getWordUntilPosition(position); - const wordRange = new Range( - position.lineNumber, - wordInfo.startColumn, - position.lineNumber, - wordInfo.endColumn - ); - - const items: languages.CompletionItem[] = info.items.map((entry) => { - const item: languages.CompletionItem = { - label: entry.label, - insertText: entry.insertText || entry.label, - sortText: entry.sortText, - filterText: entry.filterText, - documentation: entry.documentation, - detail: entry.detail, - command: toCommand(entry.command), - range: wordRange, - kind: toCompletionItemKind(entry.kind) - }; - if (entry.textEdit) { - if (isInsertReplaceEdit(entry.textEdit)) { - item.range = { - insert: toRange(entry.textEdit.insert), - replace: toRange(entry.textEdit.replace) - }; - } else { - item.range = toRange(entry.textEdit.range); - } - item.insertText = entry.textEdit.newText; - } - if (entry.additionalTextEdits) { - item.additionalTextEdits = - entry.additionalTextEdits.map(toTextEdit); - } - if (entry.insertTextFormat === lsTypes.InsertTextFormat.Snippet) { - item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet; - } - return item; - }); - - return { - isIncomplete: info.isIncomplete, - suggestions: items - }; - }); +export class JSONCompletionAdapter extends CompletionAdapter { + constructor(worker: WorkerAccessor) { + super(worker, [' ', ':', '"']); } }