Skip to content

Commit

Permalink
Add notebook selected cell status bar item and center selected cell c…
Browse files Browse the repository at this point in the history
…ommand (#14046)

Signed-off-by: Jonah Iden <jonah.iden@typefox.io>
  • Loading branch information
jonah-iden authored Aug 20, 2024
1 parent 7c0ed59 commit d34c0b3
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-s
import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys';
import { NotebookClipboardService } from '../service/notebook-clipboard-service';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { NotebookEditorWidget } from '../notebook-editor-widget';

export namespace NotebookCommands {
export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({
Expand Down Expand Up @@ -87,6 +88,11 @@ export namespace NotebookCommands {
id: 'notebook.find',
category: 'Notebook',
});

export const CENTER_ACTIVE_CELL = Command.toDefaultLocalizedCommand({
id: 'notebook.centerActiveCell',
category: 'Notebook',
});
}

export enum CellChangeDirection {
Expand Down Expand Up @@ -253,6 +259,13 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
}
});

commands.registerCommand(NotebookCommands.CENTER_ACTIVE_CELL, {
execute: (editor?: NotebookEditorWidget) => {
const model = editor ? editor.model : this.notebookEditorWidgetService.focusedEditor?.model;
model?.selectedCell?.requestCenterEditor();
}
});

}

protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler {
Expand Down Expand Up @@ -345,6 +358,11 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
keybinding: 'ctrlcmd+f',
when: `${NOTEBOOK_EDITOR_FOCUSED}`
},
{
command: NotebookCommands.CENTER_ACTIVE_CELL.id,
keybinding: 'ctrlcmd+l',
when: `${NOTEBOOK_EDITOR_FOCUSED}`
}
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// *****************************************************************************
// 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 { inject, injectable } from '@theia/core/shared/inversify';
import { FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser';
import { Disposable } from '@theia/core/lib/common';
import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
import { NotebookEditorWidget } from '../notebook-editor-widget';
import { nls } from '@theia/core';
import { NotebookCommands } from './notebook-actions-contribution';

export const NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID = 'notebook-cell-selection-position';

@injectable()
export class NotebookStatusBarContribution implements FrontendApplicationContribution {

@inject(StatusBar) protected readonly statusBar: StatusBar;
@inject(NotebookEditorWidgetService) protected readonly editorWidgetService: NotebookEditorWidgetService;

protected currentCellSelectionListener: Disposable | undefined;
protected lastFocusedEditor: NotebookEditorWidget | undefined;

onStart(): void {
this.editorWidgetService.onDidChangeFocusedEditor(editor => {
this.currentCellSelectionListener?.dispose();
this.currentCellSelectionListener = editor?.model?.onDidChangeSelectedCell(() =>
this.updateStatusbar(editor)
);
editor?.onDidDispose(() => {
this.lastFocusedEditor = undefined;
this.updateStatusbar();
});
this.updateStatusbar(editor);
this.lastFocusedEditor = editor;
});
if (this.editorWidgetService.focusedEditor) {
this.updateStatusbar();
}
}

protected async updateStatusbar(editor?: NotebookEditorWidget): Promise<void> {
if ((!editor && !this.lastFocusedEditor?.isVisible) || editor?.model?.cells.length === 0) {
this.statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID);
return;
}

await editor?.ready;
if (!editor?.model) {
return;
}

const selectedCellIndex = editor.model.selectedCell ? editor.model.cells.indexOf(editor.model.selectedCell) + 1 : '';

this.statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, {
text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, editor.model.cells.length),
alignment: StatusBarAlignment.RIGHT,
priority: 100,
command: NotebookCommands.CENTER_ACTIVE_CELL.id,
arguments: [editor]
});

}

}
1 change: 1 addition & 0 deletions packages/notebook/src/browser/notebook-editor-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
}
// Ensure that the model is loaded before adding the editor
this.notebookEditorService.addNotebookEditor(this);
this._model.selectedCell = this._model.cells[0];
this.update();
this.notebookContextManager.init(this);
return this._model;
Expand Down
4 changes: 4 additions & 0 deletions packages/notebook/src/browser/notebook-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { NotebookClipboardService } from './service/notebook-clipboard-service';
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';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(NotebookColorContribution).toSelf().inSingletonScope();
Expand Down Expand Up @@ -112,4 +113,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {

bind(NotebookUndoRedoHandler).toSelf().inSingletonScope();
bind(UndoRedoHandler).toService(NotebookUndoRedoHandler);

bind(NotebookStatusBarContribution).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution);
});
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export class NotebookCellModel implements NotebookCell, Disposable {
protected readonly onDidSelectFindMatchEmitter = new Emitter<NotebookCodeEditorFindMatch>();
readonly onDidSelectFindMatch: Event<NotebookCodeEditorFindMatch> = this.onDidSelectFindMatchEmitter.event;

protected onDidRequestCenterEditorEmitter = new Emitter<void>();
readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event;

@inject(NotebookCellModelProps)
protected readonly props: NotebookCellModelProps;

Expand Down Expand Up @@ -299,6 +302,10 @@ export class NotebookCellModel implements NotebookCell, Disposable {
this.onWillBlurCellEditorEmitter.fire();
}

requestCenterEditor(): void {
this.onDidRequestCenterEditorEmitter.fire();
}

spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);
Expand Down
34 changes: 20 additions & 14 deletions packages/notebook/src/browser/view/notebook-cell-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
animationFrame().then(() => this.setMatches());
}));

this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => {
const editorDomNode = this.editor?.getControl().getDomNode();
if (editorDomNode) {
editorDomNode.scrollIntoView({
behavior: 'instant',
block: 'center'
});
} else {
this.container?.scrollIntoView({
behavior: 'instant',
block: 'center'
});
}
}));
this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => this.centerEditorInView()));

this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => {
if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) {
Expand All @@ -155,6 +142,10 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
});
this.toDispose.push(disposable);
}

this.toDispose.push(this.props.cell.onDidRequestCenterEditor(() => {
this.centerEditorInView();
}));
}

override componentWillUnmount(): void {
Expand All @@ -166,6 +157,21 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
this.toDispose = new DisposableCollection();
}

protected centerEditorInView(): void {
const editorDomNode = this.editor?.getControl().getDomNode();
if (editorDomNode) {
editorDomNode.scrollIntoView({
behavior: 'instant',
block: 'center'
});
} else {
this.container?.scrollIntoView({
behavior: 'instant',
block: 'center'
});
}
}

protected async initEditor(): Promise<void> {
const { cell, notebookModel, monacoServices } = this.props;
if (this.container) {
Expand Down

0 comments on commit d34c0b3

Please sign in to comment.