@krassowski/jupyterlab-lsp
Version:
Language Server Protocol integration for JupyterLab
140 lines • 5.9 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { Completer } from '@jupyterlab/completer';
import { Signal } from '@lumino/signaling';
export class LSPCompletionRenderer extends Completer.Renderer {
constructor(options) {
super();
this.options = options;
this.ITEM_PLACEHOLDER_CLASS = 'lsp-detail-placeholder';
this.EXTRA_INFO_CLASS = 'jp-Completer-typeExtended';
this.activeChanged = new Signal(this);
this.itemShown = new Signal(this);
this.elementToItem = new WeakMap();
this.wasActivated = new WeakMap();
this.visibilityObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (!entry.isIntersecting) {
return;
}
let li = entry.target;
let item = this.elementToItem.get(li);
this.itemShown.emit({
item: item,
element: li
});
});
}, {
threshold: 0.25
});
// note: there should be no need to unobserve deleted elements as per:
// https://stackoverflow.com/a/51106262/6646912
this.activityObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
let li = mutation.target;
if (!(li instanceof HTMLLIElement)) {
return;
}
let inactive = !this.wasActivated.get(li);
if (li.classList.contains('jp-mod-active')) {
if (inactive) {
this.wasActivated.set(li, true);
let item = this.elementToItem.get(li);
this.activeChanged.emit({
item: item,
element: li
});
}
}
else {
this.wasActivated.set(li, false);
}
});
});
}
getExtraInfo(item) {
var _a, _b, _c, _d, _e, _f;
const labelExtra = this.options.integrator.settings.composite.labelExtra;
switch (labelExtra) {
case 'detail':
return (item === null || item === void 0 ? void 0 : item.detail) || '';
case 'type':
return (_b = (_a = item === null || item === void 0 ? void 0 : item.type) === null || _a === void 0 ? void 0 : _a.toLowerCase) === null || _b === void 0 ? void 0 : _b.call(_a);
case 'source':
return (_c = item === null || item === void 0 ? void 0 : item.source) === null || _c === void 0 ? void 0 : _c.name;
case 'auto':
return [
(item === null || item === void 0 ? void 0 : item.detail) || '',
(_e = (_d = item === null || item === void 0 ? void 0 : item.type) === null || _d === void 0 ? void 0 : _d.toLowerCase) === null || _e === void 0 ? void 0 : _e.call(_d),
(_f = item === null || item === void 0 ? void 0 : item.source) === null || _f === void 0 ? void 0 : _f.name
].filter(x => !!x)[0];
default:
this.options.console.warn('labelExtra does not match any of the expected values', labelExtra);
return '';
}
}
updateExtraInfo(item, li) {
const extraText = this.getExtraInfo(item);
if (extraText) {
const extraElement = li.getElementsByClassName(this.EXTRA_INFO_CLASS)[0];
extraElement.textContent = extraText;
}
}
createCompletionItemNode(item, orderedTypes) {
const li = super.createCompletionItemNode(item, orderedTypes);
// make sure that an instance reference, and not an object copy is being used;
const lsp_item = item.self;
// only monitor nodes that have item.self as others are not our completion items
if (lsp_item) {
lsp_item.element = li;
this.elementToItem.set(li, lsp_item);
this.activityObserver.observe(li, {
attributes: true,
attributeFilter: ['class']
});
this.visibilityObserver.observe(li);
// TODO: build custom li from ground up
this.updateExtraInfo(lsp_item, li);
}
else {
this.updateExtraInfo(item, li);
}
return li;
}
createDocumentationNode(item) {
// note: not worth trying to `fetchDocumentation()` as this is not
// invoked if documentation is empty (as of jlab 3.2)
if (item.isDocumentationMarkdown && this.options.markdownRenderer) {
let documentation = item.documentation;
this.options.markdownRenderer
.renderModel({
data: {
'text/markdown': documentation
},
trusted: false,
metadata: {},
setData(options) {
// empty
}
})
.then(() => {
if (this.options.markdownRenderer &&
this.options.latexTypesetter &&
documentation &&
documentation.includes('$')) {
this.options.latexTypesetter.typeset(this.options.markdownRenderer.node);
}
})
.catch(this.options.console.warn);
return this.options.markdownRenderer.node;
}
else {
let node = document.createElement('pre');
if (item.documentation) {
node.textContent = item.documentation;
}
return node;
}
}
}
//# sourceMappingURL=renderer.js.map