UNPKG

@jupyterlab/fileeditor

Version:
211 lines 6.34 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { CodeEditorWrapper } from '@jupyterlab/codeeditor'; import { ABCWidgetFactory, DocumentWidget } from '@jupyterlab/docregistry'; import { textEditorIcon } from '@jupyterlab/ui-components'; import { PromiseDelegate } from '@lumino/coreutils'; import { StackedLayout, Widget } from '@lumino/widgets'; /** * The data attribute added to a widget that can run code. */ const CODE_RUNNER = 'jpCodeRunner'; /** * The data attribute added to a widget that can undo. */ const UNDOER = 'jpUndoer'; /** * A widget for editors. */ export class FileEditor extends Widget { /** * Construct a new editor widget. */ constructor(options) { super(); this._ready = new PromiseDelegate(); this.addClass('jp-FileEditor'); const context = (this._context = options.context); this._mimeTypeService = options.mimeTypeService; const editorWidget = (this._editorWidget = new CodeEditorWrapper({ factory: options.factory, model: context.model, editorOptions: { config: FileEditor.defaultEditorConfig } })); this._editorWidget.addClass('jp-FileEditorCodeWrapper'); this._editorWidget.node.dataset[CODE_RUNNER] = 'true'; this._editorWidget.node.dataset[UNDOER] = 'true'; this.editor = editorWidget.editor; this.model = editorWidget.model; void context.ready.then(() => { this._onContextReady(); }); // Listen for changes to the path. this._onPathChanged(); context.pathChanged.connect(this._onPathChanged, this); const layout = (this.layout = new StackedLayout()); layout.addWidget(editorWidget); } /** * Get the context for the editor widget. */ get context() { return this._context; } /** * A promise that resolves when the file editor is ready. */ get ready() { return this._ready.promise; } /** * Handle the DOM events for the widget. * * @param event - The DOM event sent to the widget. * * #### Notes * This method implements the DOM `EventListener` interface and is * called in response to events on the widget's node. It should * not be called directly by user code. */ handleEvent(event) { if (!this.model) { return; } switch (event.type) { case 'mousedown': this._ensureFocus(); break; default: break; } } /** * Handle `after-attach` messages for the widget. */ onAfterAttach(msg) { super.onAfterAttach(msg); const node = this.node; node.addEventListener('mousedown', this); } /** * Handle `before-detach` messages for the widget. */ onBeforeDetach(msg) { const node = this.node; node.removeEventListener('mousedown', this); } /** * Handle `'activate-request'` messages. */ onActivateRequest(msg) { this._ensureFocus(); } /** * Ensure that the widget has focus. */ _ensureFocus() { if (!this.editor.hasFocus()) { this.editor.focus(); } } /** * Handle actions that should be taken when the context is ready. */ _onContextReady() { if (this.isDisposed) { return; } // Prevent the initial loading from disk from being in the editor history. this.editor.clearHistory(); // Resolve the ready promise. this._ready.resolve(undefined); } /** * Handle a change to the path. */ _onPathChanged() { const editor = this.editor; const localPath = this._context.localPath; editor.model.mimeType = this._mimeTypeService.getMimeTypeByFilePath(localPath); } } /** * The namespace for editor widget statics. */ (function (FileEditor) { /** * File editor default configuration. */ FileEditor.defaultEditorConfig = { lineNumbers: true, scrollPastEnd: true }; })(FileEditor || (FileEditor = {})); /** * A document widget for file editor widgets. */ export class FileEditorWidget extends DocumentWidget { /** * Set URI fragment identifier for text files */ async setFragment(fragment) { const parsedFragments = fragment.split('='); // TODO: expand to allow more schemes of Fragment Identification Syntax // reference: https://datatracker.ietf.org/doc/html/rfc5147#section-3 if (parsedFragments[0] !== '#line') { return; } const positionOrRange = parsedFragments[1]; let firstLine; if (positionOrRange.includes(',')) { // Only respect range start for now. firstLine = positionOrRange.split(',')[0] || '0'; } else { firstLine = positionOrRange; } // Reveal the line return this.context.ready.then(() => { const position = { line: parseInt(firstLine, 10), column: 0 }; this.content.editor.setCursorPosition(position); this.content.editor.revealPosition(position); }); } } /** * A widget factory for editors. */ export class FileEditorFactory extends ABCWidgetFactory { /** * Construct a new editor widget factory. */ constructor(options) { super(options.factoryOptions); this._services = options.editorServices; } /** * Create a new widget given a context. */ createNewWidget(context) { const func = this._services.factoryService.newDocumentEditor; const factory = options => { // Use same id as document factory return func(options); }; const content = new FileEditor({ factory, context, mimeTypeService: this._services.mimeTypeService }); content.title.icon = textEditorIcon; const widget = new FileEditorWidget({ content, context }); return widget; } } //# sourceMappingURL=widget.js.map