Skip to content

Commit 038ed74

Browse files
committed
WIP: enable sorting, improve marking and filtering and doc panel
1 parent f080ad0 commit 038ed74

File tree

8 files changed

+476
-150
lines changed

8 files changed

+476
-150
lines changed

atest/05_Features/Completion.robot

Lines changed: 25 additions & 2 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
@@ -297,6 +315,11 @@ Completer Should Suggest
297315
Wait Until Page Contains Element ${COMPLETER_BOX} .jp-Completer-item[data-value="${text}"] timeout=${timeout}
298316
Capture Page Screenshot ${text.replace(' ', '_')}.png
299317

318+
Get Completion Item Vertical Position
319+
[Arguments] ${text}
320+
${position} = Get Vertical Position ${COMPLETER_BOX} .jp-Completer-item[data-value="${text}"]
321+
[Return] ${position}
322+
300323
Completer Should Include Icon
301324
[Arguments] ${icon}
302325
Wait Until Page Contains Element ${COMPLETER_BOX} svg[data-icon="${icon}"] timeout=10s

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: 10 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,11 @@ export class LSPConnector
19557
lab_integration: CompletionLabIntegration;
19658
items: CompletionHandler.ICompletionItems;
19759

60+
get kernel_completions_first(): boolean {
61+
// TODO: test this in acceptance tests!
62+
return this.options.settings.composite.kernelCompletionsFirst;
63+
}
64+
19865
protected get suppress_auto_invoke_in(): string[] {
19966
return this.options.settings.composite.suppressInvokeIn;
20067
}
@@ -449,7 +316,6 @@ export class LSPConnector
449316
lspCompletionItems.forEach(match => {
450317
let kind = match.kind ? CompletionItemKind[match.kind] : '';
451318
let completionItem = new LazyCompletionItem(
452-
match.label,
453319
kind,
454320
this.icon_for(kind),
455321
match,
@@ -520,15 +386,17 @@ export class LSPConnector
520386
label: item.text as string,
521387
insertText: item.text as string,
522388
type: item.type as string,
523-
icon: this.icon_for(item.type as string)
389+
icon: this.icon_for(item.type as string),
390+
sortText: this.kernel_completions_first ? 'a' : 'z'
524391
};
525392
});
526393
} else {
527394
items = reply.matches.map(match => {
528395
return {
529396
label: match,
530397
insertText: match,
531-
icon: this.icon_for('Kernel')
398+
icon: this.icon_for('Kernel'),
399+
sortText: this.kernel_completions_first ? 'a' : 'z'
532400
};
533401
});
534402
}

0 commit comments

Comments
 (0)