diff --git a/src/client/datascience/interactive-common/interactiveWindowTypes.ts b/src/client/datascience/interactive-common/interactiveWindowTypes.ts index 6c3e69e1592b..0a7e6eb2deb8 100644 --- a/src/client/datascience/interactive-common/interactiveWindowTypes.ts +++ b/src/client/datascience/interactive-common/interactiveWindowTypes.ts @@ -3,6 +3,7 @@ 'use strict'; import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; import { IServerState } from '../../../datascience-ui/interactive-common/mainState'; +import { IAddCellAction } from '../../../datascience-ui/interactive-common/redux/reducers/types'; import { CssMessages, IGetCssRequest, IGetCssResponse, IGetMonacoThemeRequest } from '../messages'; import { ICell, IInteractiveWindowInfo, IJupyterVariable, IJupyterVariablesRequest, IJupyterVariablesResponse } from '../types'; @@ -307,7 +308,7 @@ export class IInteractiveWindowMapping { public [InteractiveWindowMessages.GetAllCells]: ICell; public [InteractiveWindowMessages.ReturnAllCells]: ICell[]; public [InteractiveWindowMessages.DeleteCell]: never | undefined; - public [InteractiveWindowMessages.DeleteAllCells]: never | undefined; + public [InteractiveWindowMessages.DeleteAllCells]: IAddCellAction; public [InteractiveWindowMessages.Undo]: never | undefined; public [InteractiveWindowMessages.Redo]: never | undefined; public [InteractiveWindowMessages.ExpandAll]: never | undefined; diff --git a/src/datascience-ui/history-react/redux/mapping.ts b/src/datascience-ui/history-react/redux/mapping.ts index 54d4160e955f..3375ae0cd0a7 100644 --- a/src/datascience-ui/history-react/redux/mapping.ts +++ b/src/datascience-ui/history-react/redux/mapping.ts @@ -9,6 +9,7 @@ import { IMainState, IServerState } from '../../interactive-common/mainState'; import { IncomingMessageActions } from '../../interactive-common/redux/postOffice'; import { CommonActionType, + IAddCellAction, ICellAction, ICodeAction, IEditCellAction, @@ -42,7 +43,7 @@ export class IInteractiveActionMapping { public [CommonActionType.GATHER_CELL]: InteractiveReducerFunc; public [CommonActionType.EDIT_CELL]: InteractiveReducerFunc; public [CommonActionType.SUBMIT_INPUT]: InteractiveReducerFunc; - public [CommonActionType.DELETE_ALL_CELLS]: InteractiveReducerFunc; + public [CommonActionType.DELETE_ALL_CELLS]: InteractiveReducerFunc; public [CommonActionType.EXPAND_ALL]: InteractiveReducerFunc; public [CommonActionType.COLLAPSE_ALL]: InteractiveReducerFunc; public [CommonActionType.EDITOR_LOADED]: InteractiveReducerFunc; @@ -63,7 +64,7 @@ export class IInteractiveActionMapping { public [IncomingMessageActions.GETALLCELLS]: InteractiveReducerFunc; public [IncomingMessageActions.EXPANDALL]: InteractiveReducerFunc; public [IncomingMessageActions.COLLAPSEALL]: InteractiveReducerFunc; - public [IncomingMessageActions.DELETEALLCELLS]: InteractiveReducerFunc; + public [IncomingMessageActions.DELETEALLCELLS]: InteractiveReducerFunc; public [IncomingMessageActions.STARTPROGRESS]: InteractiveReducerFunc; public [IncomingMessageActions.STOPPROGRESS]: InteractiveReducerFunc; public [IncomingMessageActions.UPDATESETTINGS]: InteractiveReducerFunc; diff --git a/src/datascience-ui/interactive-common/redux/reducers/types.ts b/src/datascience-ui/interactive-common/redux/reducers/types.ts index 6dccb434d322..9a3f8f893190 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/types.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/types.ts @@ -94,6 +94,14 @@ export interface ICellAction { cellId: string | undefined; } +export interface IAddCellAction { + /** + * Id of the new cell that is to be added. + * If none provided, then generate a new id. + */ + newCellId: string; +} + export interface ICodeAction extends ICellAction { code: string; } @@ -103,9 +111,16 @@ export interface IEditCellAction extends ICodeAction { modelId: string; } -export interface IExecuteAction extends ICodeAction { - moveOp: 'add' | 'select' | 'none'; -} +// I.e. when using the operation `add`, we need the corresponding `IAddCellAction`. +// They are mutually exclusive, if not `add`, then there's no `newCellId`. +export type IExecuteAction = + | (ICodeAction & { + moveOp: 'select' | 'none'; + }) + | (ICodeAction & + IAddCellAction & { + moveOp: 'add'; + }); export interface ICodeCreatedAction extends ICellAction { modelId: string; diff --git a/src/datascience-ui/native-editor/redux/actions.ts b/src/datascience-ui/native-editor/redux/actions.ts index 0ab2f50e7d95..ade4d76b5fde 100644 --- a/src/datascience-ui/native-editor/redux/actions.ts +++ b/src/datascience-ui/native-editor/redux/actions.ts @@ -2,13 +2,14 @@ // Licensed under the MIT License. 'use strict'; import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; - +import * as uuid from 'uuid/v4'; import { NativeCommandType } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { IJupyterVariable, IJupyterVariablesRequest } from '../../../client/datascience/types'; import { CursorPos } from '../../interactive-common/mainState'; import { CommonAction, CommonActionType, + IAddCellAction, ICellAction, ICellAndCursorAction, IChangeCellTypeAction, @@ -25,9 +26,9 @@ import { // See https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object export const actionCreators = { - insertAbove: (cellId: string | undefined): CommonAction => ({ type: CommonActionType.INSERT_ABOVE, payload: { cellId } }), - insertAboveFirst: (): CommonAction => ({ type: CommonActionType.INSERT_ABOVE_FIRST }), - insertBelow: (cellId: string | undefined): CommonAction => ({ type: CommonActionType.INSERT_BELOW, payload: { cellId } }), + insertAbove: (cellId: string | undefined): CommonAction => ({ type: CommonActionType.INSERT_ABOVE, payload: { cellId, newCellId: uuid() } }), + insertAboveFirst: (): CommonAction => ({ type: CommonActionType.INSERT_ABOVE_FIRST, payload: { newCellId: uuid() } }), + insertBelow: (cellId: string | undefined): CommonAction => ({ type: CommonActionType.INSERT_BELOW, payload: { cellId, newCellId: uuid() } }), focusCell: (cellId: string, cursorPos: CursorPos = CursorPos.Current): CommonAction => ({ type: CommonActionType.FOCUS_CELL, payload: { cellId, cursorPos } @@ -37,11 +38,20 @@ export const actionCreators = { type: CommonActionType.SELECT_CELL, payload: { cellId, cursorPos } }), - addCell: (): CommonAction => ({ type: CommonActionType.ADD_NEW_CELL }), - executeCell: (cellId: string, code: string, moveOp: 'add' | 'select' | 'none'): CommonAction => ({ - type: CommonActionType.EXECUTE_CELL, - payload: { cellId, code, moveOp } - }), + addCell: (): CommonAction => ({ type: CommonActionType.ADD_NEW_CELL, payload: { newCellId: uuid() } }), + executeCell: (cellId: string, code: string, moveOp: 'add' | 'select' | 'none'): CommonAction => { + if (moveOp === 'add') { + return { + type: CommonActionType.EXECUTE_CELL, + payload: { cellId, code, moveOp, newCellId: uuid() } + }; + } else { + return { + type: CommonActionType.EXECUTE_CELL, + payload: { cellId, code, moveOp } + }; + } + }, executeAllCells: (): CommonAction => ({ type: CommonActionType.EXECUTE_ALL_CELLS }), executeAbove: (cellId: string): CommonAction => ({ type: CommonActionType.EXECUTE_ABOVE, payload: { cellId } }), executeCellAndBelow: (cellId: string, code: string): CommonAction => ({ type: CommonActionType.EXECUTE_CELL_AND_BELOW, payload: { cellId, code } }), diff --git a/src/datascience-ui/native-editor/redux/mapping.ts b/src/datascience-ui/native-editor/redux/mapping.ts index 2460cd0d3e69..f9b33fde953a 100644 --- a/src/datascience-ui/native-editor/redux/mapping.ts +++ b/src/datascience-ui/native-editor/redux/mapping.ts @@ -9,6 +9,7 @@ import { IMainState, IServerState } from '../../interactive-common/mainState'; import { IncomingMessageActions } from '../../interactive-common/redux/postOffice'; import { CommonActionType, + IAddCellAction, ICellAction, ICellAndCursorAction, IChangeCellTypeAction, @@ -27,12 +28,12 @@ type NativeEditorReducerFunc = ReducerFunc; export type NativeEditorReducerArg = ReducerArg; export class INativeEditorActionMapping { - public [CommonActionType.INSERT_ABOVE]: NativeEditorReducerFunc; - public [CommonActionType.INSERT_BELOW]: NativeEditorReducerFunc; - public [CommonActionType.INSERT_ABOVE_FIRST]: NativeEditorReducerFunc; + public [CommonActionType.INSERT_ABOVE]: NativeEditorReducerFunc; + public [CommonActionType.INSERT_BELOW]: NativeEditorReducerFunc; + public [CommonActionType.INSERT_ABOVE_FIRST]: NativeEditorReducerFunc; public [CommonActionType.FOCUS_CELL]: NativeEditorReducerFunc; public [CommonActionType.UNFOCUS_CELL]: NativeEditorReducerFunc; - public [CommonActionType.ADD_NEW_CELL]: NativeEditorReducerFunc; + public [CommonActionType.ADD_NEW_CELL]: NativeEditorReducerFunc; public [CommonActionType.EXECUTE_CELL]: NativeEditorReducerFunc; public [CommonActionType.EXECUTE_ALL_CELLS]: NativeEditorReducerFunc; public [CommonActionType.EXECUTE_ABOVE]: NativeEditorReducerFunc; @@ -74,9 +75,9 @@ export class INativeEditorActionMapping { public [IncomingMessageActions.LOADALLCELLS]: NativeEditorReducerFunc; public [IncomingMessageActions.NOTEBOOKRUNALLCELLS]: NativeEditorReducerFunc; public [IncomingMessageActions.NOTEBOOKRUNSELECTEDCELL]: NativeEditorReducerFunc; - public [IncomingMessageActions.NOTEBOOKADDCELLBELOW]: NativeEditorReducerFunc; + public [IncomingMessageActions.NOTEBOOKADDCELLBELOW]: NativeEditorReducerFunc; public [IncomingMessageActions.DOSAVE]: NativeEditorReducerFunc; - public [IncomingMessageActions.DELETEALLCELLS]: NativeEditorReducerFunc; + public [IncomingMessageActions.DELETEALLCELLS]: NativeEditorReducerFunc; public [IncomingMessageActions.UNDO]: NativeEditorReducerFunc; public [IncomingMessageActions.REDO]: NativeEditorReducerFunc; public [IncomingMessageActions.STARTPROGRESS]: NativeEditorReducerFunc; diff --git a/src/datascience-ui/native-editor/redux/reducers/creation.ts b/src/datascience-ui/native-editor/redux/reducers/creation.ts index f1be4c8db3aa..fad4c7d3c92f 100644 --- a/src/datascience-ui/native-editor/redux/reducers/creation.ts +++ b/src/datascience-ui/native-editor/redux/reducers/creation.ts @@ -8,7 +8,7 @@ import { ICell, IDataScienceExtraSettings } from '../../../../client/datascience import { createCellVM, createEmptyCell, CursorPos, extractInputText, ICellViewModel, IMainState } from '../../../interactive-common/mainState'; import { createPostableAction } from '../../../interactive-common/redux/postOffice'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; -import { ICellAction } from '../../../interactive-common/redux/reducers/types'; +import { IAddCellAction, ICellAction } from '../../../interactive-common/redux/reducers/types'; import { actionCreators } from '../actions'; import { NativeEditorReducerArg } from '../mapping'; @@ -37,8 +37,8 @@ export namespace Creation { } } - export function insertAbove(arg: NativeEditorReducerArg): IMainState { - const newVM = prepareCellVM(createEmptyCell(uuid(), null), false, arg.prevState.settings); + export function insertAbove(arg: NativeEditorReducerArg): IMainState { + const newVM = prepareCellVM(createEmptyCell(arg.payload.newCellId || uuid(), null), false, arg.prevState.settings); const newList = [...arg.prevState.cellVMs]; // Find the position where we want to insert @@ -69,8 +69,8 @@ export namespace Creation { return result; } - export function insertBelow(arg: NativeEditorReducerArg): IMainState { - const newVM = prepareCellVM(createEmptyCell(uuid(), null), false, arg.prevState.settings); + export function insertBelow(arg: NativeEditorReducerArg): IMainState { + const newVM = prepareCellVM(createEmptyCell(arg.payload.newCellId || uuid(), null), false, arg.prevState.settings); const newList = [...arg.prevState.cellVMs]; // Find the position where we want to insert @@ -104,17 +104,17 @@ export namespace Creation { return result; } - export function insertAboveFirst(arg: NativeEditorReducerArg): IMainState { + export function insertAboveFirst(arg: NativeEditorReducerArg): IMainState { // Get the first cell id const firstCellId = arg.prevState.cellVMs.length > 0 ? arg.prevState.cellVMs[0].cell.id : undefined; // Do what an insertAbove does - return insertAbove({ ...arg, payload: { cellId: firstCellId } }); + return insertAbove({ ...arg, payload: { cellId: firstCellId, newCellId: arg.payload.newCellId } }); } - export function addNewCell(arg: NativeEditorReducerArg): IMainState { + export function addNewCell(arg: NativeEditorReducerArg): IMainState { // Do the same thing that an insertBelow does using the currently selected cell. - return insertBelow({ ...arg, payload: { cellId: arg.prevState.selectedCellId } }); + return insertBelow({ ...arg, payload: { cellId: arg.prevState.selectedCellId, newCellId: arg.payload.newCellId } }); } export function startCell(arg: NativeEditorReducerArg): IMainState { @@ -129,13 +129,13 @@ export namespace Creation { return Helpers.updateOrAdd(arg, (c: ICell, s: IMainState) => prepareCellVM(c, true, s.settings)); } - export function deleteAllCells(arg: NativeEditorReducerArg): IMainState { + export function deleteAllCells(arg: NativeEditorReducerArg): IMainState { // Send messages to other side to indicate the deletes arg.queueAction(createPostableAction(InteractiveWindowMessages.DeleteAllCells)); // Just leave one single blank empty cell const newVM: ICellViewModel = { - cell: createEmptyCell(uuid(), null), + cell: createEmptyCell(arg.payload.newCellId, null), editable: true, inputBlockOpen: true, inputBlockShow: true, diff --git a/src/datascience-ui/native-editor/redux/reducers/execution.ts b/src/datascience-ui/native-editor/redux/reducers/execution.ts index 86be83ac613a..ed51ae4fffe9 100644 --- a/src/datascience-ui/native-editor/redux/reducers/execution.ts +++ b/src/datascience-ui/native-editor/redux/reducers/execution.ts @@ -69,30 +69,28 @@ export namespace Execution { const executeResult = executeRange(arg.prevState, index, index, [arg.payload.code], arg.queueAction); // Modify the execute result if moving - switch (arg.payload.moveOp) { - case 'add': - // Add a new cell below - return Creation.insertBelow({ ...arg, prevState: executeResult }); - - case 'select': - // Select the cell below this one, but don't focus it - if (index < arg.prevState.cellVMs.length - 1) { - return Effects.selectCell({ - ...arg, - prevState: { - ...executeResult - }, - payload: { - ...arg.payload, - cellId: arg.prevState.cellVMs[index + 1].cell.id, - cursorPos: CursorPos.Current - } - }); - } - return executeResult; - - default: - return executeResult; + // Use `if` instead of `switch case` to ensure type safety. + if (arg.payload.moveOp === 'add') { + // Add a new cell below + return Creation.insertBelow({ ...arg, prevState: executeResult, payload: { ...arg.payload } }); + } else if (arg.payload.moveOp === 'select') { + // Select the cell below this one, but don't focus it + if (index < arg.prevState.cellVMs.length - 1) { + return Effects.selectCell({ + ...arg, + prevState: { + ...executeResult + }, + payload: { + ...arg.payload, + cellId: arg.prevState.cellVMs[index + 1].cell.id, + cursorPos: CursorPos.Current + } + }); + } + return executeResult; + } else { + return executeResult; } } return arg.prevState;