diff --git a/CHANGELOG.md b/CHANGELOG.md index c398c9480..6db6b2616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,16 @@ - completer panel will now always render markdown documentation if available ([#520]) - the implementation re-renders the panel as it is the best we can do until [jupyterlab#9663](https://github.com/jupyterlab/jupyterlab/pull/9663) is merged - the completer now uses `filterText` and `sortText` if available to better filter and sort completions ([#520]) + - completer `suppressInvokeIn` setting was removed; `suppressContinuousHintingIn` and `suppressTriggerCharacterIn` settings were added ([#521]) + - `suppressContinuousHintingIn` by default includes `def` to improve the experience when writing function names ([#521]) - bug fixes: + - user-invoked completion in strings works again ([#521]) - completer documentation will now consistently show up after filtering the completion items ([#520]) - completions containing HTML-like syntax will be displayed properly (an upstream issue) ([#520]) [#520]: https://github.com/krassowski/jupyterlab-lsp/pull/520 +[#521]: https://github.com/krassowski/jupyterlab-lsp/pull/521 ### `@krassowski/jupyterlab-lsp 3.3.1` (2020-02-07) diff --git a/atest/05_Features/Completion.robot b/atest/05_Features/Completion.robot index c6c6fcd87..23017ecf4 100644 --- a/atest/05_Features/Completion.robot +++ b/atest/05_Features/Completion.robot @@ -102,11 +102,24 @@ Works After Kernel Restart In New Cells Works In File Editor [Setup] Prepare File for Editing Python completion completion.py Place Cursor In File Editor At 9 2 - Capture Page Screenshot 01-editor-ready.png + Wait Until Fully Initialized Trigger Completer Completer Should Suggest add [Teardown] Clean Up After Working With File completion.py +Completes In Strings Or Python Dictionaries + [Setup] Prepare File for Editing Python completion completion.py + Place Cursor In File Editor At 16 0 + Wait Until Fully Initialized + Press Keys None test_dict[''] + Place Cursor In File Editor At 16 11 + Trigger Completer + # note: in jedi-language-server this would be key_a without ' + Completer Should Suggest 'key_a + Select Completer Suggestion 'key_a + Wait Until Keyword Succeeds 40x 0.5s File Editor Line Should Equal 15 test_dict['key_a'] + [Teardown] Clean Up After Working With File completion.py + Continious Hinting Works Configure JupyterLab Plugin {"continuousHinting": true} plugin id=${COMPLETION PLUGIN ID} Prepare File for Editing Python completion completion.py diff --git a/atest/examples/completion.py b/atest/examples/completion.py index fcdfcaf5f..6ca2b23eb 100644 --- a/atest/examples/completion.py +++ b/atest/examples/completion.py @@ -6,4 +6,10 @@ def add(a: int, b: int): return a, b -ad \ No newline at end of file +ad + + +test_dict = { + 'key_a': 1, + 'key_b': 2 +} diff --git a/packages/jupyterlab-lsp/schema/completion.json b/packages/jupyterlab-lsp/schema/completion.json index 1124b3fd5..45d765cb8 100644 --- a/packages/jupyterlab-lsp/schema/completion.json +++ b/packages/jupyterlab-lsp/schema/completion.json @@ -17,14 +17,23 @@ "default": false, "description": "Whether to enable continuous hinting (Hinterland mode)." }, - "suppressInvokeIn": { - "title": "Suppress invoke in specific code fragments", + "suppressContinuousHintingIn": { + "title": "Suppress invoke continuous hinting in specific code fragments", + "type": "array", + "items": { + "type": "string" + }, + "default": ["comment", "string", "def"], + "description": "An array of CodeMirror tokens for which the continuous hinting should be suppressed. The token names vary between languages (modes)." + }, + "suppressTriggerCharacterIn": { + "title": "Suppress invoke via trigger character in specific code fragments", "type": "array", "items": { "type": "string" }, "default": ["comment", "string"], - "description": "An array of CodeMirror tokens for which the auto-invoke should be suppressed. Adding 'def' will prevent continuous hinting when writing a function name in Python, Julia, JavaScript and other languages. The token names vary between languages (modes)." + "description": "An array of CodeMirror tokens for which the auto-invoke after entering a trigger (e.g. `.` in Python or `::` in R) character should be suppressed. The token names vary between languages (modes)." }, "kernelResponseTimeout": { "title": "Kernel completion response timeout", diff --git a/packages/jupyterlab-lsp/src/features/completion/completion.ts b/packages/jupyterlab-lsp/src/features/completion/completion.ts index e724470cf..2d4a284e7 100644 --- a/packages/jupyterlab-lsp/src/features/completion/completion.ts +++ b/packages/jupyterlab-lsp/src/features/completion/completion.ts @@ -63,7 +63,7 @@ export class CompletionCM extends CodeMirrorIntegration { ); (this.feature.labIntegration as CompletionLabIntegration) .invoke_completer(CompletionTriggerKind.TriggerCharacter) - .catch(console.warn); + .catch(this.console.warn); return; } @@ -74,7 +74,7 @@ export class CompletionCM extends CodeMirrorIntegration { ) { (this.feature.labIntegration as CompletionLabIntegration) .invoke_completer(AdditionalCompletionTriggerKinds.AutoInvoked) - .catch(console.warn); + .catch(this.console.warn); } } } @@ -198,13 +198,18 @@ export class CompletionLabIntegration implements IFeatureLabIntegration { completer.model = new LSPCompleterModel(); } - get completer() { + protected get completer() { + // TODO upstream: make completer public? return this.current_completion_handler.completer; } + protected get model(): LSPCompleterModel { + return this.completer.model as LSPCompleterModel; + } + invoke_completer(kind: ExtendedCompletionTriggerKind) { + // TODO: ideally this would not re-trigger if list of items not isIncomplete let command: string; - this.current_completion_connector.trigger_kind = kind; if (this.adapterManager.currentAdapter instanceof NotebookAdapter) { @@ -234,12 +239,9 @@ export class CompletionLabIntegration implements IFeatureLabIntegration { } private get current_items() { - // TODO upstream: make completer public? - let completer = this.current_completion_handler.completer; - // TODO upstream: allow to get completionItems() without markup // (note: not trivial as _markup() does filtering too) - return completer.model.completionItems(); + return this.model.completionItems(); } private get current_index() { diff --git a/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts b/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts index 651278aed..5d92bb718 100644 --- a/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts +++ b/packages/jupyterlab-lsp/src/features/completion/completion_handler.ts @@ -61,8 +61,12 @@ export class LSPConnector return this.options.settings.composite.kernelCompletionsFirst; } - protected get suppress_auto_invoke_in(): string[] { - return this.options.settings.composite.suppressInvokeIn; + protected get suppress_continuous_hinting_in(): string[] { + return this.options.settings.composite.suppressContinuousHintingIn; + } + + protected get suppress_trigger_character_in(): string[] { + return this.options.settings.composite.suppressTriggerCharacterIn; } get should_show_documentation(): boolean { @@ -155,9 +159,16 @@ export class LSPConnector const cursor = editor.getCursorPosition(); const token = editor.getTokenForPosition(cursor); - if (this.suppress_auto_invoke_in.indexOf(token.type) !== -1) { - this.console.log('Suppressing completer auto-invoke in', token.type); - return; + if (this.trigger_kind == AdditionalCompletionTriggerKinds.AutoInvoked) { + if (this.suppress_continuous_hinting_in.indexOf(token.type) !== -1) { + this.console.debug('Suppressing completer auto-invoke in', token.type); + return; + } + } else if (this.trigger_kind == CompletionTriggerKind.TriggerCharacter) { + if (this.suppress_trigger_character_in.indexOf(token.type) !== -1) { + this.console.debug('Suppressing completer auto-invoke in', token.type); + return; + } } const start = editor.getPositionAt(token.offset); @@ -343,9 +354,15 @@ export class LSPConnector this.console.debug('Transformed'); // required to make the repetitive trigger characters like :: or ::: work for R with R languageserver, // see https://github.com/krassowski/jupyterlab-lsp/issues/436 - const prefix_offset = token.value.length; + let prefix_offset = token.value.length; + // completion of dictionaries for Python with jedi-language-server was + // causing an issue for dic[''] case; to avoid this let's make + // sure that prefix.length >= prefix.offset + if (all_non_prefixed && prefix_offset > prefix.length) { + prefix_offset = prefix.length; + } - return { + let response = { // note in the ContextCompleter it was: // start: token.offset, // end: token.offset + token.value.length, @@ -359,6 +376,14 @@ export class LSPConnector end: token.offset + prefix.length, items: items }; + if (response.start > response.end) { + console.warn( + 'Response contains start beyond end; this should not happen!', + response + ); + } + + return response; } protected icon_for(type: string): LabIcon { diff --git a/packages/jupyterlab-lsp/src/features/completion/model.ts b/packages/jupyterlab-lsp/src/features/completion/model.ts index 9f3c1fb97..c10a0b6cf 100644 --- a/packages/jupyterlab-lsp/src/features/completion/model.ts +++ b/packages/jupyterlab-lsp/src/features/completion/model.ts @@ -35,12 +35,9 @@ export class GenericCompleterModel< let unfilteredItems = super.completionItems() as T[]; this.query = query; - //if (query) { // always want to sort // TODO does this behave strangely with %% if always sorting? return this._sortAndFilter(query, unfilteredItems); - //} - //return unfilteredItems; } setCompletionItems(newValue: T[]) {