Skip to content

Commit b3ae26e

Browse files
committed
Pre-fetch completion items (above/below), show placeholder while waiing
1 parent 762c8e6 commit b3ae26e

File tree

3 files changed

+70
-10
lines changed

3 files changed

+70
-10
lines changed

packages/completion-theme/style/index.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@
2323
.jp-Completer-docpanel {
2424
overflow: auto;
2525
}
26+
27+
.lsp-completer-placeholder:after {
28+
content: 'Loading...';
29+
color: #7f7f7f;
30+
}

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

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import { ILSPCompletionThemeManager } from '@krassowski/completion-theme/lib/typ
2222
import { LSPCompletionRenderer } from './renderer';
2323
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
2424

25+
const DOC_PANEL_SELECTOR = '.jp-Completer-docpanel';
26+
const DOC_PANEL_PLACEHOLDER_CLASS = 'lsp-completer-placeholder';
27+
2528
export class CompletionCM extends CodeMirrorIntegration {
2629
private _completionCharacters: string[];
2730

@@ -112,11 +115,29 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
112115
renderer: LSPCompletionRenderer,
113116
item: LazyCompletionItem
114117
) {
118+
if (!item.supportsResolution()) {
119+
return;
120+
}
121+
115122
if (item.needsResolution()) {
123+
this.set_doc_panel_placeholder(true);
116124
item.fetchDocumentation();
117125
} else if (item.isResolved()) {
118126
this.refresh_doc_panel(item);
119127
}
128+
129+
// also fetch completion for the previous and the next item to prevent jitter
130+
const index = this.current_index;
131+
const items = this.current_items;
132+
133+
if (index - 1 >= 0) {
134+
const previous = items[index - 1] as LazyCompletionItem;
135+
previous?.self?.fetchDocumentation();
136+
}
137+
if (index + 1 < items.length) {
138+
const next = items[index + 1] as LazyCompletionItem;
139+
next?.self?.fetchDocumentation();
140+
}
120141
}
121142

122143
private swap_adapter(
@@ -197,27 +218,39 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
197218
this.current_completion_handler.connector = this.current_completion_connector;
198219
}
199220

200-
refresh_doc_panel(item: LazyCompletionItem) {
221+
private get current_items() {
201222
// TODO upstream: make completer public?
202223
let completer = this.current_completion_handler.completer;
203224

204225
// TODO upstream: allow to get completionItems() without markup
205226
// (note: not trivial as _markup() does filtering too)
206-
const items = completer.model.completionItems();
227+
return completer.model.completionItems();
228+
}
229+
230+
private get current_index() {
231+
let completer = this.current_completion_handler.completer;
207232

208233
// TODO upstream: add getActiveItem() to Completer
209234
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
210235
// @ts-ignore
211-
const index = completer._activeIndex;
212-
const active: CompletionHandler.ICompletionItem = items[index];
236+
return completer._activeIndex;
237+
}
238+
239+
refresh_doc_panel(item: LazyCompletionItem) {
240+
let completer = this.current_completion_handler.completer;
241+
242+
const active: CompletionHandler.ICompletionItem = this.current_items[
243+
this.current_index
244+
];
213245

214246
if (active.insertText != item.insertText) {
215247
return;
216248
}
217249

218-
if (item.documentation) {
219-
let docPanel = completer.node.querySelector('.jp-Completer-docpanel');
250+
const docPanel = completer.node.querySelector(DOC_PANEL_SELECTOR);
251+
docPanel.classList.remove(DOC_PANEL_PLACEHOLDER_CLASS);
220252

253+
if (item.documentation) {
221254
// remove all children
222255
docPanel.textContent = '';
223256
// TODO upstream: renderer should take care of the documentation rendering
@@ -227,6 +260,20 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
227260
docPanel.appendChild(node);
228261

229262
docPanel.setAttribute('style', '');
263+
} else {
264+
docPanel.setAttribute('style', 'none');
265+
}
266+
}
267+
268+
set_doc_panel_placeholder(enabled: boolean) {
269+
let completer = this.current_completion_handler.completer;
270+
const docPanel = completer.node.querySelector(DOC_PANEL_SELECTOR);
271+
if (enabled) {
272+
docPanel.setAttribute('style', '');
273+
docPanel.classList.add(DOC_PANEL_PLACEHOLDER_CLASS);
274+
} else {
275+
docPanel.setAttribute('style', 'none');
276+
docPanel.classList.remove(DOC_PANEL_PLACEHOLDER_CLASS);
230277
}
231278
}
232279

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ export class LazyCompletionItem implements CompletionHandler.ICompletionItem {
9292
return this.match.insertText || this.match.label;
9393
}
9494

95+
public supportsResolution() {
96+
const connection = this.connector.get_connection(this.uri);
97+
98+
return connection.isCompletionResolveProvider();
99+
}
100+
95101
public needsResolution(): boolean {
96102
if (this.documentation) {
97103
return false;
@@ -105,9 +111,7 @@ export class LazyCompletionItem implements CompletionHandler.ICompletionItem {
105111
return false;
106112
}
107113

108-
const connection = this.connector.get_connection(this.uri);
109-
110-
return connection.isCompletionResolveProvider();
114+
return this.supportsResolution();
111115
}
112116

113117
public isResolved() {
@@ -131,9 +135,13 @@ export class LazyCompletionItem implements CompletionHandler.ICompletionItem {
131135
}
132136
this._setDocumentation(resolvedCompletionItem.documentation);
133137
this._resolved = true;
138+
this.connector.lab_integration.set_doc_panel_placeholder(false);
134139
this.connector.lab_integration.refresh_doc_panel(this);
135140
})
136-
.catch(console.warn);
141+
.catch(e => {
142+
this.connector.lab_integration.set_doc_panel_placeholder(false);
143+
console.warn(e);
144+
});
137145
}
138146

139147
/**

0 commit comments

Comments
 (0)