@jupyter-lsp/jupyterlab-lsp
Version:
Language Server Protocol integration for JupyterLab
147 lines • 6.28 kB
JavaScript
import { EditorView } from '@codemirror/view';
import { IEditorServices } from '@jupyterlab/codeeditor';
import { IEditorLanguageRegistry, EditorExtensionRegistry } from '@jupyterlab/codemirror';
import { ILSPFeatureManager, ILSPDocumentConnectionManager } from '@jupyterlab/lsp';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { LabIcon } from '@jupyterlab/ui-components';
import syntaxSvg from '../../style/icons/syntax-highlight.svg';
import { FeatureSettings, Feature } from '../feature';
import { PLUGIN_ID } from '../tokens';
export const syntaxHighlightingIcon = new LabIcon({
name: 'lsp:syntax-highlighting',
svgstr: syntaxSvg
});
export class SyntaxHighlightingFeature extends Feature {
constructor(options) {
super(options);
this.options = options;
this.id = SyntaxHighlightingFeature.id;
// note: semantic highlighting could be implemented here
this.capabilities = {};
this.originalModes = new Map();
this.extensionFactory = {
name: 'lsp:syntaxHighlighting',
factory: factoryOptions => {
const { editor: editorAccessor, widgetAdapter: adapter } = factoryOptions;
const allReady = Promise.all([editorAccessor.ready, adapter.ready]);
const updateHandler = async (awaitUpdate = true) => {
await allReady;
await this.updateMode(adapter, editorAccessor, awaitUpdate);
};
const updateListener = EditorView.updateListener.of(async () => {
await updateHandler();
});
allReady.then(() => updateHandler(false)).catch(console.warn);
// update the mode at first update even if no changes to ensure the
// correct mode gets applied on load.
return EditorExtensionRegistry.createImmutableExtension([
updateListener
]);
}
};
}
getMode(language) {
const mimetype = this.options.mimeTypeService.getMimeTypeByLanguage({
name: language
});
if (!mimetype || mimetype == 'text/plain') {
// if a mimetype cannot be found it will be 'text/plain', therefore do
// not change mode to text/plain, as this could be a step backwards for
// the user experience
return;
}
const editorLanguage = this.options.languageRegistry.findByMIME(mimetype);
if (!editorLanguage) {
return;
}
if (Array.isArray(mimetype)) {
// Contrarily to what types say, mimetype can be an array.
// https://github.com/jupyterlab/jupyterlab/issues/15100
return mimetype[0];
}
return mimetype;
}
async updateMode(adapter, editorAccessor, awaitUpdate = true) {
const topDocument = adapter.virtualDocument;
if (!topDocument) {
return;
}
if (awaitUpdate) {
await topDocument.updateManager.updateDone;
}
const editor = editorAccessor.getEditor();
if (!editor) {
return;
}
const totalArea = editor.state.doc.length;
const overrides = new Map();
for (const map of topDocument.getForeignDocuments(editorAccessor)) {
for (const [range, block] of map.entries()) {
const blockEditor = block.editor.getEditor();
if (blockEditor != editor) {
continue;
}
const coveredArea = editor.getOffsetAt(range.end) - editor.getOffsetAt(range.start);
const coverage = coveredArea / totalArea;
const language = block.virtualDocument.language;
const mode = this.getMode(language);
// if not highlighting mode available, skip this editor
if (typeof mode === 'undefined') {
continue;
}
// change the mode if the majority of the code is the foreign code
if (coverage > this.options.settings.composite.foreignCodeThreshold) {
const original = editor.model.mimeType;
// this will trigger a side effect of switching language by updating
// private language compartment (implementation detail).
editor.model.mimeType = mode;
overrides.set(editor, original);
}
}
}
// restore mode on the editor if it no longer over the threshold
// (but only those which belong to this adapter).
if (!overrides.has(editor)) {
const originalMode = this.originalModes.get(editor);
if (originalMode) {
editor.model.mimeType = originalMode;
}
}
// add new overrides to remember the original mode
for (const [editor, mode] of overrides) {
if (!this.originalModes.has(editor)) {
this.originalModes.set(editor, mode);
}
}
}
}
(function (SyntaxHighlightingFeature) {
SyntaxHighlightingFeature.id = PLUGIN_ID + ':syntax_highlighting';
})(SyntaxHighlightingFeature || (SyntaxHighlightingFeature = {}));
export const SYNTAX_HIGHLIGHTING_PLUGIN = {
id: SyntaxHighlightingFeature.id,
requires: [
ILSPFeatureManager,
IEditorServices,
ISettingRegistry,
IEditorLanguageRegistry,
ILSPDocumentConnectionManager
],
autoStart: true,
activate: async (app, featureManager, editorServices, settingRegistry, languageRegistry, connectionManager) => {
const settings = new FeatureSettings(settingRegistry, SyntaxHighlightingFeature.id);
await settings.ready;
if (settings.composite.disable) {
return;
}
const feature = new SyntaxHighlightingFeature({
settings,
connectionManager,
mimeTypeService: editorServices.mimeTypeService,
languageRegistry
});
featureManager.register(feature);
// return feature;
}
};
//# sourceMappingURL=syntax_highlighting.js.map