@krassowski/jupyterlab-lsp
Version:
Language Server Protocol integration for JupyterLab
208 lines (179 loc) • 5.17 kB
text/typescript
import { CompletionHandler } from '@jupyterlab/completer';
import { LabIcon } from '@jupyterlab/ui-components';
import * as lsProtocol from 'vscode-languageserver-types';
import { until_ready } from '../../utils';
import { LSPConnector } from './completion_handler';
/**
* To be upstreamed
*/
export interface ICompletionsSource {
/**
* The name displayed in the GUI
*/
name: string;
/**
* The higher the number the higher the priority
*/
priority: number;
/**
* The icon to be displayed if no type icon is present
*/
fallbackIcon?: LabIcon;
}
/**
* To be upstreamed
*/
export interface IExtendedCompletionItem
extends CompletionHandler.ICompletionItem {
insertText: string;
sortText: string;
source?: ICompletionsSource;
}
export class LazyCompletionItem implements IExtendedCompletionItem {
private _detail: string | undefined;
private _documentation: string | undefined;
private _is_documentation_markdown: boolean;
private _requested_resolution: boolean;
private _resolved: boolean;
/**
* Self-reference to make sure that the instance for will remain accessible
* after any copy operation (whether via spread syntax or Object.assign)
* performed by the JupyterLab completer internals.
*/
public self: LazyCompletionItem;
public element: HTMLLIElement;
private _currentInsertText: string;
get isDocumentationMarkdown(): boolean {
return this._is_documentation_markdown;
}
/**
* User facing completion.
* If insertText is not set, this will be inserted.
*/
public label: string;
public source: ICompletionsSource;
constructor(
/**
* Type of this completion item.
*/
public type: string,
/**
* LabIcon object for icon to be rendered with completion type.
*/
public icon: LabIcon,
private match: lsProtocol.CompletionItem,
private connector: LSPConnector,
private uri: string
) {
this.label = match.label;
this._setDocumentation(match.documentation);
this._requested_resolution = false;
this._resolved = false;
this._detail = match.detail;
this.self = this;
}
private _setDocumentation(
documentation: string | lsProtocol.MarkupContent | undefined
) {
if (lsProtocol.MarkupContent.is(documentation)) {
this._documentation = documentation.value;
this._is_documentation_markdown = documentation.kind === 'markdown';
} else {
this._documentation = documentation;
this._is_documentation_markdown = false;
}
}
/**
* Completion to be inserted.
*/
get insertText(): string {
return this._currentInsertText || this.match.insertText || this.match.label;
}
set insertText(text: string) {
this._currentInsertText = text;
}
get sortText(): string {
return this.match.sortText || this.match.label;
}
get filterText(): string | undefined {
return this.match.filterText;
}
public supportsResolution() {
const connection = this.connector.get_connection(this.uri);
return connection != null && connection.isCompletionResolveProvider();
}
get detail(): string | undefined {
return this._detail;
}
public needsResolution(): boolean {
if (this.documentation) {
return false;
}
if (this._resolved) {
return false;
}
if (this._requested_resolution) {
return false;
}
return this.supportsResolution();
}
public isResolved() {
return this._resolved;
}
/**
* Resolve (fetch) details such as documentation.
*/
public resolve(): Promise<lsProtocol.CompletionItem> {
if (this._resolved) {
return Promise.resolve(this);
}
if (!this.supportsResolution()) {
return Promise.resolve(this);
}
if (this._requested_resolution) {
return until_ready(() => this._resolved, 100, 50).then(() => this);
}
const connection = this.connector.get_connection(this.uri)!;
this._requested_resolution = true;
return connection
.getCompletionResolve(this.match)
.then(resolvedCompletionItem => {
if (resolvedCompletionItem === null) {
return resolvedCompletionItem;
}
this._setDocumentation(resolvedCompletionItem?.documentation);
this._detail = resolvedCompletionItem?.detail;
// TODO: implement in pyls and enable with proper LSP communication
// this.label = resolvedCompletionItem.label;
this._resolved = true;
return this;
});
}
/**
* A human-readable string with additional information
* about this item, like type or symbol information.
*/
get documentation(): string | undefined {
if (!this.connector.should_show_documentation) {
return undefined;
}
if (this._documentation) {
return this._documentation;
}
return undefined;
}
/**
* Indicates if the item is deprecated.
*/
get deprecated(): boolean {
if (this.match.deprecated) {
return this.match.deprecated;
}
return (
this.match.tags != null &&
this.match.tags.some(
tag => tag == lsProtocol.CompletionItemTag.Deprecated
)
);
}
}