|
@@ -29,12 +29,12 @@ export interface ILanguageWorkerWithDiagnostics {
|
|
|
}
|
|
|
|
|
|
export class DiagnosticsAdapter<T extends ILanguageWorkerWithDiagnostics> {
|
|
|
- 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<T>,
|
|
|
+ private readonly _languageId: string,
|
|
|
+ protected readonly _worker: WorkerAccessor<T>,
|
|
|
configChangeEvent: IEvent<any>
|
|
|
) {
|
|
|
const onModelAdd = (model: editor.IModel): void => {
|
|
@@ -97,7 +97,7 @@ export class DiagnosticsAdapter<T extends ILanguageWorkerWithDiagnostics> {
|
|
|
|
|
|
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<lsTypes.CompletionList>;
|
|
|
+}
|
|
|
+
|
|
|
+export class CompletionAdapter<T extends ILanguageWorkerWithCompletions>
|
|
|
+ implements languages.CompletionItemProvider
|
|
|
+{
|
|
|
+ constructor(
|
|
|
+ private readonly _worker: WorkerAccessor<T>,
|
|
|
+ private readonly _triggerCharacters: string[]
|
|
|
+ ) {}
|
|
|
+
|
|
|
+ public get triggerCharacters(): string[] {
|
|
|
+ return this._triggerCharacters;
|
|
|
+ }
|
|
|
+
|
|
|
+ provideCompletionItems(
|
|
|
+ model: editor.IReadOnlyModel,
|
|
|
+ position: Position,
|
|
|
+ context: languages.CompletionContext,
|
|
|
+ token: CancellationToken
|
|
|
+ ): Promise<languages.CompletionList | undefined> {
|
|
|
+ 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<languages.TextEdit>(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 (<lsTypes.InsertReplaceEdit>edit).insert !== 'undefined' &&
|
|
|
+ typeof (<lsTypes.InsertReplaceEdit>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
|