Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aligned active text and notebook editor more towards vscode #14190

Merged
merged 4 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -183,10 +183,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) {
msujew marked this conversation as resolved.
Show resolved Hide resolved
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
Loading