@krassowski/jupyterlab-lsp
Version:
Language Server Protocol integration for JupyterLab
119 lines (98 loc) • 3.1 kB
text/typescript
import { CodeEditor } from '@jupyterlab/codeeditor';
import { IFeatureEditorIntegration } from '../feature';
import { IRootPosition } from '../positioning';
import { ILSPLogConsole } from '../tokens';
import { until_ready } from '../utils';
import { VirtualDocument } from '../virtual/document';
import { IEditorChange, IVirtualEditor } from '../virtual/editor';
import IEditor = CodeEditor.IEditor;
export class EditorAdapter<T extends IVirtualEditor<IEditor>> {
features: Map<string, IFeatureEditorIntegration<T>>;
isDisposed = false;
private last_change: IEditorChange | null;
private console: ILSPLogConsole;
constructor(
protected editor: IVirtualEditor<CodeEditor.IEditor>,
protected virtual_document: VirtualDocument,
features = new Array<IFeatureEditorIntegration<T>>(),
console: ILSPLogConsole
) {
this.editor.change.connect(this.saveChange, this);
this.console = console.scope('EditorAdapter');
this.features = new Map();
for (let feature of features) {
feature.register();
if (!feature.is_registered) {
this.console.warn(
'The feature ',
feature,
'was not registered properly'
);
}
this.features.set(feature.feature.id, feature);
}
}
public async updateAfterChange() {
try {
await until_ready(() => this.last_change != null, 30, 22);
} catch (err) {
this.console.log(
'No change obtained from CodeMirror editor within the expected time of 0.66s'
);
return;
}
let change: IEditorChange | null = this.last_change;
let root_position: IRootPosition;
try {
root_position = this.editor.get_cursor_position();
} catch (err) {
this.console.log('Root position not found');
return;
}
try {
let document = this.editor.document_at_root_position(root_position);
if (this.virtual_document !== document) {
return true;
}
if (!change || !change.text.length || !change.text[0].length) {
// deletion - ignore
return true;
}
for (let feature of this.features.values()) {
try {
if (feature.afterChange) {
feature.afterChange(change, root_position);
}
} catch (err) {
console.warn('afterChange of feature', feature, 'failed with', err);
}
}
return true;
} catch (e) {
this.console.log('updateAfterChange failure');
this.console.error(e);
}
this.invalidateLastChange();
return undefined;
}
public invalidateLastChange() {
this.last_change = null;
}
public saveChange(sender: IVirtualEditor<IEditor>, change: IEditorChange) {
this.last_change = change;
}
public dispose() {
if (this.isDisposed) {
return;
}
for (let feature of this.features.values()) {
feature.remove();
}
this.features.clear();
this.editor.change.disconnect(this.saveChange);
// just to be sure
this.editor = null as any;
// actually disposed
this.isDisposed = true;
}
}