Skip to content

Commit 5bf9427

Browse files
authored
Merge pull request #520 from krassowski/completion/model-and-sorting
Sorted completions, improve completions marking and filtering and doc panel
2 parents f080ad0 + 9dc9f81 commit 5bf9427

File tree

11 files changed

+503
-159
lines changed

11 files changed

+503
-159
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
## CHANGELOG
22

3+
### `@krassowski/jupyterlab-lsp 3.4.0` (unreleased)
4+
5+
- features:
6+
7+
- the priority of the completions from kernel can now be changed by switching new `kernelCompletionsFirst` setting ([#520])
8+
- completer panel will now always render markdown documentation if available ([#520])
9+
- 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
10+
- the completer now uses `filterText` and `sortText` if available to better filter and sort completions ([#520])
11+
12+
- bug fixes:
13+
- completer documentation will now consistently show up after filtering the completion items ([#520])
14+
- completions containing HTML-like syntax will be displayed properly (an upstream issue) ([#520])
15+
16+
[#520]: https://github.com/krassowski/jupyterlab-lsp/pull/520
17+
318
### `@krassowski/jupyterlab-lsp 3.3.1` (2020-02-07)
419

520
- bug fixes:

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ python scripts/atest.py --suite "05_Features.Completion"
227227
##### Run a single test
228228

229229
```bash
230-
python scripts/atest.py --test "Works With Kernel Running"
230+
python scripts/atest.py --test "Works When Kernel Is Idle"
231231
```
232232

233233
##### Run test with a tag

atest/05_Features/Completion.robot

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,31 @@ Works When Kernel Is Idle
2222
Completer Should Suggest TabError
2323
# this comes from LSP:
2424
Completer Should Suggest test
25-
# this comes from kernel; sometimes the kernel response may come a bit later
26-
Wait Until Keyword Succeeds 20x 0.5s Completer Should Suggest %%timeit
25+
# this comes from kernel
26+
Completer Should Suggest %%timeit
2727
Press Keys None ENTER
2828
Capture Page Screenshot 03-completion-confirmed.png
2929
${content} = Get Cell Editor Content 1
3030
Should Contain ${content} TabError
3131

32+
Can Prioritize Kernel Completions
33+
Configure JupyterLab Plugin {"kernelCompletionsFirst": true, "kernelResponseTimeout": -1} plugin id=${COMPLETION PLUGIN ID}
34+
Enter Cell Editor 1 line=2
35+
Trigger Completer
36+
Completer Should Suggest %%timeit
37+
${lsp_position} = Get Completion Item Vertical Position test
38+
${kernel_position} = Get Completion Item Vertical Position %%timeit
39+
Should Be True ${kernel_position} < ${lsp_position}
40+
41+
Can Prioritize LSP Completions
42+
Configure JupyterLab Plugin {"kernelCompletionsFirst": false, "kernelResponseTimeout": -1} plugin id=${COMPLETION PLUGIN ID}
43+
Enter Cell Editor 1 line=2
44+
Trigger Completer
45+
Completer Should Suggest %%timeit
46+
${lsp_position} = Get Completion Item Vertical Position test
47+
${kernel_position} = Get Completion Item Vertical Position %%timeit
48+
Should Be True ${kernel_position} > ${lsp_position}
49+
3250
Invalidates On Cell Change
3351
Enter Cell Editor 1 line=2
3452
Press Keys None TAB
@@ -233,10 +251,10 @@ Completes Correctly With R Double And Triple Colon
233251
Place Cursor In File Editor At 2 7
234252
Wait Until Fully Initialized
235253
Trigger Completer
236-
Completer Should Suggest assertCondition
237-
Select Completer Suggestion assertCondition
238-
Wait Until Keyword Succeeds 40x 0.5s File Editor Line Should Equal 1 tools::assertCondition
239-
# tripple colont
254+
Completer Should Suggest .print.via.format
255+
Select Completer Suggestion .print.via.format
256+
Wait Until Keyword Succeeds 40x 0.5s File Editor Line Should Equal 1 tools::.print.via.format
257+
# tripple colon
240258
Place Cursor In File Editor At 4 11
241259
Trigger Completer
242260
Completer Should Suggest .packageName
@@ -254,9 +272,13 @@ Completes Large Namespaces
254272

255273
Shows Documentation With CompletionItem Resolve
256274
[Setup] Prepare File for Editing R completion completion.R
257-
Place Cursor In File Editor At 8 12
275+
Place Cursor In File Editor At 8 7
258276
Wait Until Fully Initialized
259277
Trigger Completer
278+
Completer Should Suggest print.data.frame
279+
Completer Should Include Documentation Print a data frame.
280+
# should remain visible after typing:
281+
Press Keys None efa
260282
Completer Should Suggest print.default
261283
Completer Should Include Documentation the default method of the
262284
[Teardown] Clean Up After Working With File completion.R
@@ -297,6 +319,11 @@ Completer Should Suggest
297319
Wait Until Page Contains Element ${COMPLETER_BOX} .jp-Completer-item[data-value="${text}"] timeout=${timeout}
298320
Capture Page Screenshot ${text.replace(' ', '_')}.png
299321

322+
Get Completion Item Vertical Position
323+
[Arguments] ${text}
324+
${position} = Get Vertical Position ${COMPLETER_BOX} .jp-Completer-item[data-value="${text}"]
325+
[Return] ${position}
326+
300327
Completer Should Include Icon
301328
[Arguments] ${icon}
302329
Wait Until Page Contains Element ${COMPLETER_BOX} svg[data-icon="${icon}"] timeout=10s

atest/examples/completion.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# `tools::<tab>` → select `assertCondition` → `tools::assertCondition`
1+
# `tools::<tab>` → select `.print.via.format` → `tools::.print.via.format`
22
tools::
33
# `datasets:::<tab>` → select `.packageName` → `datasets:::.packageName`
44
datasets:::
55
# `base:::<tab>` → works
66
base:::
7-
# `print.defaul<tab>` → shows documentation for `print.default`
8-
print.defaul
7+
# `print.d<tab>` → shows documentation for `print.data.frame` → press `efa` → shows documentation for `print.default`
8+
print.d

packages/jupyterlab-lsp/schema/completion.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@
4242
"title": "Completer theme",
4343
"type": ["string", "null"],
4444
"default": "vscode",
45-
"description": "The identifier of a completer theme with icons which indicate the kind of completion. Set to null to disable icons."
45+
"description": "The identifier of a completer theme with icons which indicate the kind of completion. Set to null to disable icons. Search for 'completer themes' in the command palette for a command displaying available themes."
46+
},
47+
"kernelCompletionsFirst": {
48+
"title": "Prioritize completions from kernel",
49+
"default": false,
50+
"description": "In case of ties when sorting completions, should the kernel completions receive higher priority than the language server completions?"
4651
},
4752
"typesMap": {
4853
"title": "Mapping of custom kernel types to valid completion kind names",

packages/jupyterlab-lsp/src/features/completion/completion.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as CodeMirror from 'codemirror';
77
import { CodeMirrorIntegration } from '../../editor_integration/codemirror';
88
import { JupyterFrontEnd } from '@jupyterlab/application';
99
import { IEditorChangedData, WidgetAdapter } from '../../adapters/adapter';
10-
import { LazyCompletionItem, LSPConnector } from './completion_handler';
10+
import { LSPConnector } from './completion_handler';
1111
import { CompletionHandler, ICompletionManager } from '@jupyterlab/completer';
1212
import { CodeEditor } from '@jupyterlab/codeeditor';
1313
import { IDocumentWidget } from '@jupyterlab/docregistry';
@@ -21,6 +21,8 @@ import { NotebookAdapter } from '../../adapters/notebook/notebook';
2121
import { ILSPCompletionThemeManager } from '@krassowski/completion-theme/lib/types';
2222
import { LSPCompletionRenderer } from './renderer';
2323
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
24+
import { LSPCompleterModel } from './model';
25+
import { LazyCompletionItem } from './item';
2426

2527
const DOC_PANEL_SELECTOR = '.jp-Completer-docpanel';
2628
const DOC_PANEL_PLACEHOLDER_CLASS = 'lsp-completer-placeholder';
@@ -117,6 +119,10 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
117119
item: LazyCompletionItem
118120
) {
119121
if (!item.supportsResolution()) {
122+
if (item.isDocumentationMarkdown) {
123+
// TODO: remove once https://github.com/jupyterlab/jupyterlab/pull/9663 is merged and released
124+
this.refresh_doc_panel(item);
125+
}
120126
return;
121127
}
122128

@@ -187,10 +193,18 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
187193
},
188194
this.renderer
189195
) as CompletionHandler;
196+
let completer = this.completer;
197+
completer.addClass('lsp-completer');
198+
completer.model = new LSPCompleterModel();
199+
}
200+
201+
get completer() {
202+
return this.current_completion_handler.completer;
190203
}
191204

192205
invoke_completer(kind: ExtendedCompletionTriggerKind) {
193206
let command: string;
207+
194208
this.current_completion_connector.trigger_kind = kind;
195209

196210
if (this.adapterManager.currentAdapter instanceof NotebookAdapter) {

packages/jupyterlab-lsp/src/features/completion/completion_handler.ts

Lines changed: 9 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -33,147 +33,9 @@ import {
3333
import { LabIcon } from '@jupyterlab/ui-components';
3434
import { ILSPLogConsole } from '../../tokens';
3535
import { CompletionLabIntegration } from './completion';
36+
import { LazyCompletionItem } from './item';
3637
import ICompletionItemsResponseType = CompletionHandler.ICompletionItemsResponseType;
3738

38-
export class LazyCompletionItem implements CompletionHandler.ICompletionItem {
39-
private _documentation: string;
40-
private _is_documentation_markdown: boolean;
41-
private _requested_resolution: boolean;
42-
private _resolved: boolean;
43-
/**
44-
* Self-reference to make sure that the instance for will remain accessible
45-
* after any copy operation (whether via spread syntax or Object.assign)
46-
* performed by the JupyterLab completer internals.
47-
*/
48-
public self: LazyCompletionItem;
49-
50-
get isDocumentationMarkdown(): boolean {
51-
return this._is_documentation_markdown;
52-
}
53-
54-
constructor(
55-
/**
56-
* User facing completion.
57-
* If insertText is not set, this will be inserted.
58-
*/
59-
public label: string,
60-
/**
61-
* Type of this completion item.
62-
*/
63-
public type: string,
64-
/**
65-
* LabIcon object for icon to be rendered with completion type.
66-
*/
67-
public icon: LabIcon,
68-
private match: lsProtocol.CompletionItem,
69-
private connector: LSPConnector,
70-
private uri: string
71-
) {
72-
this._setDocumentation(match.documentation);
73-
this._requested_resolution = false;
74-
this._resolved = false;
75-
this.self = this;
76-
}
77-
78-
private _setDocumentation(documentation: string | lsProtocol.MarkupContent) {
79-
if (lsProtocol.MarkupContent.is(documentation)) {
80-
this._documentation = documentation.value;
81-
this._is_documentation_markdown = documentation.kind === 'markdown';
82-
} else {
83-
this._documentation = documentation;
84-
this._is_documentation_markdown = false;
85-
}
86-
}
87-
88-
/**
89-
* Completion to be inserted.
90-
*/
91-
get insertText(): string {
92-
return this.match.insertText || this.match.label;
93-
}
94-
95-
public supportsResolution() {
96-
const connection = this.connector.get_connection(this.uri);
97-
98-
return connection.isCompletionResolveProvider();
99-
}
100-
101-
public needsResolution(): boolean {
102-
if (this.documentation) {
103-
return false;
104-
}
105-
106-
if (this._resolved) {
107-
return false;
108-
}
109-
110-
if (this._requested_resolution) {
111-
return false;
112-
}
113-
114-
return this.supportsResolution();
115-
}
116-
117-
public isResolved() {
118-
return this._resolved;
119-
}
120-
121-
public fetchDocumentation(): void {
122-
if (!this.needsResolution()) {
123-
return;
124-
}
125-
126-
const connection = this.connector.get_connection(this.uri);
127-
128-
this._requested_resolution = true;
129-
130-
connection
131-
.getCompletionResolve(this.match)
132-
.then(resolvedCompletionItem => {
133-
this.connector.lab_integration.set_doc_panel_placeholder(false);
134-
if (resolvedCompletionItem === null) {
135-
return;
136-
}
137-
this._setDocumentation(resolvedCompletionItem.documentation);
138-
this._resolved = true;
139-
this.connector.lab_integration.refresh_doc_panel(this);
140-
})
141-
.catch(e => {
142-
this.connector.lab_integration.set_doc_panel_placeholder(false);
143-
console.warn(e);
144-
});
145-
}
146-
147-
/**
148-
* A human-readable string with additional information
149-
* about this item, like type or symbol information.
150-
*/
151-
get documentation(): string {
152-
if (!this.connector.should_show_documentation) {
153-
return null;
154-
}
155-
if (this._documentation) {
156-
return this._documentation;
157-
}
158-
return null;
159-
}
160-
161-
/**
162-
* Indicates if the item is deprecated.
163-
*/
164-
get deprecated(): boolean {
165-
if (this.match.deprecated) {
166-
return this.match.deprecated;
167-
}
168-
return (
169-
this.match.tags &&
170-
this.match.tags.some(
171-
tag => tag == lsProtocol.CompletionItemTag.Deprecated
172-
)
173-
);
174-
}
175-
}
176-
17739
/**
17840
* A LSP connector for completion handlers.
17941
*/
@@ -195,6 +57,10 @@ export class LSPConnector
19557
lab_integration: CompletionLabIntegration;
19658
items: CompletionHandler.ICompletionItems;
19759

60+
get kernel_completions_first(): boolean {
61+
return this.options.settings.composite.kernelCompletionsFirst;
62+
}
63+
19864
protected get suppress_auto_invoke_in(): string[] {
19965
return this.options.settings.composite.suppressInvokeIn;
20066
}
@@ -449,7 +315,6 @@ export class LSPConnector
449315
lspCompletionItems.forEach(match => {
450316
let kind = match.kind ? CompletionItemKind[match.kind] : '';
451317
let completionItem = new LazyCompletionItem(
452-
match.label,
453318
kind,
454319
this.icon_for(kind),
455320
match,
@@ -520,15 +385,17 @@ export class LSPConnector
520385
label: item.text as string,
521386
insertText: item.text as string,
522387
type: item.type as string,
523-
icon: this.icon_for(item.type as string)
388+
icon: this.icon_for(item.type as string),
389+
sortText: this.kernel_completions_first ? 'a' : 'z'
524390
};
525391
});
526392
} else {
527393
items = reply.matches.map(match => {
528394
return {
529395
label: match,
530396
insertText: match,
531-
icon: this.icon_for('Kernel')
397+
icon: this.icon_for('Kernel'),
398+
sortText: this.kernel_completions_first ? 'a' : 'z'
532399
};
533400
});
534401
}

0 commit comments

Comments
 (0)