import { CodeEditor } from '@jupyterlab/codeeditor'; import { CodeMirrorEditor } from '@jupyterlab/codemirror'; import { IDocumentWidget } from '@jupyterlab/docregistry'; import { FileEditor } from '@jupyterlab/fileeditor'; import { ICommandContext } from '../../command_manager'; import { PositionConverter } from '../../converter'; import { LSPExtension } from '../../index'; import { IRootPosition, IVirtualPosition } from '../../positioning'; import { VirtualDocument } from '../../virtual/document'; import { IVirtualEditor } from '../../virtual/editor'; import { WidgetAdapter } from '../adapter'; import IEditor = CodeEditor.IEditor; export class FileEditorAdapter extends WidgetAdapter< IDocumentWidget > { editor: FileEditor; virtual_editor: IVirtualEditor; get document_path() { return this.widget.context.path; } get mime_type() { const codeMirrorMimeType = this.editor.model.mimeType; const contentsModel = this.editor.context.contentsModel; // when MIME type is not known it defaults to 'text/plain', // so if it is different we can accept it as it is if (codeMirrorMimeType != 'text/plain') { return codeMirrorMimeType; } else if (contentsModel) { // a script that does not have a MIME type known by the editor // (no syntax highlight mode), can still be known by the document // registry (and this is arguably easier to extend), so let's check it // just in case; this is also how the "Klingon" language for testing // gets registered, so we need it for tests too. let fileType = this.extension.app.docRegistry.getFileTypeForModel(contentsModel); return fileType.mimeTypes[0]; } else { // "text/plain" this is return codeMirrorMimeType; } } get language_file_extension(): string { let parts = this.document_path.split('.'); return parts[parts.length - 1]; } get ce_editor(): CodeMirrorEditor { return this.editor.editor as CodeMirrorEditor; } get activeEditor(): CodeEditor.IEditor { return this.editor.editor; } constructor( extension: LSPExtension, editor_widget: IDocumentWidget ) { super(extension, editor_widget); this.editor = editor_widget.content; this.initialized = new Promise((resolve, reject) => { this.init_once_ready().then(resolve).catch(reject); }); } protected async init_once_ready() { if (!this.editor.context.isReady) { await this.editor.context.ready; } this.init_virtual(); this.console.log('file ready for connection:', this.path); // connect the document, but do not open it as the adapter will handle this // after registering all features this.connect_document(this.virtual_editor.virtual_document, false).catch( this.console.warn ); this.editor.model.mimeTypeChanged.connect(this.reload_connection, this); } get_editor_index(ce_editor: CodeEditor.IEditor): number { return 0; } get_editor_wrapper(ce_editor: CodeEditor.IEditor): HTMLElement { return this.wrapper_element; } get wrapper_element(): HTMLElement { return this.widget.node; } get path() { return this.widget.context.path; } context_from_active_document(): ICommandContext | null { // short circuit if disposed if (!this.virtual_editor || !this.get_context) { return null; } let editor = this.widget.content.editor; let ce_cursor = editor.getCursorPosition(); let root_position = PositionConverter.ce_to_cm(ce_cursor) as IRootPosition; return this.get_context(root_position); } get_editor_index_at(position: IVirtualPosition): number { return 0; } get editors(): CodeEditor.IEditor[] { return [this.editor.editor]; } create_virtual_document(): VirtualDocument { // TODO: for now the magics and extractors are not used in FileEditor, // although it would make sense to pass extractors (e.g. for CSS in HTML, // or SQL in Python files) in the future. However, these would need to be // a different registry (as we would not want to extract kernel-specific // constructs like magics) return new VirtualDocument({ language: this.language, file_extension: this.language_file_extension, path: this.document_path, overrides_registry: {}, foreign_code_extractors: {}, standalone: true, has_lsp_supported_file: true, console: this.console }); } }