From 93d95090d1e81bc121626acfd66366ab42f42262 Mon Sep 17 00:00:00 2001 From: krassowski Date: Thu, 3 Sep 2020 21:45:45 +0100 Subject: [PATCH] Implement a workaround for #334 --- .../src/features/completion/completion.ts | 13 ++- .../features/completion/completion_handler.ts | 91 ++++++++++++------- packages/jupyterlab-lsp/src/lsp.ts | 8 ++ 3 files changed, 75 insertions(+), 37 deletions(-) diff --git a/packages/jupyterlab-lsp/src/features/completion/completion.ts b/packages/jupyterlab-lsp/src/features/completion/completion.ts index fd08db282..71c9916ac 100644 --- a/packages/jupyterlab-lsp/src/features/completion/completion.ts +++ b/packages/jupyterlab-lsp/src/features/completion/completion.ts @@ -1,4 +1,8 @@ -import { CompletionTriggerKind } from '../../lsp'; +import { + AdditionalCompletionTriggerKinds, + CompletionTriggerKind, + ExtendedCompletionTriggerKind +} from '../../lsp'; import * as CodeMirror from 'codemirror'; import { CodeMirrorIntegration } from '../../editor_integration/codemirror'; import { JupyterFrontEnd } from '@jupyterlab/application'; @@ -18,7 +22,6 @@ import { ILSPCompletionThemeManager } from '@krassowski/completion-theme/lib/typ export class CompletionCM extends CodeMirrorIntegration { private _completionCharacters: string[]; - // TODO chek if this works if yest then remove settings from options settings: FeatureSettings; get completionCharacters() { @@ -44,7 +47,7 @@ export class CompletionCM extends CodeMirrorIntegration { this.settings.composite.continuousHinting ) { (this.feature.labIntegration as CompletionLabIntegration) - .invoke_completer(CompletionTriggerKind.Invoked) + .invoke_completer(AdditionalCompletionTriggerKinds.AutoInvoked) .catch(console.warn); return; } @@ -126,7 +129,7 @@ export class CompletionLabIntegration implements IFeatureLabIntegration { }); } - invoke_completer(kind: CompletionTriggerKind) { + invoke_completer(kind: ExtendedCompletionTriggerKind) { let command: string; this.current_completion_connector.trigger_kind = kind; @@ -135,7 +138,7 @@ export class CompletionLabIntegration implements IFeatureLabIntegration { } else { command = 'completer:invoke-file'; } - return this.app.commands.execute(command).then(() => { + return this.app.commands.execute(command).catch(() => { this.current_completion_connector.trigger_kind = CompletionTriggerKind.Invoked; }); diff --git a/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts b/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts index b13785fc5..3ddf2e50c 100644 --- a/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts +++ b/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts @@ -1,12 +1,17 @@ import { + CompletionConnector, CompletionHandler, ContextConnector, - KernelConnector, - CompletionConnector + KernelConnector } from '@jupyterlab/completer'; import { CodeEditor } from '@jupyterlab/codeeditor'; import { JSONArray, JSONObject } from '@lumino/coreutils'; -import { CompletionItemKind, CompletionTriggerKind } from '../../lsp'; +import { + AdditionalCompletionTriggerKinds, + CompletionItemKind, + CompletionTriggerKind, + ExtendedCompletionTriggerKind +} from '../../lsp'; import * as lsProtocol from 'vscode-languageserver-types'; import { VirtualDocument } from '../../virtual/document'; import { IVirtualEditor } from '../../virtual/editor'; @@ -17,7 +22,6 @@ import { } from '../../positioning'; import { LSPConnection } from '../../connection'; import { Session } from '@jupyterlab/services'; -import ICompletionItemsResponseType = CompletionHandler.ICompletionItemsResponseType; import { CodeCompletion as LSPCompletionSettings } from '../../_completion'; import { FeatureSettings } from '../../feature'; @@ -27,6 +31,7 @@ import { KernelKind } from '@krassowski/completion-theme/lib/types'; import { LabIcon } from '@jupyterlab/ui-components'; +import ICompletionItemsResponseType = CompletionHandler.ICompletionItemsResponseType; /** * A LSP connector for completion handlers. @@ -44,7 +49,7 @@ export class LSPConnector responseType = ICompletionItemsResponseType; virtual_editor: IVirtualEditor; - trigger_kind: CompletionTriggerKind; + trigger_kind: ExtendedCompletionTriggerKind; protected get suppress_auto_invoke_in(): string[] { return this.options.settings.composite.suppressInvokeIn; @@ -156,6 +161,17 @@ export class LSPConnector cursor_in_root ); + const lsp_promise = this.fetch_lsp( + token, + typed_character, + virtual_start, + virtual_end, + virtual_cursor, + document, + position_in_token + ); + let promise: Promise = null; + try { if (this._kernel_connector && this._has_kernel) { // TODO: this would be awesome if we could connect to rpy2 for R suggestions in Python, @@ -165,41 +181,34 @@ export class LSPConnector const kernelLanguage = await this._kernel_language(); if (document.language === kernelLanguage) { - return Promise.all([ + promise = Promise.all([ this._kernel_connector.fetch(request), - this.fetch_lsp( - token, - typed_character, - virtual_start, - virtual_end, - virtual_cursor, - document, - position_in_token - ) + lsp_promise ]).then(([kernel, lsp]) => this.merge_replies(this.transform_reply(kernel), lsp, this._editor) ); } } - - return this.fetch_lsp( - token, - typed_character, - virtual_start, - virtual_end, - virtual_cursor, - document, - position_in_token - ).catch(e => { - console.warn('LSP: hint failed', e); - return this.fallback_connector - .fetch(request) - .then(this.transform_reply); - }); + if (!promise) { + promise = lsp_promise.catch(e => { + console.warn('LSP: hint failed', e); + return this.fallback_connector + .fetch(request) + .then(this.transform_reply); + }); + } } catch (e) { console.warn('LSP: kernel completions failed', e); - return this.fallback_connector.fetch(request).then(this.transform_reply); + promise = this.fallback_connector + .fetch(request) + .then(this.transform_reply); } + + return promise.then(reply => { + reply = this.suppress_if_needed(reply); + this.trigger_kind = CompletionTriggerKind.Invoked; + return reply; + }); } async fetch_lsp( @@ -216,6 +225,11 @@ export class LSPConnector console.log('[LSP][Completer] Fetching and Transforming'); console.log('[LSP][Completer] Token:', token, start, end); + const trigger_kind = + this.trigger_kind == AdditionalCompletionTriggerKinds.AutoInvoked + ? CompletionTriggerKind.Invoked + : this.trigger_kind; + let lspCompletionItems = ((await connection.getCompletion( cursor, { @@ -226,7 +240,7 @@ export class LSPConnector document.document_info, false, typed_character, - this.trigger_kind + trigger_kind )) || []) as lsProtocol.CompletionItem[]; let prefix = token.value.slice(0, position_in_token + 1); @@ -398,6 +412,19 @@ export class LSPConnector save(id: CompletionHandler.IRequest, value: void): Promise { return Promise.resolve(undefined); } + + private suppress_if_needed(reply: CompletionHandler.ICompletionItemsReply) { + if (this.trigger_kind == AdditionalCompletionTriggerKinds.AutoInvoked) { + if (reply.start == reply.end) { + return { + start: reply.start, + end: reply.end, + items: [] + }; + } + } + return reply; + } } /** diff --git a/packages/jupyterlab-lsp/src/lsp.ts b/packages/jupyterlab-lsp/src/lsp.ts index 7a5b3f403..a187c3939 100644 --- a/packages/jupyterlab-lsp/src/lsp.ts +++ b/packages/jupyterlab-lsp/src/lsp.ts @@ -45,6 +45,14 @@ export enum CompletionTriggerKind { TriggerForIncompleteCompletions = 3 } +export enum AdditionalCompletionTriggerKinds { + AutoInvoked = 9999 +} + +export type ExtendedCompletionTriggerKind = + | CompletionTriggerKind + | AdditionalCompletionTriggerKinds; + export type CompletionItemKindStrings = keyof typeof CompletionItemKind; /**