Skip to content

Commit

Permalink
Aligned active text and notebook editor more towards vscode (#14190)
Browse files Browse the repository at this point in the history
* active notebook editor now undefined when a text editor is selected

Signed-off-by: Jonah Iden <[email protected]>

* working vscode api active text editor for cells

Signed-off-by: Jonah Iden <[email protected]>

* fixed backend focused cell when creating new

Signed-off-by: Jonah Iden <[email protected]>

* fixed notebook and text editors side by side not being set as active correctly

Signed-off-by: Jonah Iden <[email protected]>

---------

Signed-off-by: Jonah Iden <[email protected]>
  • Loading branch information
jonah-iden authored Sep 20, 2024
1 parent 5312708 commit 0f1ebdf
Show file tree
Hide file tree
Showing 14 changed files with 214 additions and 31 deletions.
6 changes: 2 additions & 4 deletions packages/editor/src/browser/editor-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,8 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
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;
Expand Down
16 changes: 14 additions & 2 deletions packages/monaco/src/browser/simple-monaco-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ 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 {

protected editor: CodeEditorWidget;
protected readonly toDispose = new DisposableCollection();

protected readonly onCursorPositionChangedEmitter = new Emitter<Position>();
protected readonly onSelectionChangedEmitter = new Emitter<Range>();
protected readonly onFocusChangedEmitter = new Emitter<boolean>();
protected readonly onDocumentContentChangedEmitter = new Emitter<TextDocumentChangeEvent>();
readonly onDocumentContentChanged = this.onDocumentContentChangedEmitter.event;
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -158,6 +166,10 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab
};
}

focus(): void {
this.editor.focus();
}

refresh(): void {
this.autoresize();
}
Expand Down
1 change: 1 addition & 0 deletions packages/notebook/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 2 additions & 0 deletions packages/notebook/src/browser/notebook-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<void>();
readonly onDidChangeCellEditors = this.onDidChangeCellEditorsEmitter.event;

protected onDidChangeFocusedCellEditorEmitter = new Emitter<SimpleMonacoEditor | undefined>();
readonly onDidChangeFocusedCellEditor = this.onDidChangeFocusedCellEditorEmitter.event;

protected currentActiveCell?: SimpleMonacoEditor;

protected currentCellEditors: Map<string, SimpleMonacoEditor> = 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;
}
}
23 changes: 18 additions & 5 deletions packages/notebook/src/browser/view/notebook-cell-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -153,6 +155,9 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
}

protected disposeEditor(): void {
if (this.editor) {
this.props.notebookCellEditorService.editorDisposed(this.editor.uri);
}
this.toDispose.dispose();
this.toDispose = new DisposableCollection();
}
Expand Down Expand Up @@ -197,7 +202,14 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
}));
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;
Expand All @@ -212,10 +224,11 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
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.setMatches();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand All @@ -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} />
<NotebookCodeCellStatus cell={cell} notebook={notebookModel}
commandRegistry={this.commandRegistry}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { NotebookOptionsService } from '../service/notebook-options';
import { NotebookCodeCellStatus } from './notebook-code-cell-view';
import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './notebook-find-widget';
import * as mark from 'advanced-mark.js';
import { NotebookCellEditorService } from '../service/notebook-cell-editor-service';

@injectable()
export class NotebookMarkdownCellRenderer implements CellRenderer {
Expand All @@ -47,6 +48,9 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
@inject(NotebookOptionsService)
protected readonly notebookOptionsService: NotebookOptionsService;

@inject(NotebookCellEditorService)
protected readonly notebookCellEditorService: NotebookCellEditorService;

render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode {
return <MarkdownCell
markdownRenderer={this.markdownRenderer}
Expand All @@ -55,7 +59,8 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
notebookOptionsService={this.notebookOptionsService}
cell={cell}
notebookModel={notebookModel}
notebookContextManager={this.notebookContextManager} />;
notebookContextManager={this.notebookContextManager}
notebookCellEditorService={this.notebookCellEditorService} />;
}

renderDragImage(cell: NotebookCellModel): HTMLElement {
Expand All @@ -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;
Expand Down Expand Up @@ -133,6 +139,7 @@ function MarkdownCell({
<CellEditor notebookModel={notebookModel} cell={cell}
monacoServices={monacoServices}
notebookContextManager={notebookContextManager}
notebookCellEditorService={notebookCellEditorService}
fontInfo={notebookOptionsService.editorFontInfo} />
<NotebookCodeCellStatus cell={cell} notebook={notebookModel}
commandRegistry={commandRegistry}
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2673,6 +2673,7 @@ export interface NotebookDocumentsExt {

export interface NotebookDocumentsAndEditorsExt {
$acceptDocumentsAndEditorsDelta(delta: NotebookDocumentsAndEditorsDelta): Promise<void>;
$acceptActiveCellEditorChange(newActiveEditor: string | null): void;
}

export interface NotebookDocumentsAndEditorsMain extends Disposable {
Expand Down
27 changes: 25 additions & 2 deletions packages/plugin-ext/src/main/browser/editors-and-documents-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, NotebookEditorWidgetService } from '@theia/notebook/lib/browser';
import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';

export class EditorsAndDocumentsMain implements Disposable {

Expand Down Expand Up @@ -67,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, 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);
Expand Down Expand Up @@ -218,6 +224,8 @@ class EditorAndDocumentStateComputer implements Disposable {
constructor(
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
) { }
Expand All @@ -240,6 +248,16 @@ 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()));

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);
}
Expand Down Expand Up @@ -318,6 +336,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) {
Expand Down Expand Up @@ -384,7 +407,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}`;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 0f1ebdf

Please sign in to comment.