From 5cac9ec9abce0613c00e5a22441bdbcc78741a7b Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 16 Sep 2024 08:55:58 +0200 Subject: [PATCH 1/4] active notebook editor now undefined when a text editor is selected Signed-off-by: Jonah Iden --- packages/plugin-ext/src/plugin/notebook/notebooks.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 6dd1b6da06cf8..4e4c445ed2237 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -97,6 +97,13 @@ export class NotebooksExtImpl implements NotebooksExt { } } }); + + textDocumentsAndEditors.onDidChangeActiveTextEditor(e => { + if (e?.document.uri.scheme !== CellUri.cellUriScheme) { + this.activeNotebookEditor = undefined; + this.onDidChangeActiveNotebookEditorEmitter.fire(undefined); + } + }); } async $provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise { From db8440f166d73d3f74c642ecddadc19711161ea7 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 17 Sep 2024 17:33:22 +0200 Subject: [PATCH 2/4] working vscode api active text editor for cells Signed-off-by: Jonah Iden --- packages/editor/src/browser/editor-manager.ts | 6 +- .../src/browser/simple-monaco-editor.ts | 16 +++++- packages/notebook/src/browser/index.ts | 1 + .../src/browser/notebook-frontend-module.ts | 2 + .../service/notebook-cell-editor-service.ts | 56 +++++++++++++++++++ .../src/browser/view/notebook-cell-editor.tsx | 21 +++++-- .../browser/view/notebook-code-cell-view.tsx | 5 ++ .../view/notebook-markdown-cell-view.tsx | 11 +++- .../plugin-ext/src/common/plugin-api-rpc.ts | 1 + .../browser/editors-and-documents-main.ts | 14 ++++- .../notebook-documents-and-editors-main.ts | 5 +- .../src/main/browser/text-editor-main.ts | 46 ++++++++++++--- .../src/plugin/notebook/notebooks.ts | 16 +++++- packages/plugin-ext/src/plugin/text-editor.ts | 2 +- 14 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 packages/notebook/src/browser/service/notebook-cell-editor-service.ts diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 3545725b893ab..aa3912ec62d14 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -183,10 +183,8 @@ export class EditorManager extends NavigatableWidgetOpenHandler { return this._currentEditor; } protected setCurrentEditor(current: EditorWidget | undefined): void { - if (this._currentEditor !== current) { - this._currentEditor = current; - this.onCurrentEditorChangedEmitter.fire(this._currentEditor); - } + this._currentEditor = current; + this.onCurrentEditorChangedEmitter.fire(this._currentEditor); } protected updateCurrentEditor(): void { const widget = this.shell.currentWidget; diff --git a/packages/monaco/src/browser/simple-monaco-editor.ts b/packages/monaco/src/browser/simple-monaco-editor.ts index 3ef6322f59efb..5c862b81fba94 100644 --- a/packages/monaco/src/browser/simple-monaco-editor.ts +++ b/packages/monaco/src/browser/simple-monaco-editor.ts @@ -25,6 +25,8 @@ import { MonacoEditorModel } from './monaco-editor-model'; import { Dimension, EditorMouseEvent, MouseTarget, Position, TextDocumentChangeEvent } from '@theia/editor/lib/browser'; import * as monaco from '@theia/monaco-editor-core'; import { ElementExt } from '@theia/core/shared/@phosphor/domutils'; +import { Selection } from '@theia/editor/lib/browser/editor'; +import { SelectionDirection } from '@theia/monaco-editor-core/esm/vs/editor/common/core/selection'; export class SimpleMonacoEditor extends MonacoEditorServices implements Disposable { @@ -32,7 +34,6 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab protected readonly toDispose = new DisposableCollection(); protected readonly onCursorPositionChangedEmitter = new Emitter(); - protected readonly onSelectionChangedEmitter = new Emitter(); protected readonly onFocusChangedEmitter = new Emitter(); protected readonly onDocumentContentChangedEmitter = new Emitter(); readonly onDocumentContentChanged = this.onDocumentContentChangedEmitter.event; @@ -57,7 +58,6 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab super(services); this.toDispose.pushAll([ this.onCursorPositionChangedEmitter, - this.onSelectionChangedEmitter, this.onFocusChangedEmitter, this.onDocumentContentChangedEmitter, this.onMouseDownEmitter, @@ -76,6 +76,14 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab return this.editor; } + onSelectionChanged(listener: (range: Selection) => void): Disposable { + return this.editor.onDidChangeCursorSelection(event => + listener({ + ...this.m2p.asRange(event.selection), + direction: event.selection.getDirection() === SelectionDirection.LTR ? 'ltr' : 'rtl' + })); + } + protected create(options?: MonacoEditor.IOptions, override?: EditorServiceOverrides, widgetOptions?: ICodeEditorWidgetOptions): Disposable { const combinedOptions = { ...options, @@ -158,6 +166,10 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab }; } + focus(): void { + this.editor.focus(); + } + refresh(): void { this.autoresize(); } diff --git a/packages/notebook/src/browser/index.ts b/packages/notebook/src/browser/index.ts index 1c213a4415eeb..4fca99bd3a23c 100644 --- a/packages/notebook/src/browser/index.ts +++ b/packages/notebook/src/browser/index.ts @@ -23,5 +23,6 @@ export * from './service/notebook-kernel-service'; export * from './service/notebook-execution-state-service'; export * from './service/notebook-model-resolver-service'; export * from './service/notebook-renderer-messaging-service'; +export * from './service/notebook-cell-editor-service'; export * from './renderers/cell-output-webview'; export * from './notebook-types'; diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 16da00f9c6676..839d16908a17d 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -48,6 +48,7 @@ import { bindNotebookPreferences } from './contributions/notebook-preferences'; import { NotebookOptionsService } from './service/notebook-options'; import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler'; import { NotebookStatusBarContribution } from './contributions/notebook-status-bar-contribution'; +import { NotebookCellEditorService } from './service/notebook-cell-editor-service'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -70,6 +71,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookKernelHistoryService).toSelf().inSingletonScope(); bind(NotebookKernelQuickPickService).toSelf().inSingletonScope(); bind(NotebookClipboardService).toSelf().inSingletonScope(); + bind(NotebookCellEditorService).toSelf().inSingletonScope(); bind(NotebookCellResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookCellResourceResolver); diff --git a/packages/notebook/src/browser/service/notebook-cell-editor-service.ts b/packages/notebook/src/browser/service/notebook-cell-editor-service.ts new file mode 100644 index 0000000000000..e1d338a4b7602 --- /dev/null +++ b/packages/notebook/src/browser/service/notebook-cell-editor-service.ts @@ -0,0 +1,56 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Emitter, URI } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; + +@injectable() +export class NotebookCellEditorService { + + protected onDidChangeCellEditorsEmitter = new Emitter(); + readonly onDidChangeCellEditors = this.onDidChangeCellEditorsEmitter.event; + + protected onDidChangeFocusedCellEditorEmitter = new Emitter(); + readonly onDidChangeFocusedCellEditor = this.onDidChangeFocusedCellEditorEmitter.event; + + protected currentActiveCell?: SimpleMonacoEditor; + + protected currentCellEditors: Map = new Map(); + + get allCellEditors(): SimpleMonacoEditor[] { + return Array.from(this.currentCellEditors.values()); + } + + editorCreated(uri: URI, editor: SimpleMonacoEditor): void { + this.currentCellEditors.set(uri.toString(), editor); + this.onDidChangeCellEditorsEmitter.fire(); + } + + editorDisposed(uri: URI): void { + this.currentCellEditors.delete(uri.toString()); + this.onDidChangeCellEditorsEmitter.fire(); + } + + editorFocusChanged(editor?: SimpleMonacoEditor): void { + this.currentActiveCell = editor; + this.onDidChangeFocusedCellEditorEmitter.fire(editor); + } + + getActiveCell(): SimpleMonacoEditor | undefined { + return this.currentActiveCell; + } +} diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index a0832cc2a2b82..5800292e59976 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -30,13 +30,15 @@ import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/edito import { ModelDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; import { animationFrame } from '@theia/core/lib/browser'; +import { NotebookCellEditorService } from '../service/notebook-cell-editor-service'; interface CellEditorProps { - notebookModel: NotebookModel, - cell: NotebookCellModel, - monacoServices: MonacoEditorServices, + notebookModel: NotebookModel; + cell: NotebookCellModel; + monacoServices: MonacoEditorServices; notebookContextManager: NotebookContextManager; - notebookViewportService?: NotebookViewportService, + notebookCellEditorService: NotebookCellEditorService; + notebookViewportService?: NotebookViewportService; fontInfo?: BareFontInfo; } @@ -153,6 +155,9 @@ export class CellEditor extends React.Component { } protected disposeEditor(): void { + if (this.editor) { + this.props.notebookCellEditorService.editorDisposed(this.editor.uri); + } this.toDispose.dispose(); this.toDispose = new DisposableCollection(); } @@ -197,7 +202,14 @@ export class CellEditor extends React.Component { })); this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => { this.props.notebookModel.setSelectedCell(cell, false); + this.props.notebookCellEditorService.editorFocusChanged(this.editor); + })); + this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => { + if (this.props.notebookCellEditorService.getActiveCell()?.uri.toString() === this.props.cell.uri.toString()) { + this.props.notebookCellEditorService.editorFocusChanged(undefined); + } })); + this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => { const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection); this.props.notebookModel.selectedText = selectedText; @@ -215,6 +227,7 @@ export class CellEditor extends React.Component { if (cell.editing && notebookModel.selectedCell === cell) { this.editor.getControl().focus(); } + this.props.notebookCellEditorService.editorCreated(uri, this.editor); this.setMatches(); } } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 27764fac9f7af..24fb8630d5da9 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -35,6 +35,7 @@ import { EditorPreferences } from '@theia/editor/lib/browser'; import { NotebookOptionsService } from '../service/notebook-options'; import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent'; +import { NotebookCellEditorService } from '../service/notebook-cell-editor-service'; @injectable() export class NotebookCodeCellRenderer implements CellRenderer { @@ -62,6 +63,9 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(EditorPreferences) protected readonly editorPreferences: EditorPreferences; + @inject(NotebookCellEditorService) + protected readonly notebookCellEditorService: NotebookCellEditorService; + @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -86,6 +90,7 @@ export class NotebookCodeCellRenderer implements CellRenderer { monacoServices={this.monacoServices} notebookContextManager={this.notebookContextManager} notebookViewportService={this.notebookViewportService} + notebookCellEditorService={this.notebookCellEditorService} fontInfo={this.notebookOptionsService.editorFontInfo} /> ; + notebookContextManager={this.notebookContextManager} + notebookCellEditorService={this.notebookCellEditorService} />; } renderDragImage(cell: NotebookCellModel): HTMLElement { @@ -77,10 +82,11 @@ interface MarkdownCellProps { notebookModel: NotebookModel; notebookContextManager: NotebookContextManager; notebookOptionsService: NotebookOptionsService; + notebookCellEditorService: NotebookCellEditorService } function MarkdownCell({ - markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry + markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry, notebookCellEditorService }: MarkdownCellProps): React.JSX.Element { const [editMode, setEditMode] = React.useState(cell.editing); let empty = false; @@ -133,6 +139,7 @@ function MarkdownCell({ ; + $acceptActiveCellEditorChange(newActiveEditor: string | null): void; } export interface NotebookDocumentsAndEditorsMain extends Disposable { diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index f0e8c8b633e29..59861414b27c1 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -34,6 +34,8 @@ import { DisposableCollection, Emitter, URI } from '@theia/core'; import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import { TabsMainImpl } from './tabs/tabs-main'; +import { NotebookCellEditorService } from '@theia/notebook/lib/browser'; +import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; export class EditorsAndDocumentsMain implements Disposable { @@ -67,7 +69,7 @@ export class EditorsAndDocumentsMain implements Disposable { this.modelService = container.get(EditorModelService); this.saveResourceService = container.get(SaveableService); - this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService, tabsMain); + this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, container.get(NotebookCellEditorService), this.modelService, tabsMain); this.toDispose.push(this.stateComputer); this.toDispose.push(this.onTextEditorAddEmitter); this.toDispose.push(this.onTextEditorRemoveEmitter); @@ -218,6 +220,7 @@ class EditorAndDocumentStateComputer implements Disposable { constructor( private callback: (delta: EditorAndDocumentStateDelta) => void, private readonly editorService: EditorManager, + private readonly cellEditorService: NotebookCellEditorService, private readonly modelService: EditorModelService, private readonly tabsMain: TabsMainImpl ) { } @@ -240,6 +243,8 @@ class EditorAndDocumentStateComputer implements Disposable { this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this)); this.toDispose.push(this.modelService.onModelRemoved(() => this.update())); + this.toDispose.push(this.cellEditorService.onDidChangeCellEditors(() => this.update())); + for (const widget of this.editorService.all) { this.onTextEditorAdd(widget); } @@ -318,6 +323,11 @@ class EditorAndDocumentStateComputer implements Disposable { } } + for (const editor of this.cellEditorService.allCellEditors) { + const editorSnapshot = new EditorSnapshot(editor); + editors.set(editorSnapshot.id, editorSnapshot); + }; + const newState = new EditorAndDocumentState(models, editors, activeId); const delta = EditorAndDocumentState.compute(this.currentState, newState); if (!delta.isEmpty) { @@ -384,7 +394,7 @@ class EditorAndDocumentState { class EditorSnapshot { readonly id: string; - constructor(readonly editor: MonacoEditor) { + constructor(readonly editor: MonacoEditor | SimpleMonacoEditor) { this.id = `${editor.getControl().getId()},${editor.getControl().getModel()!.id}`; } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index 46c73e5a2fec4..11285a9f55101 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -21,7 +21,7 @@ import { Disposable, DisposableCollection } from '@theia/core'; import { interfaces } from '@theia/core/shared/inversify'; import { UriComponents } from '@theia/core/lib/common/uri'; -import { NotebookEditorWidget, NotebookService, NotebookEditorWidgetService } from '@theia/notebook/lib/browser'; +import { NotebookEditorWidget, NotebookService, NotebookEditorWidgetService, NotebookCellEditorService } from '@theia/notebook/lib/browser'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { MAIN_RPC_CONTEXT, NotebookDocumentsAndEditorsDelta, NotebookDocumentsAndEditorsMain, NotebookEditorAddData, NotebookModelAddedData, NotebooksExt } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; @@ -105,6 +105,9 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain this.notebookService = container.get(NotebookService); this.notebookEditorService = container.get(NotebookEditorWidgetService); this.WidgetManager = container.get(WidgetManager); + const notebookCellEditorService = container.get(NotebookCellEditorService); + + notebookCellEditorService.onDidChangeFocusedCellEditor(editor => this.proxy.$acceptActiveCellEditorChange(editor?.uri.toString() ?? null), this, this.disposables); this.notebookService.onDidAddNotebookDocument(async () => this.updateState(), this, this.disposables); this.notebookService.onDidRemoveNotebookDocument(async () => this.updateState(), this, this.disposables); diff --git a/packages/plugin-ext/src/main/browser/text-editor-main.ts b/packages/plugin-ext/src/main/browser/text-editor-main.ts index 000e83509b276..d3f62e6435198 100644 --- a/packages/plugin-ext/src/main/browser/text-editor-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editor-main.ts @@ -33,11 +33,13 @@ import { Range } from '../../common/plugin-api-rpc-model'; import { Emitter, Event } from '@theia/core'; import { TextEditorCursorStyle, cursorStyleToString } from '../../common/editor-options'; import { TextEditorLineNumbersStyle, EndOfLine } from '../../plugin/types-impl'; +import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; +import { EndOfLineSequence, ITextModel } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; export class TextEditorMain implements Disposable { private properties: TextEditorPropertiesMain | undefined; - private editor: MonacoEditor | undefined; + private editor: MonacoEditor | SimpleMonacoEditor | undefined; private readonly onPropertiesChangedEmitter = new Emitter(); @@ -48,8 +50,8 @@ export class TextEditorMain implements Disposable { constructor( private id: string, - private model: monaco.editor.IModel, - editor: MonacoEditor + private model: monaco.editor.IModel | ITextModel, + editor: MonacoEditor | SimpleMonacoEditor ) { this.toDispose.push(this.model.onDidChangeOptions(() => this.updateProperties(undefined) @@ -63,7 +65,25 @@ export class TextEditorMain implements Disposable { } private updateProperties(source?: string): void { - this.setProperties(TextEditorPropertiesMain.readFromEditor(this.properties, this.model, this.editor!), source); + if (this.editor instanceof MonacoEditor) { + this.setProperties(TextEditorPropertiesMain.readFromEditor(this.properties, this.model as monaco.editor.IModel, this.editor!), source); + } else if (this.editor) { + this.setProperties(new TextEditorPropertiesMain( + this.editor.getControl().getSelections()?.map(selection => new monaco.Selection( + selection.startLineNumber, + selection.startColumn, + selection.positionLineNumber, + selection.positionColumn)) ?? [], + { + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On, + insertSpaces: this.model.getOptions().insertSpaces, + tabSize: this.model.getOptions().tabSize, + indentSize: this.model.getOptions().indentSize + }, + [] + ), source); + } } private setProperties(newProperties: TextEditorPropertiesMain, source: string | undefined): void { @@ -76,7 +96,7 @@ export class TextEditorMain implements Disposable { protected readonly toDisposeOnEditor = new DisposableCollection(); - private setEditor(editor?: MonacoEditor): void { + private setEditor(editor?: MonacoEditor | SimpleMonacoEditor): void { if (this.editor === editor) { return; } @@ -115,7 +135,7 @@ export class TextEditorMain implements Disposable { return this.id; } - getModel(): monaco.editor.IModel { + getModel(): monaco.editor.IModel | ITextModel { return this.model; } @@ -208,7 +228,7 @@ export class TextEditorMain implements Disposable { } revealRange(range: monaco.Range, revealType: TextEditorRevealType): void { - if (!this.editor) { + if (!this.editor || this.editor instanceof SimpleMonacoEditor) { return; } switch (revealType) { @@ -240,10 +260,14 @@ export class TextEditorMain implements Disposable { return false; } - if (opts.setEndOfLine === EndOfLine.CRLF) { + if (opts.setEndOfLine === EndOfLine.CRLF && !this.isSimpleWidget(this.model)) { this.model.setEOL(monaco.editor.EndOfLineSequence.CRLF); - } else if (opts.setEndOfLine === EndOfLine.LF) { + } else if (opts.setEndOfLine === EndOfLine.LF && !this.isSimpleWidget(this.model)) { this.model.setEOL(monaco.editor.EndOfLineSequence.LF); + } else if (opts.setEndOfLine === EndOfLine.CRLF && this.isSimpleWidget(this.model)) { + this.model.setEOL(EndOfLineSequence.CRLF); + } else if (opts.setEndOfLine === EndOfLine.LF && this.isSimpleWidget(this.model)) { + this.model.setEOL(EndOfLineSequence.CRLF); } const editOperations: monaco.editor.IIdentifiedSingleEditOperation[] = []; @@ -311,6 +335,10 @@ export class TextEditorMain implements Disposable { private static toMonacoSelections(selection: Selection): monaco.Selection { return new monaco.Selection(selection.selectionStartLineNumber, selection.selectionStartColumn, selection.positionLineNumber, selection.positionColumn); } + + private isSimpleWidget(model: monaco.editor.IModel | ITextModel): model is ITextModel { + return !!(model as ITextModel).isForSimpleWidget; + } } // TODO move to monaco typings! diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 4e4c445ed2237..891354a3b1fdf 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -99,7 +99,7 @@ export class NotebooksExtImpl implements NotebooksExt { }); textDocumentsAndEditors.onDidChangeActiveTextEditor(e => { - if (e?.document.uri.scheme !== CellUri.cellUriScheme) { + if (e && e?.document.uri.scheme !== CellUri.cellUriScheme && this.activeNotebookEditor) { this.activeNotebookEditor = undefined; this.onDidChangeActiveNotebookEditorEmitter.fire(undefined); } @@ -138,6 +138,15 @@ export class NotebooksExtImpl implements NotebooksExt { this.statusBarRegistry.delete(cacheId); } + $acceptActiveCellEditorChange(newActiveEditor: string | null): void { + const newActiveEditorId = this.textDocumentsAndEditors.allEditors().find(editor => editor.document.uri.toString() === newActiveEditor)?.id; + if (newActiveEditorId || newActiveEditor === null) { + this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ + newActiveEditor: newActiveEditorId ?? null + }); + } + } + // --- serialize/deserialize private currentSerializerHandle = 0; @@ -326,6 +335,11 @@ export class NotebooksExtImpl implements NotebooksExt { console.error(`FAILED to find active notebook editor ${delta.newActiveEditor}`); } this.activeNotebookEditor = this.editors.get(delta.newActiveEditor); + if (!(this.textDocumentsAndEditors.activeEditor()?.document.uri.path === this.activeNotebookEditor?.notebookData.uri.path)) { + this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ + newActiveEditor: null + }); + } } if (delta.newActiveEditor !== undefined) { this.onDidChangeActiveNotebookEditorEmitter.fire(this.activeNotebookEditor?.apiEditor); diff --git a/packages/plugin-ext/src/plugin/text-editor.ts b/packages/plugin-ext/src/plugin/text-editor.ts index 99c35fe8572ca..98f578f3b567a 100644 --- a/packages/plugin-ext/src/plugin/text-editor.ts +++ b/packages/plugin-ext/src/plugin/text-editor.ts @@ -30,7 +30,7 @@ export class TextEditorExt implements theia.TextEditor { private disposed = false; constructor( private readonly proxy: TextEditorsMain, - private readonly id: string, + readonly id: string, document: DocumentDataExt, private _selections: Selection[], options: TextEditorConfiguration, From e6059d5e56b999196f17ddf6daec462c6894d1d0 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 18 Sep 2024 10:23:38 +0200 Subject: [PATCH 3/4] fixed backend focused cell when creating new Signed-off-by: Jonah Iden --- .../src/browser/view/notebook-cell-editor.tsx | 4 +- .../src/main/browser/text-editor-main.ts | 63 +++++++++++-------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 5800292e59976..1a42c7669ad38 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -224,11 +224,11 @@ export class CellEditor extends React.Component { this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, false); } })); + this.props.notebookCellEditorService.editorCreated(uri, this.editor); + this.setMatches(); if (cell.editing && notebookModel.selectedCell === cell) { this.editor.getControl().focus(); } - this.props.notebookCellEditorService.editorCreated(uri, this.editor); - this.setMatches(); } } diff --git a/packages/plugin-ext/src/main/browser/text-editor-main.ts b/packages/plugin-ext/src/main/browser/text-editor-main.ts index d3f62e6435198..f71d93a18d3fe 100644 --- a/packages/plugin-ext/src/main/browser/text-editor-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editor-main.ts @@ -35,6 +35,7 @@ import { TextEditorCursorStyle, cursorStyleToString } from '../../common/editor- import { TextEditorLineNumbersStyle, EndOfLine } from '../../plugin/types-impl'; import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; import { EndOfLineSequence, ITextModel } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; +import { EditorOption, RenderLineNumbersType } from '@theia/monaco-editor-core/esm/vs/editor/common/config/editorOptions'; export class TextEditorMain implements Disposable { @@ -65,25 +66,7 @@ export class TextEditorMain implements Disposable { } private updateProperties(source?: string): void { - if (this.editor instanceof MonacoEditor) { - this.setProperties(TextEditorPropertiesMain.readFromEditor(this.properties, this.model as monaco.editor.IModel, this.editor!), source); - } else if (this.editor) { - this.setProperties(new TextEditorPropertiesMain( - this.editor.getControl().getSelections()?.map(selection => new monaco.Selection( - selection.startLineNumber, - selection.startColumn, - selection.positionLineNumber, - selection.positionColumn)) ?? [], - { - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: TextEditorLineNumbersStyle.On, - insertSpaces: this.model.getOptions().insertSpaces, - tabSize: this.model.getOptions().tabSize, - indentSize: this.model.getOptions().indentSize - }, - [] - ), source); - } + this.setProperties(TextEditorPropertiesMain.readFromEditor(this.properties, this.model, this.editor!), source); } private setProperties(newProperties: TextEditorPropertiesMain, source: string | undefined): void { @@ -390,17 +373,26 @@ export class TextEditorPropertiesMain { return undefined; } - static readFromEditor(prevProperties: TextEditorPropertiesMain | undefined, model: monaco.editor.IModel, editor: MonacoEditor): TextEditorPropertiesMain { + static readFromEditor(prevProperties: TextEditorPropertiesMain | undefined, + model: monaco.editor.IModel | ITextModel, + editor: MonacoEditor | SimpleMonacoEditor): TextEditorPropertiesMain { + const selections = TextEditorPropertiesMain.getSelectionsFromEditor(prevProperties, editor); const options = TextEditorPropertiesMain.getOptionsFromEditor(prevProperties, model, editor); const visibleRanges = TextEditorPropertiesMain.getVisibleRangesFromEditor(prevProperties, editor); return new TextEditorPropertiesMain(selections, options, visibleRanges); } - private static getSelectionsFromEditor(prevProperties: TextEditorPropertiesMain | undefined, editor: MonacoEditor): monaco.Selection[] { + private static getSelectionsFromEditor(prevProperties: TextEditorPropertiesMain | undefined, editor: MonacoEditor | SimpleMonacoEditor): monaco.Selection[] { let result: monaco.Selection[] | undefined = undefined; - if (editor) { + if (editor && editor instanceof MonacoEditor) { result = editor.getControl().getSelections() || undefined; + } else if (editor && editor instanceof SimpleMonacoEditor) { + result = editor.getControl().getSelections()?.map(selection => new monaco.Selection( + selection.startLineNumber, + selection.startColumn, + selection.positionLineNumber, + selection.positionColumn)); } if (!result && prevProperties) { @@ -413,14 +405,16 @@ export class TextEditorPropertiesMain { return result; } - private static getOptionsFromEditor(prevProperties: TextEditorPropertiesMain | undefined, model: monaco.editor.IModel, editor: MonacoEditor): TextEditorConfiguration { + private static getOptionsFromEditor(prevProperties: TextEditorPropertiesMain | undefined, + model: monaco.editor.IModel | ITextModel, + editor: MonacoEditor | SimpleMonacoEditor): TextEditorConfiguration { if (model.isDisposed()) { return prevProperties!.options; } let cursorStyle: TextEditorCursorStyle; let lineNumbers: TextEditorLineNumbersStyle; - if (editor) { + if (editor && editor instanceof MonacoEditor) { const editorOptions = editor.getControl().getOptions(); const lineNumbersOpts = editorOptions.get(monaco.editor.EditorOption.lineNumbers); cursorStyle = editorOptions.get(monaco.editor.EditorOption.cursorStyle); @@ -438,6 +432,25 @@ export class TextEditorPropertiesMain { lineNumbers = TextEditorLineNumbersStyle.On; break; } + } else if (editor && editor instanceof SimpleMonacoEditor) { + const editorOptions = editor.getControl().getOptions(); + const lineNumbersOpts = editorOptions.get(EditorOption.lineNumbers); + cursorStyle = editorOptions.get(EditorOption.cursorStyle); + switch (lineNumbersOpts.renderType) { + case RenderLineNumbersType.Off: + lineNumbers = TextEditorLineNumbersStyle.Off; + break; + case RenderLineNumbersType.Relative: + lineNumbers = TextEditorLineNumbersStyle.Relative; + break; + case RenderLineNumbersType.Interval: + lineNumbers = TextEditorLineNumbersStyle.Interval; + break; + default: + lineNumbers = TextEditorLineNumbersStyle.On; + break; + } + } else if (prevProperties) { cursorStyle = prevProperties.options.cursorStyle; lineNumbers = prevProperties.options.lineNumbers; @@ -456,7 +469,7 @@ export class TextEditorPropertiesMain { }; } - private static getVisibleRangesFromEditor(prevProperties: TextEditorPropertiesMain | undefined, editor: MonacoEditor): monaco.Range[] { + private static getVisibleRangesFromEditor(prevProperties: TextEditorPropertiesMain | undefined, editor: MonacoEditor | SimpleMonacoEditor): monaco.Range[] { if (editor) { return editor.getControl().getVisibleRanges(); } From 4400f11596f01275bfd73b63c9f0b15381d6780c Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 18 Sep 2024 16:28:54 +0200 Subject: [PATCH 4/4] fixed notebook and text editors side by side not being set as active correctly Signed-off-by: Jonah Iden --- .../main/browser/editors-and-documents-main.ts | 17 +++++++++++++++-- .../plugin-ext/src/plugin/notebook/notebooks.ts | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index 59861414b27c1..f88d72dc27b0b 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -34,7 +34,7 @@ import { DisposableCollection, Emitter, URI } from '@theia/core'; import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import { TabsMainImpl } from './tabs/tabs-main'; -import { NotebookCellEditorService } from '@theia/notebook/lib/browser'; +import { NotebookCellEditorService, NotebookEditorWidgetService } from '@theia/notebook/lib/browser'; import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; export class EditorsAndDocumentsMain implements Disposable { @@ -69,7 +69,11 @@ export class EditorsAndDocumentsMain implements Disposable { this.modelService = container.get(EditorModelService); this.saveResourceService = container.get(SaveableService); - this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, container.get(NotebookCellEditorService), this.modelService, tabsMain); + this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), + this.editorManager, + container.get(NotebookCellEditorService), + container.get(NotebookEditorWidgetService), + this.modelService, tabsMain); this.toDispose.push(this.stateComputer); this.toDispose.push(this.onTextEditorAddEmitter); this.toDispose.push(this.onTextEditorRemoveEmitter); @@ -221,6 +225,7 @@ class EditorAndDocumentStateComputer implements Disposable { private callback: (delta: EditorAndDocumentStateDelta) => void, private readonly editorService: EditorManager, private readonly cellEditorService: NotebookCellEditorService, + private readonly notebookWidgetService: NotebookEditorWidgetService, private readonly modelService: EditorModelService, private readonly tabsMain: TabsMainImpl ) { } @@ -245,6 +250,14 @@ class EditorAndDocumentStateComputer implements Disposable { this.toDispose.push(this.cellEditorService.onDidChangeCellEditors(() => this.update())); + this.toDispose.push(this.notebookWidgetService.onDidChangeFocusedEditor(() => { + this.currentState = this.currentState && new EditorAndDocumentState( + this.currentState.documents, + this.currentState.editors, + undefined + ); + })); + for (const widget of this.editorService.all) { this.onTextEditorAdd(widget); } diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 891354a3b1fdf..5470495d402ec 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -335,7 +335,7 @@ export class NotebooksExtImpl implements NotebooksExt { console.error(`FAILED to find active notebook editor ${delta.newActiveEditor}`); } this.activeNotebookEditor = this.editors.get(delta.newActiveEditor); - if (!(this.textDocumentsAndEditors.activeEditor()?.document.uri.path === this.activeNotebookEditor?.notebookData.uri.path)) { + if (this.textDocumentsAndEditors.activeEditor()?.document.uri.path !== this.activeNotebookEditor?.notebookData.uri.path) { this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ newActiveEditor: null });