提交 a6af5692 编写于 作者: R Rob Lourens

Add a toolbar with some basic actions

上级 35b51327
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'workbench.notebook.code.insertCellAbove';
export const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'workbench.notebook.code.insertCellBelow';
export const INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID = 'workbench.notebook.markdown.insertCellAbove';
export const INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID = 'workbench.notebook.markdown.insertCellAbove';
export const EDIT_CELL_COMMAND_ID = 'workbench.notebook.cell.edit';
export const SAVE_CELL_COMMAND_ID = 'workbench.notebook.cell.save';
export const DELETE_CELL_COMMAND_ID = 'workbench.notebook.cell.delete';
export const MOVE_CELL_UP_COMMAND_ID = 'workbench.notebook.cell.moveUp';
export const MOVE_CELL_DOWN_COMMAND_ID = 'workbench.notebook.cell.moveDown';
export const EXECUTE_CELL_COMMAND_ID = 'workbench.notebook.cell.execute';
......@@ -4,26 +4,31 @@
*--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { localize } from 'vs/nls';
import { Action2, IAction2Options, MenuId, MenuItemAction, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { DELETE_CELL_COMMAND_ID, EDIT_CELL_COMMAND_ID, INSERT_CODE_CELL_ABOVE_COMMAND_ID, INSERT_CODE_CELL_BELOW_COMMAND_ID, INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID, INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID, MOVE_CELL_DOWN_COMMAND_ID, MOVE_CELL_UP_COMMAND_ID, SAVE_CELL_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/constants';
import { INotebookEditor, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { NOTEBOOK_EDITOR_FOCUSED, NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookCellViewModel';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.executeNotebookCell',
title: 'Execute Notebook Cell',
title: localize('notebookActions.execute', "Execute Notebook Cell"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
primary: KeyMod.WinCtrl | KeyCode.Enter,
win: {
primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Enter
},
weight: KeybindingWeight.WorkbenchContrib
}
});
......@@ -38,7 +43,7 @@ registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.executeNotebookCellSelectBelow',
title: 'Execute Notebook Cell and Select Below',
title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
primary: KeyMod.Shift | KeyCode.Enter,
......@@ -78,7 +83,7 @@ registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.executeNotebookCellInsertBelow',
title: 'Execute Notebook Cell and Insert Below',
title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
primary: KeyMod.Alt | KeyCode.Enter,
......@@ -107,7 +112,7 @@ registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.executeNotebook',
title: 'Execute Notebook'
title: localize('notebookActions.executeNotebook', "Execute Notebook")
});
}
......@@ -134,7 +139,7 @@ registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.quitNotebookEdit',
title: 'Quit Notebook Cell Editing',
title: localize('notebookActions.quitEditing', "Quit Notebook Cell Editing"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext),
primary: KeyCode.Escape,
......@@ -158,43 +163,11 @@ registerAction2(class extends Action2 {
}
});
registerAction2(class extends Action2 {
static readonly ID = 'workbench.action.editNotebookActiveCell';
static readonly LABEL = 'Edit Notebook Active Cell';
constructor() {
super({
id: 'workbench.action.editNotebookActiveCell',
title: 'Edit Notebook Active Cell',
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyCode.Enter,
weight: KeybindingWeight.WorkbenchContrib
}
});
}
async run(accessor: ServicesAccessor): Promise<void> {
let editorService = accessor.get(IEditorService);
let editor = getActiveNotebookEditor(editorService);
if (!editor) {
return;
}
let activeCell = editor.getActiveCell();
if (activeCell) {
editor.editNotebookCell(activeCell);
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.notebook.hideFind',
title: 'Hide Find in Notebook',
title: localize('notebookActions.hideFind', "Hide Find in Notebook"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED),
primary: KeyCode.Escape,
......@@ -210,11 +183,12 @@ registerAction2(class extends Action2 {
editor?.hideFind();
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.notebook.find',
title: 'Find in Notebook',
title: localize('notebookActions.findInNotebook', "Find in Notebook"),
keybinding: {
when: NOTEBOOK_EDITOR_FOCUSED,
primary: KeyCode.KEY_F | KeyMod.CtrlCmd,
......@@ -234,7 +208,7 @@ registerAction2(class extends Action2 {
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: 'workbench.action.executeNotebook',
title: 'Execute Notebook (Run all cells)',
title: localize('notebookActions.menu.executeNotebook', "Execute Notebook (Run all cells)"),
icon: { id: 'codicon/debug-start' }
},
order: -1,
......@@ -246,7 +220,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: 'workbench.action.executeNotebookCell',
title: 'Execute Notebook Cell',
title: localize('notebookActions.menu.execute', "Execute Notebook Cell"),
icon: { id: 'codicon/debug-continue' }
},
order: -1,
......@@ -258,7 +232,7 @@ registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.changeCellToCode',
title: 'Change Cell to Code',
title: localize('notebookActions.changeCellToCode', "Change Cell to Code"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyCode.KEY_Y,
......@@ -276,7 +250,7 @@ registerAction2(class extends Action2 {
constructor() {
super({
id: 'workbench.action.changeCellToMarkdown',
title: 'Change Cell to Markdown',
title: localize('notebookActions.changeCellToMarkdown', "Change Cell to Markdown"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyCode.KEY_M,
......@@ -290,8 +264,9 @@ registerAction2(class extends Action2 {
}
});
function getActiveNotebookEditor(editorService: IEditorService): NotebookEditor | undefined {
const activeEditorPane = editorService.activeEditorPane as NotebookEditor | undefined;
function getActiveNotebookEditor(editorService: IEditorService): INotebookEditor | undefined {
// TODO can `isNotebookEditor` be on INotebookEditor to avoid a circular dependency?
const activeEditorPane = editorService.activeEditorPane as any | undefined;
return activeEditorPane?.isNotebookEditor ? activeEditorPane : undefined;
}
......@@ -361,3 +336,383 @@ function changeActiveCellToKind(kind: CellKind, accessor: ServicesAccessor): voi
editor.focusNotebookCell(newCell, false);
editor.deleteNotebookCell(activeCell);
}
export interface INotebookCellActionContext {
cell: CellViewModel;
notebookEditor: INotebookEditor;
}
function getActiveCellContext(accessor: ServicesAccessor): INotebookCellActionContext | undefined {
const editorService = accessor.get(IEditorService);
const editor = getActiveNotebookEditor(editorService);
if (!editor) {
return;
}
const activeCell = editor.getActiveCell();
if (!activeCell) {
return;
}
return {
cell: activeCell,
notebookEditor: editor
};
}
abstract class InsertCellCommand extends Action2 {
constructor(
desc: Readonly<IAction2Options>,
private kind: CellKind,
private direction: 'above' | 'below'
) {
super(desc);
}
async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise<void> {
if (!context) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}
context.notebookEditor.insertNotebookCell(context.cell, this.kind, this.direction);
}
}
registerAction2(class extends InsertCellCommand {
constructor() {
super(
{
id: INSERT_CODE_CELL_ABOVE_COMMAND_ID,
title: localize('notebookActions.insertCodeCellAbove', "Insert Code Cell Above")
},
CellKind.Code,
'above');
}
});
registerAction2(class extends InsertCellCommand {
constructor() {
super(
{
id: INSERT_CODE_CELL_BELOW_COMMAND_ID,
title: localize('notebookActions.insertCodeCellBelow', "Insert Code Cell Below")
},
CellKind.Code,
'below');
}
});
registerAction2(class extends InsertCellCommand {
constructor() {
super(
{
id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID,
title: localize('notebookActions.insertMarkdownCellAbove', "Insert Markdown Cell Above"),
},
CellKind.Markdown,
'above');
}
});
registerAction2(class extends InsertCellCommand {
constructor() {
super(
{
id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID,
title: localize('notebookActions.insertMarkdownCellBelow', "Insert Markdown Cell Below"),
},
CellKind.Code,
'below');
}
});
export class InsertCodeCellAboveAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: INSERT_CODE_CELL_ABOVE_COMMAND_ID,
title: localize('notebookActions.insertCodeCellAbove', "Insert Code Cell Above")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-add';
}
}
export class InsertCodeCellBelowAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: INSERT_CODE_CELL_BELOW_COMMAND_ID,
title: localize('notebookActions.insertCodeCellBelow', "Insert Code Cell Below")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-add';
}
}
export class InsertMarkdownCellAboveAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: INSERT_MARKDOWN_CELL_ABOVE_COMMAND_ID,
title: localize('notebookActions.insertMarkdownCellAbove', "Insert Markdown Cell Above")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-add';
}
}
export class InsertMarkdownCellBelowAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: INSERT_MARKDOWN_CELL_BELOW_COMMAND_ID,
title: localize('notebookActions.insertMarkdownCellBelow', "Insert Markdown Cell Below")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-add';
}
}
registerAction2(class extends Action2 {
constructor() {
super(
{
id: EDIT_CELL_COMMAND_ID,
title: localize('notebookActions.editCell', "Edit Cell"),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyCode.Enter,
weight: KeybindingWeight.WorkbenchContrib
}
});
}
run(accessor: ServicesAccessor, context?: INotebookCellActionContext) {
if (!context) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}
return context.notebookEditor.editNotebookCell(context.cell);
}
});
export class EditCellAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: EDIT_CELL_COMMAND_ID,
title: localize('notebookActions.editCell', "Edit Cell")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-pencil';
}
}
registerAction2(class extends Action2 {
constructor() {
super(
{
id: SAVE_CELL_COMMAND_ID,
title: localize('notebookActions.saveCell', "Save Cell")
});
}
run(accessor: ServicesAccessor, context?: INotebookCellActionContext) {
if (!context) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}
return context.notebookEditor.saveNotebookCell(context.cell);
}
});
export class SaveCellAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: SAVE_CELL_COMMAND_ID,
title: localize('notebookActions.saveCell', "Save Cell")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-save';
}
}
registerAction2(class extends Action2 {
constructor() {
super(
{
id: DELETE_CELL_COMMAND_ID,
title: localize('notebookActions.deleteCell', "Delete Cell")
});
}
run(accessor: ServicesAccessor, context?: INotebookCellActionContext) {
if (!context) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}
return context.notebookEditor.deleteNotebookCell(context.cell);
}
});
export class DeleteCellAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: DELETE_CELL_COMMAND_ID,
title: localize('notebookActions.deleteCell', "Delete Cell")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-trash';
}
}
async function moveCell(context: INotebookCellActionContext, direction: 'up' | 'down'): Promise<void> {
direction === 'up' ?
context.notebookEditor.moveCellUp(context.cell) :
context.notebookEditor.moveCellDown(context.cell);
}
registerAction2(class extends Action2 {
constructor() {
super(
{
id: MOVE_CELL_UP_COMMAND_ID,
title: localize('notebookActions.moveCellUp', "Move Cell Up")
});
}
async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) {
if (!context) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}
return moveCell(context, 'up');
}
});
export class MoveCellUpAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: MOVE_CELL_UP_COMMAND_ID,
title: localize('notebookActions.moveCellUp', "Move Cell Up")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-arrow-up';
}
}
registerAction2(class extends Action2 {
constructor() {
super(
{
id: MOVE_CELL_DOWN_COMMAND_ID,
title: localize('notebookActions.moveCellDown', "Move Cell Down")
});
}
async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) {
if (!context) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}
return moveCell(context, 'down');
}
});
export class MoveCellDownAction extends MenuItemAction {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService commandService: ICommandService
) {
super(
{
id: MOVE_CELL_DOWN_COMMAND_ID,
title: localize('notebookActions.moveCellDown', "Move Cell Down")
},
undefined,
{ shouldForwardArgs: true },
contextKeyService,
commandService);
this.class = 'codicon-arrow-down';
}
}
......@@ -116,6 +116,16 @@
cursor: pointer;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .monaco-toolbar {
visibility: hidden;
margin-right: 24px;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.focused .monaco-toolbar,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row:hover .monaco-toolbar {
visibility: visible;
}
.monaco-workbench .part.editor > .content .notebook-editor .monaco-tree.focused.no-focused-item:focus:before,
.monaco-workbench .part.editor > .content .notebook-editor .monaco-list:not(.element-focused):focus:before {
outline: none !important;
......
......@@ -13,9 +13,13 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { NotebookViewModel, IModelDecorationsChangeAccessor } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { FindMatch } from 'vs/editor/common/model';
import { Range } from 'vs/editor/common/core/range';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { DisposableStore } from 'vs/base/common/lifecycle';
export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey<boolean>('notebookFindWidgetFocused', false);
export const NOTEBOOK_EDITOR_FOCUSED = new RawContextKey<boolean>('notebookEditorFocused', false);
export interface NotebookLayoutInfo {
width: number;
height: number;
......@@ -58,6 +62,16 @@ export interface INotebookEditor {
*/
deleteNotebookCell(cell: CellViewModel): void;
/**
* Move a cell up one spot
*/
moveCellUp(cell: CellViewModel): void;
/**
* Move a cell down one spot
*/
moveCellDown(cell: CellViewModel): void;
/**
* Switch the cell into editing mode.
*
......@@ -171,9 +185,11 @@ export interface CellRenderTemplate {
container: HTMLElement;
cellContainer: HTMLElement;
menuContainer?: HTMLElement;
toolbar: ToolBar;
editingContainer?: HTMLElement;
outputContainer?: HTMLElement;
editor?: CodeEditorWidget;
disposables: DisposableStore;
}
export interface IOutputTransformContribution {
......
......@@ -571,6 +571,31 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
this.list?.splice(index, 1);
}
moveCellDown(cell: CellViewModel): void {
const index = this.notebookViewModel!.getViewCellIndex(cell);
const newIdx = index + 1;
this.moveCellToIndex(cell, index, newIdx);
}
moveCellUp(cell: CellViewModel): void {
const index = this.notebookViewModel!.getViewCellIndex(cell);
const newIdx = index - 1;
this.moveCellToIndex(cell, index, newIdx);
}
private moveCellToIndex(cell: CellViewModel, index: number, newIdx: number): void {
if (!this.notebookViewModel!.moveCellToIdx(index, newIdx)) {
return;
}
this.list?.splice(index, 1);
this.list!.splice(newIdx, 0, [cell]);
DOM.scheduleAtNextAnimationFrame(() => {
this.list?.revealInCenterIfOutsideViewport(index + 1);
});
}
editNotebookCell(cell: CellViewModel): void {
cell.state = CellState.Editing;
......
......@@ -247,7 +247,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
const wrapperBottom = scrollTop + this.view.renderHeight;
const elementTop = this.view.elementTop(index);
if (ignoreIfInsideViewport && elementTop >= scrollTop && elementTop <= wrapperBottom) {
if (ignoreIfInsideViewport && elementTop >= scrollTop && elementTop < wrapperBottom) {
// inside the viewport
return;
}
......
......@@ -3,28 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!vs/workbench/contrib/notebook/browser/notebook';
import { getZoomLevel } from 'vs/base/browser/browser';
import * as DOM from 'vs/base/browser/dom';
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { Action } from 'vs/base/common/actions';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IAction } from 'vs/base/common/actions';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { deepClone } from 'vs/base/common/objects';
import 'vs/css!vs/workbench/contrib/notebook/browser/notebook';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InsertCodeCellAboveAction, INotebookCellActionContext, InsertCodeCellBelowAction, InsertMarkdownCellAboveAction, InsertMarkdownCellBelowAction, EditCellAction, SaveCellAction, DeleteCellAction, MoveCellUpAction, MoveCellDownAction } from 'vs/workbench/contrib/notebook/browser/contrib/notebookActions';
import { CellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
import { StatefullMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/markdownCell';
import { CellKind, EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellViewModel } from '../../viewModel/notebookCellViewModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EDITOR_TOP_PADDING, EDITOR_BOTTOM_PADDING, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export class NotebookCellListDelegate implements IListVirtualDelegate<CellViewModel> {
private _lineHeight: number;
private _toolbarHeight = 22;
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService
) {
......@@ -33,7 +37,7 @@ export class NotebookCellListDelegate implements IListVirtualDelegate<CellViewMo
}
getHeight(element: CellViewModel): number {
return element.getHeight(this._lineHeight);
return element.getHeight(this._lineHeight) + this._toolbarHeight;
}
hasDynamicHeight(element: CellViewModel): boolean {
......@@ -49,14 +53,15 @@ export class NotebookCellListDelegate implements IListVirtualDelegate<CellViewMo
}
}
class AbstractCellRenderer {
abstract class AbstractCellRenderer {
protected editorOptions: IEditorOptions;
constructor(
protected notebookEditor: INotebookEditor,
private contextMenuService: IContextMenuService,
private configurationService: IConfigurationService,
language: string
protected readonly instantiationService: IInstantiationService,
protected readonly notebookEditor: INotebookEditor,
protected readonly contextMenuService: IContextMenuService,
private readonly configurationService: IConfigurationService,
language: string,
) {
const editorOptions = deepClone(this.configurationService.getValue<IEditorOptions>('editor', { overrideIdentifier: language }));
this.editorOptions = {
......@@ -82,88 +87,16 @@ class AbstractCellRenderer {
}
showContextMenu(listIndex: number | undefined, element: CellViewModel, x: number, y: number) {
const actions: Action[] = [];
const insertAbove = new Action(
'workbench.notebook.code.insertCellAbove',
'Insert Code Cell Above',
undefined,
true,
async () => {
await this.notebookEditor.insertNotebookCell(element, CellKind.Code, 'above');
}
);
actions.push(insertAbove);
const insertBelow = new Action(
'workbench.notebook.code.insertCellBelow',
'Insert Code Cell Below',
undefined,
true,
async () => {
await this.notebookEditor.insertNotebookCell(element, CellKind.Code, 'below');
}
);
actions.push(insertBelow);
const insertMarkdownAbove = new Action(
'workbench.notebook.markdown.insertCellAbove',
'Insert Markdown Cell Above',
undefined,
true,
async () => {
await this.notebookEditor.insertNotebookCell(element, CellKind.Markdown, 'above');
}
);
actions.push(insertMarkdownAbove);
const insertMarkdownBelow = new Action(
'workbench.notebook.markdown.insertCellBelow',
'Insert Markdown Cell Below',
undefined,
true,
async () => {
await this.notebookEditor.insertNotebookCell(element, CellKind.Markdown, 'below');
}
);
actions.push(insertMarkdownBelow);
if (element.cellKind === CellKind.Markdown) {
const editAction = new Action(
'workbench.notebook.editCell',
'Edit Cell',
undefined,
true,
async () => {
this.notebookEditor.editNotebookCell(element);
}
);
actions.push(editAction);
const saveAction = new Action(
'workbench.notebook.saveCell',
'Save Cell',
undefined,
true,
async () => {
this.notebookEditor.saveNotebookCell(element);
}
);
actions.push(saveAction);
}
const deleteCell = new Action(
'workbench.notebook.deleteCell',
'Delete Cell',
undefined,
true,
async () => {
this.notebookEditor.deleteNotebookCell(element);
}
);
actions.push(deleteCell);
const actions: IAction[] = [
this.instantiationService.createInstance(InsertCodeCellAboveAction),
this.instantiationService.createInstance(InsertCodeCellBelowAction),
this.instantiationService.createInstance(InsertMarkdownCellAboveAction),
this.instantiationService.createInstance(InsertMarkdownCellBelowAction),
];
actions.push(...this.getAdditionalContextMenuActions());
actions.push(...[
this.instantiationService.createInstance(DeleteCellAction)
]);
this.contextMenuService.showContextMenu({
getAnchor: () => {
......@@ -172,12 +105,16 @@ class AbstractCellRenderer {
y
};
},
getActions: () => {
return actions;
getActions: () => actions,
getActionsContext: () => <INotebookCellActionContext>{
cell: element,
notebookEditor: this.notebookEditor
},
autoSelectFirstItem: false
});
}
abstract getAdditionalContextMenuActions(): IAction[];
}
export class MarkdownCellRenderer extends AbstractCellRenderer implements IListRenderer<CellViewModel, CellRenderTemplate> {
......@@ -186,11 +123,11 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
constructor(
notehookEditor: INotebookEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@IContextMenuService contextMenuService: IContextMenuService
) {
super(notehookEditor, contextMenuService, configurationService, 'markdown');
super(instantiationService, notehookEditor, contextMenuService, configurationService, 'markdown');
}
get templateId() {
......@@ -202,6 +139,18 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
DOM.addClasses(codeInnerContent, 'cell', 'code');
codeInnerContent.style.display = 'none';
const disposables = new DisposableStore();
const toolbar = new ToolBar(container, this.contextMenuService);
toolbar.setActions([
this.instantiationService.createInstance(MoveCellUpAction),
this.instantiationService.createInstance(MoveCellDownAction),
this.instantiationService.createInstance(InsertCodeCellBelowAction),
this.instantiationService.createInstance(EditCellAction),
this.instantiationService.createInstance(SaveCellAction),
this.instantiationService.createInstance(DeleteCellAction)
])();
disposables.add(toolbar);
container.appendChild(codeInnerContent);
const innerContent = document.createElement('div');
......@@ -212,14 +161,14 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
DOM.addClasses(action, 'menu', 'codicon-settings-gear', 'codicon');
container.appendChild(action);
const template = {
return {
container: container,
cellContainer: innerContent,
menuContainer: action,
editingContainer: codeInnerContent
editingContainer: codeInnerContent,
disposables,
toolbar
};
return template;
}
renderElement(element: CellViewModel, index: number, templateData: CellRenderTemplate, height: number | undefined): void {
......@@ -256,6 +205,18 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
elementDisposable!.add(new StatefullMarkdownCell(this.notebookEditor, element, templateData, this.editorOptions, this.instantiationService));
}
templateData.toolbar!.context = <INotebookCellActionContext>{
cell: element,
notebookEditor: this.notebookEditor
};
}
getAdditionalContextMenuActions(): IAction[] {
return [
this.instantiationService.createInstance(EditCellAction),
this.instantiationService.createInstance(SaveCellAction),
];
}
disposeTemplate(templateData: CellRenderTemplate): void {
......@@ -279,9 +240,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
private renderedEditors: Map<CellViewModel, ICodeEditor | undefined>,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(notebookEditor, contextMenuService, configurationService, 'python');
super(instantiationService, notebookEditor, contextMenuService, configurationService, 'python');
}
get templateId() {
......@@ -289,33 +250,46 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
}
renderTemplate(container: HTMLElement): CellRenderTemplate {
const innerContent = document.createElement('div');
DOM.addClasses(innerContent, 'cell', 'code');
container.appendChild(innerContent);
const editor = this.instantiationService.createInstance(CodeEditorWidget, innerContent, {
const disposables = new DisposableStore();
const toolbarContainer = document.createElement('div');
container.appendChild(toolbarContainer);
DOM.addClasses(toolbarContainer, 'menu', 'codicon-settings-gear', 'codicon');
const toolbar = new ToolBar(container, this.contextMenuService);
toolbar.setActions([
this.instantiationService.createInstance(MoveCellUpAction),
this.instantiationService.createInstance(MoveCellDownAction),
this.instantiationService.createInstance(InsertCodeCellBelowAction),
this.instantiationService.createInstance(DeleteCellAction)
])();
disposables.add(toolbar);
const cellContainer = document.createElement('div');
DOM.addClasses(cellContainer, 'cell', 'code');
container.appendChild(cellContainer);
const editor = this.instantiationService.createInstance(CodeEditorWidget, cellContainer, {
...this.editorOptions,
dimension: {
width: 0,
height: 0
}
}, {});
const action = document.createElement('div');
DOM.addClasses(action, 'menu', 'codicon-settings-gear', 'codicon');
container.appendChild(action);
const menuContainer = document.createElement('div');
DOM.addClasses(menuContainer, 'menu', 'codicon-settings-gear', 'codicon');
container.appendChild(menuContainer);
const outputContainer = document.createElement('div');
DOM.addClasses(outputContainer, 'output');
container.appendChild(outputContainer);
let tempalte = {
container: container,
cellContainer: innerContent,
menuContainer: action,
outputContainer: outputContainer,
editor
return {
container,
cellContainer,
menuContainer,
toolbar,
outputContainer,
editor,
disposables
};
return tempalte;
}
renderElement(element: CellViewModel, index: number, templateData: CellRenderTemplate, height: number | undefined): void {
......@@ -332,7 +306,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
this.disposables.set(element, new DisposableStore());
}
let elementDisposable = this.disposables.get(element);
const elementDisposable = this.disposables.get(element);
elementDisposable?.add(DOM.addStandardDisposableListener(templateData.menuContainer!, 'mousedown', e => {
let { top, height } = DOM.getDomNodePagePosition(templateData.menuContainer!);
......@@ -354,9 +328,19 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
elementDisposable?.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData));
this.renderedEditors.set(element, templateData.editor);
templateData.toolbar!.context = <INotebookCellActionContext>{
cell: element,
notebookEditor: this.notebookEditor
};
}
getAdditionalContextMenuActions(): IAction[] {
return [];
}
disposeTemplate(templateData: CellRenderTemplate): void {
templateData.disposables.clear();
}
disposeElement(element: CellViewModel, index: number, templateData: CellRenderTemplate, height: number | undefined): void {
......
......@@ -129,6 +129,21 @@ export class NotebookViewModel extends Disposable {
viewCell.dispose();
}
moveCellToIdx(index: number, newIdx: number): boolean {
const viewCell = this.viewCells[index];
if (!viewCell) {
return false;
}
this.viewCells.splice(index, 1);
this._model.deleteCell(viewCell.cell);
this.viewCells!.splice(newIdx, 0, viewCell);
this._model.insertCell(viewCell.cell, newIdx);
return true;
}
saveEditorViewState(): INotebookEditorViewState {
const state: { [key: number]: boolean } = {};
this.viewCells.filter(cell => cell.isEditing).forEach(cell => state[cell.cell.handle] = true);
......
......@@ -90,11 +90,24 @@ export class TestNotebookEditor implements INotebookEditor {
constructor(
) { }
setCellSelection(cell: CellViewModel, selection: Range): void {
throw new Error('Method not implemented.');
}
selectElement(cell: CellViewModel): void {
throw new Error('Method not implemented.');
}
setCellSelection(cell: CellViewModel, selection: Range): void {
moveCellDown(cell: CellViewModel): void {
throw new Error('Method not implemented.');
}
moveCellUp(cell: CellViewModel): void {
throw new Error('Method not implemented.');
}
setSelection(cell: CellViewModel, selection: Range): void {
throw new Error('Method not implemented.');
}
revealRangeInView(cell: CellViewModel, range: Range): void {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册