UNPKG

@jupyter-lsp/jupyterlab-lsp

Version:

Language Server Protocol integration for JupyterLab

166 lines 7.14 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { Completer } from '@jupyterlab/completer'; 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.LABEL_CLASS = 'jp-Completer-match'; this.elementToItem = new WeakMap(); this.visibilityObserver = new IntersectionObserver(entries => { entries.forEach(entry => { if (!entry.isIntersecting) { return; } let li = entry.target; let item = this.elementToItem.get(li); item.resolve().catch(console.error); }); }, { threshold: 0.25 }); } getExtraInfo(item) { var _a, _b, _c, _d, _e; const labelExtra = this.options.settings.composite.labelExtra; const detail = 'detail' in item ? (_a = item === null || item === void 0 ? void 0 : item.detail) !== null && _a !== void 0 ? _a : '' : ''; switch (labelExtra) { case 'detail': return detail; case 'type': return (_c = (_b = item === null || item === void 0 ? void 0 : item.type) === null || _b === void 0 ? void 0 : _b.toLowerCase) === null || _c === void 0 ? void 0 : _c.call(_b); case 'source': return item === null || item === void 0 ? void 0 : item.source; case 'auto': return [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), item === null || item === void 0 ? void 0 : item.source].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; this._elideMark(item, li); } } createCompletionItemNode(item, orderedTypes) { const li = super.createCompletionItemNode(item, orderedTypes); // make sure that an instance reference, and not an object copy is being used; const lspItem = item.self; // only monitor nodes that have item.self as others are not our completion items if (lspItem) { lspItem.element = li; this.elementToItem.set(li, lspItem); this.visibilityObserver.observe(li); // TODO: build custom li from ground up this.updateExtraInfo(lspItem, li); this._elideMark(lspItem, li); } else { this.updateExtraInfo(item, li); this._elideMark(lspItem, li); } return li; } _elideMark(item, li) { if (!item || !item.type) { return; } const type = item.type.toLowerCase(); if (type !== 'file' && type !== 'path') { // do not elide for non-paths. return; } const labelElement = li.getElementsByClassName(this.LABEL_CLASS)[0]; const originalHTMLLabel = labelElement.childNodes; let hasMark = false; for (const node of originalHTMLLabel) { if (node instanceof HTMLElement) { const element = node; const text = element.textContent; if (element.tagName === 'MARK' && text && text.length > 3) { const elidableElement = document.createElement('bdo'); elidableElement.setAttribute('dir', 'ltr'); elidableElement.textContent = text; element.title = text; element.replaceChildren(elidableElement); element.classList.add('lsp-elide'); hasMark = true; } } } if (hasMark) { const wrapper = document.createElement('div'); wrapper.className = 'lsp-elide-wrapper'; wrapper.replaceChildren(...labelElement.childNodes); labelElement.replaceChildren(wrapper); } } 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 if (item.source != 'LSP') { // fallback to default implementation for non-LSP completions return super.createDocumentationNode(item); } else { let node = document.createElement('pre'); if (item.documentation) { node.textContent = item.documentation; } return node; } } itemWidthHeuristic(item) { var _a, _b; let labelSize = item.label.replace(/<(\/)?mark>/g, '').length; const extra = this.getExtraInfo(item); const extraTextSize = (_a = extra === null || extra === void 0 ? void 0 : extra.length) !== null && _a !== void 0 ? _a : 0; const type = (_b = item.type) === null || _b === void 0 ? void 0 : _b.toLowerCase(); if (type === 'file' || type === 'path') { // account for elision const parts = item.label.split(/<\/mark>/g); const lastPart = parts[parts.length - 1]; const proposedElipsed = lastPart.length + 3; if (proposedElipsed < labelSize) { labelSize = proposedElipsed; } } if (this.options.settings.composite.layout === 'side-by-side') { // in 'side-by-side' take the sum return labelSize + extraTextSize; } // 'detail-below' mode take whichever is longer return Math.max(labelSize, extraTextSize); } } //# sourceMappingURL=renderer.js.map