From bfdf3ab83cc3e8cbc3334b877245ce63d1f20adf Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 3 Feb 2020 15:34:50 -0800 Subject: [PATCH 01/14] Checkpoint --- .vscode/launch.json | 2 +- .../interactive-common/interactiveBase.ts | 9 +- .../interactiveWindowMessageListener.ts | 16 ++ .../interactiveWindowTypes.ts | 6 +- .../interactive-common/syncrhonization.ts | 173 ++++++++++++++++++ .../datascience/interactive-common/types.ts | 20 +- .../history-react/interactiveCell.tsx | 9 - .../history-react/redux/actions.ts | 8 +- .../history-react/redux/reducers/creation.ts | 2 +- .../history-react/redux/reducers/effects.ts | 2 +- .../history-react/redux/reducers/execution.ts | 2 +- .../interactive-common/mainState.ts | 2 +- .../interactive-common/redux/helpers.ts | 87 +++++++++ .../interactive-common/redux/postOffice.ts | 32 ++-- .../redux/reducers/kernel.ts | 2 +- .../redux/reducers/monaco.ts | 3 +- .../redux/reducers/transfer.ts | 2 +- .../redux/reducers/types.ts | 10 - .../redux/reducers/variables.ts | 2 +- .../interactive-common/redux/store.ts | 63 +++++-- .../native-editor/nativeCell.tsx | 2 +- .../native-editor/redux/actions.ts | 6 +- .../native-editor/redux/reducers/creation.ts | 2 +- .../native-editor/redux/reducers/effects.ts | 37 +++- .../native-editor/redux/reducers/execution.ts | 2 +- .../native-editor/redux/reducers/movement.ts | 2 +- 26 files changed, 405 insertions(+), 98 deletions(-) create mode 100644 src/client/datascience/interactive-common/syncrhonization.ts create mode 100644 src/datascience-ui/interactive-common/redux/helpers.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index e972d37c7bad..205ee46335a3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,7 +18,7 @@ "outFiles": [ "${workspaceFolder}/out/**/*" ], - "preLaunchTask": "Compile", + // "preLaunchTask": "Compile", "skipFiles": [ "/**" ], diff --git a/src/client/datascience/interactive-common/interactiveBase.ts b/src/client/datascience/interactive-common/interactiveBase.ts index f365aec71c06..a4522a560d25 100644 --- a/src/client/datascience/interactive-common/interactiveBase.ts +++ b/src/client/datascience/interactive-common/interactiveBase.ts @@ -11,6 +11,7 @@ import { CancellationToken, ConfigurationTarget, Event, EventEmitter, Memento, P import { Disposable } from 'vscode-jsonrpc'; import { ServerStatus } from '../../../datascience-ui/interactive-common/mainState'; +import { CommonActionType } from '../../../datascience-ui/interactive-common/redux/reducers/types'; import { IApplicationShell, ICommandManager, IDocumentManager, ILiveShareApi, IWebPanelProvider, IWorkspaceService } from '../../common/application/types'; import { CancellationError } from '../../common/cancellation'; import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../common/constants'; @@ -43,7 +44,7 @@ import { JupyterInstallError } from '../jupyter/jupyterInstallError'; import { JupyterSelfCertsError } from '../jupyter/jupyterSelfCertsError'; import { JupyterKernelPromiseFailedError } from '../jupyter/kernels/jupyterKernelPromiseFailedError'; import { LiveKernelModel } from '../jupyter/kernels/types'; -import { CssMessages } from '../messages'; +import { CssMessages, SharedMessages } from '../messages'; import { ProgressReporter } from '../progress/progressReporter'; import { CellState, @@ -72,6 +73,7 @@ import { } from '../types'; import { WebViewHost } from '../webViewHost'; import { InteractiveWindowMessageListener } from './interactiveWindowMessageListener'; +import { BaseReduxActionPayload } from './types'; @injectable() export abstract class InteractiveBase extends WebViewHost implements IInteractiveBase { @@ -174,6 +176,11 @@ export abstract class InteractiveBase extends WebViewHost }; + this.postMessageInternal(syncPayload.type, syncPayload.payload).ignoreErrors(); + break; case InteractiveWindowMessages.GotoCodeCell: this.handleMessage(message, payload, this.gotoCode); break; diff --git a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts index 7234c41169ba..34273681f72a 100644 --- a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts +++ b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts @@ -10,11 +10,13 @@ import { ILiveShareApi, IWebPanel, IWebPanelMessageListener } from '../../common import { Identifiers, LiveShare } from '../constants'; import { PostOffice } from '../liveshare/postOffice'; import { InteractiveWindowMessages, InteractiveWindowRemoteMessages } from './interactiveWindowTypes'; +import { BaseReduxActionPayload } from './types'; // tslint:disable:no-any // This class listens to messages that come from the local Python Interactive window export class InteractiveWindowMessageListener implements IWebPanelMessageListener { + private static handlers = new Map void>(); private postOffice: PostOffice; private disposedCallback: () => void; private callback: (message: string, payload: any) => void; @@ -40,6 +42,7 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene this.interactiveWindowMessages.forEach(m => { this.postOffice.registerCallback(m, a => callback(m, a)).ignoreErrors(); }); + InteractiveWindowMessageListener.handlers.set(this, callback); } public async dispose() { @@ -48,6 +51,19 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene } public onMessage(message: string, payload: any) { + if (message === InteractiveWindowMessages.Sync) { + const syncPayload = payload as BaseReduxActionPayload; + Array.from(InteractiveWindowMessageListener.handlers.keys()).forEach(item => { + if (item === this) { + return; + } + const cb = InteractiveWindowMessageListener.handlers.get(item); + if (cb) { + cb(InteractiveWindowMessages.Sync, { type: message, payload: syncPayload.data }); + } + }); + return; + } // We received a message from the local webview. Broadcast it to everybody if it's a remote message if (InteractiveWindowRemoteMessages.indexOf(message) >= 0) { this.postOffice.postCommand(message, payload).ignoreErrors(); diff --git a/src/client/datascience/interactive-common/interactiveWindowTypes.ts b/src/client/datascience/interactive-common/interactiveWindowTypes.ts index fe4641254bf9..10e70f2a2ee7 100644 --- a/src/client/datascience/interactive-common/interactiveWindowTypes.ts +++ b/src/client/datascience/interactive-common/interactiveWindowTypes.ts @@ -3,10 +3,11 @@ 'use strict'; import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; import { IServerState } from '../../../datascience-ui/interactive-common/mainState'; -import { IAddCellAction, ICellAction } from '../../../datascience-ui/interactive-common/redux/reducers/types'; +import { CommonActionType, IAddCellAction, ICellAction } from '../../../datascience-ui/interactive-common/redux/reducers/types'; import { CssMessages, IGetCssRequest, IGetCssResponse, IGetMonacoThemeRequest, SharedMessages } from '../messages'; import { IGetMonacoThemeResponse } from '../monacoMessages'; import { ICell, IInteractiveWindowInfo, IJupyterVariable, IJupyterVariablesRequest, IJupyterVariablesResponse } from '../types'; +import { BaseReduxActionPayload } from './types'; export enum InteractiveWindowMessages { StartCell = 'start_cell', @@ -58,6 +59,7 @@ export enum InteractiveWindowMessages { EditCell = 'edit_cell', RemoveCell = 'remove_cell', SwapCells = 'swap_cells', + Sync = 'sync_message_used_to_broadcasst_and_sync_editors', InsertCell = 'insert_cell', LoadOnigasmAssemblyRequest = 'load_onigasm_assembly_request', LoadOnigasmAssemblyResponse = 'load_onigasm_assembly_response', @@ -368,6 +370,8 @@ export class IInteractiveWindowMapping { public [InteractiveWindowMessages.NotebookDirty]: never | undefined; public [InteractiveWindowMessages.NotebookClean]: never | undefined; public [InteractiveWindowMessages.SaveAll]: ISaveAll; + // tslint:disable-next-line: no-any + public [InteractiveWindowMessages.Sync]: { type: InteractiveWindowMessages | SharedMessages | CommonActionType; payload: BaseReduxActionPayload }; public [InteractiveWindowMessages.NativeCommand]: INativeCommand; public [InteractiveWindowMessages.VariablesComplete]: never | undefined; public [InteractiveWindowMessages.NotebookRunAllCells]: never | undefined; diff --git a/src/client/datascience/interactive-common/syncrhonization.ts b/src/client/datascience/interactive-common/syncrhonization.ts new file mode 100644 index 000000000000..4edd3312ce77 --- /dev/null +++ b/src/client/datascience/interactive-common/syncrhonization.ts @@ -0,0 +1,173 @@ +import { CommonActionType, CommonActionTypeMapping } from '../../../datascience-ui/interactive-common/redux/reducers/types'; +import { CssMessages, SharedMessages } from '../messages'; +import { IInteractiveWindowMapping, InteractiveWindowMessages } from './interactiveWindowTypes'; + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +export enum MessageType { + other = 0, + /** + * Messages must be re-broadcasted across other editors of the same file in the same session. + */ + syncAcrossSameNotebooks = 1 << 0, + /** + * Messages must be re-broadcasted across all sessions. + */ + syncWithLiveShare = 1 << 1, + noIdea = 1 << 2 +} + +type MessageMapping = { + [P in keyof T]: MessageType; +}; + +export type IInteractiveActionMapping = MessageMapping; + +// Do not change to a dictionary or a record. +// The current structure ensures all new enums added will be categorized. +// This way, if a new message is added, we'll make the decision early on whether it needs to be synchronized and how. +// Rather than waiting for users to report issues related to new messages. +const messageWithMessageTypes: MessageMapping & MessageMapping = { + [CommonActionType.ADD_NEW_CELL]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.ARROW_DOWN]: MessageType.syncWithLiveShare, + [CommonActionType.ARROW_UP]: MessageType.syncWithLiveShare, + [CommonActionType.CHANGE_CELL_TYPE]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.CLICK_CELL]: MessageType.syncWithLiveShare, + [CommonActionType.CODE_CREATED]: MessageType.noIdea, + [CommonActionType.COPY_CELL_CODE]: MessageType.other, + [CommonActionType.EDITOR_LOADED]: MessageType.other, + [CommonActionType.EDIT_CELL]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.EXECUTE_ABOVE]: MessageType.other, + [CommonActionType.EXECUTE_ALL_CELLS]: MessageType.other, + [CommonActionType.EXECUTE_CELL]: MessageType.other, + [CommonActionType.EXECUTE_CELL_AND_BELOW]: MessageType.other, + [CommonActionType.EXPORT]: MessageType.other, + [CommonActionType.FOCUS_CELL]: MessageType.syncWithLiveShare, + [CommonActionType.GATHER_CELL]: MessageType.other, + [CommonActionType.GET_VARIABLE_DATA]: MessageType.other, + [CommonActionType.GOTO_CELL]: MessageType.syncWithLiveShare, + [CommonActionType.INSERT_ABOVE]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.INSERT_ABOVE_FIRST]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.INSERT_BELOW]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.INTERRUPT_KERNEL]: MessageType.other, + [CommonActionType.LOADED_ALL_CELLS]: MessageType.other, + [CommonActionType.LINK_CLICK]: MessageType.other, + [CommonActionType.MOVE_CELL_DOWN]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.MOVE_CELL_UP]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.RESTART_KERNEL]: MessageType.other, + [CommonActionType.SAVE]: MessageType.other, + [CommonActionType.SCROLL]: MessageType.syncWithLiveShare, + [CommonActionType.SELECT_CELL]: MessageType.syncWithLiveShare, + [CommonActionType.SELECT_SERVER]: MessageType.other, + [CommonActionType.SEND_COMMAND]: MessageType.other, + [CommonActionType.SHOW_DATA_VIEWER]: MessageType.other, + [CommonActionType.SUBMIT_INPUT]: MessageType.other, + [CommonActionType.TOGGLE_INPUT_BLOCK]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.TOGGLE_LINE_NUMBERS]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.TOGGLE_OUTPUT]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.TOGGLE_VARIABLE_EXPLORER]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.UNFOCUS_CELL]: MessageType.syncWithLiveShare, + [CommonActionType.UNMOUNT]: MessageType.other, + + // Types from InteractiveWindowMessages + [InteractiveWindowMessages.Activate]: MessageType.other, + [InteractiveWindowMessages.AddCell]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.AddedSysInfo]: MessageType.other, + [InteractiveWindowMessages.CancelCompletionItemsRequest]: MessageType.other, + [InteractiveWindowMessages.CancelHoverRequest]: MessageType.other, + [InteractiveWindowMessages.CancelResolveCompletionItemRequest]: MessageType.other, + [InteractiveWindowMessages.CancelSignatureHelpRequest]: MessageType.other, + [InteractiveWindowMessages.ClearAllOutputs]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.CollapseAll]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.CopyCodeCell]: MessageType.other, + [InteractiveWindowMessages.DeleteAllCells]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.DeleteCell]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.DoSave]: MessageType.other, + [InteractiveWindowMessages.EditCell]: MessageType.other, + [InteractiveWindowMessages.ExecutionRendered]: MessageType.other, + [InteractiveWindowMessages.ExpandAll]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.Export]: MessageType.other, + [InteractiveWindowMessages.FinishCell]: MessageType.other, + [InteractiveWindowMessages.FocusedCellEditor]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.GatherCodeRequest]: MessageType.other, + [InteractiveWindowMessages.GetAllCells]: MessageType.other, + [InteractiveWindowMessages.GetVariablesRequest]: MessageType.other, + [InteractiveWindowMessages.GetVariablesResponse]: MessageType.other, + [InteractiveWindowMessages.GotoCodeCell]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.GotoCodeCell]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.InsertCell]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.Interrupt]: MessageType.other, + [InteractiveWindowMessages.LoadAllCells]: MessageType.other, + [InteractiveWindowMessages.LoadAllCellsComplete]: MessageType.other, + [InteractiveWindowMessages.LoadOnigasmAssemblyRequest]: MessageType.other, + [InteractiveWindowMessages.LoadOnigasmAssemblyResponse]: MessageType.other, + [InteractiveWindowMessages.LoadTmLanguageRequest]: MessageType.other, + [InteractiveWindowMessages.LoadTmLanguageResponse]: MessageType.other, + [InteractiveWindowMessages.MonacoReady]: MessageType.other, + [InteractiveWindowMessages.NativeCommand]: MessageType.other, + [InteractiveWindowMessages.NotebookAddCellBelow]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.NotebookClean]: MessageType.other, + [InteractiveWindowMessages.NotebookDirty]: MessageType.other, + [InteractiveWindowMessages.NotebookExecutionActivated]: MessageType.other, + [InteractiveWindowMessages.NotebookIdentity]: MessageType.other, + [InteractiveWindowMessages.NotebookRunAllCells]: MessageType.other, + [InteractiveWindowMessages.NotebookRunSelectedCell]: MessageType.other, + [InteractiveWindowMessages.OpenLink]: MessageType.other, + [InteractiveWindowMessages.ProvideCompletionItemsRequest]: MessageType.other, + [InteractiveWindowMessages.ProvideCompletionItemsResponse]: MessageType.other, + [InteractiveWindowMessages.ProvideHoverRequest]: MessageType.other, + [InteractiveWindowMessages.ProvideHoverResponse]: MessageType.other, + [InteractiveWindowMessages.ProvideSignatureHelpRequest]: MessageType.other, + [InteractiveWindowMessages.ProvideSignatureHelpResponse]: MessageType.other, + [InteractiveWindowMessages.ReExecuteCell]: MessageType.other, + [InteractiveWindowMessages.Redo]: MessageType.other, + [InteractiveWindowMessages.RemoteAddCode]: MessageType.other, + [InteractiveWindowMessages.RemoteReexecuteCode]: MessageType.other, + [InteractiveWindowMessages.RemoveCell]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.ResolveCompletionItemRequest]: MessageType.other, + [InteractiveWindowMessages.ResolveCompletionItemResponse]: MessageType.other, + [InteractiveWindowMessages.RestartKernel]: MessageType.other, + [InteractiveWindowMessages.ReturnAllCells]: MessageType.other, + [InteractiveWindowMessages.SaveAll]: MessageType.other, + [InteractiveWindowMessages.SavePng]: MessageType.other, + [InteractiveWindowMessages.ScrollToCell]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.SelectJupyterServer]: MessageType.other, + [InteractiveWindowMessages.SelectKernel]: MessageType.other, + [InteractiveWindowMessages.SendInfo]: MessageType.other, + [InteractiveWindowMessages.SettingsUpdated]: MessageType.other, + [InteractiveWindowMessages.ShowDataViewer]: MessageType.other, + [InteractiveWindowMessages.ShowPlot]: MessageType.other, + [InteractiveWindowMessages.StartCell]: MessageType.other, + [InteractiveWindowMessages.StartDebugging]: MessageType.other, + [InteractiveWindowMessages.StartProgress]: MessageType.other, + [InteractiveWindowMessages.Started]: MessageType.other, + [InteractiveWindowMessages.StopDebugging]: MessageType.other, + [InteractiveWindowMessages.StopProgress]: MessageType.other, + [InteractiveWindowMessages.SubmitNewCell]: MessageType.other, + [InteractiveWindowMessages.SwapCells]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.Sync]: MessageType.other, + [InteractiveWindowMessages.Undo]: MessageType.other, + [InteractiveWindowMessages.UnfocusedCellEditor]: MessageType.syncWithLiveShare, + [InteractiveWindowMessages.UpdateCell]: MessageType.other, + [InteractiveWindowMessages.UpdateKernel]: MessageType.other, + [InteractiveWindowMessages.VariableExplorerToggle]: MessageType.other, + [InteractiveWindowMessages.VariablesComplete]: MessageType.other, + // Types from CssMessages + [CssMessages.GetCssRequest]: MessageType.other, + [CssMessages.GetCssResponse]: MessageType.other, + [CssMessages.GetMonacoThemeRequest]: MessageType.other, + [CssMessages.GetMonacoThemeResponse]: MessageType.other, + // Types from Shared Messages + [SharedMessages.LocInit]: MessageType.other, + [SharedMessages.Started]: MessageType.other, + [SharedMessages.UpdateSettings]: MessageType.other +}; + +export function shouldRebroadcast(message: keyof IInteractiveWindowMapping): [boolean, MessageType] { + if (!messageWithMessageTypes[message]) { + return [false, MessageType.other]; + } + const messageType: MessageType = messageWithMessageTypes[message]; + return [(messageType & MessageType.syncAcrossSameNotebooks) > 0 || (messageType & MessageType.syncWithLiveShare) > 0, messageType]; +} diff --git a/src/client/datascience/interactive-common/types.ts b/src/client/datascience/interactive-common/types.ts index 590bd60fb178..bc419e929d64 100644 --- a/src/client/datascience/interactive-common/types.ts +++ b/src/client/datascience/interactive-common/types.ts @@ -3,17 +3,12 @@ 'use strict'; +import { MessageType } from './syncrhonization'; + // Stuff common to React and Extensions. type BaseData = { - /** - * If this property exists, then this is an action that has been dispatched for the solve purpose of: - * 1. Synchronizing states across different editors (pointing to the same file). - * 2. Synchronizing states across different editors (pointing to the same file) in different sessions. - * - * @type {('syncEditors' | 'syncSessions')} - */ - broadcastReason?: 'syncEditors' | 'syncSessions'; + messageType?: MessageType; /** * Tells us whether this message is incoming for reducer use or * whether this is a message that needs to be sent out to extension (from reducer). @@ -22,14 +17,7 @@ type BaseData = { }; type BaseDataWithPayload = { - /** - * If this property exists, then this is an action that has been dispatched for the solve purpose of: - * 1. Synchronizing states across different editors (pointing to the same file). - * 2. Synchronizing states across different editors (pointing to the same file) in different sessions. - * - * @type {('syncEditors' | 'syncSessions')} - */ - broadcastReason?: 'syncEditors' | 'syncSessions'; + messageType?: MessageType; /** * Tells us whether this message is incoming for reducer use or * whether this is a message that needs to be sent out to extension (from reducer). diff --git a/src/datascience-ui/history-react/interactiveCell.tsx b/src/datascience-ui/history-react/interactiveCell.tsx index 8329fcf7867a..4d5c9f0cc9a5 100644 --- a/src/datascience-ui/history-react/interactiveCell.tsx +++ b/src/datascience-ui/history-react/interactiveCell.tsx @@ -139,7 +139,6 @@ export class InteractiveCell extends React.Component { tabIndex={0} onKeyDown={this.onKeyDown} onClick={this.onMouseClick} - onDoubleClick={this.onMouseDoubleClick} >
{this.renderControls()} @@ -209,14 +208,6 @@ export class InteractiveCell extends React.Component { } }; - private onMouseDoubleClick = (ev: React.MouseEvent) => { - // When we receive double click, propagate upwards. Might change our state - if (this.props.doubleClickCell) { - ev.stopPropagation(); - this.props.doubleClickCell(this.props.cellVM.cell.id); - } - }; - private renderControls = () => { const busy = this.props.cellVM.cell.state === CellState.init || this.props.cellVM.cell.state === CellState.executing; const collapseVisible = this.props.cellVM.inputBlockCollapseNeeded && this.props.cellVM.inputBlockShow && !this.props.cellVM.editable && this.isCodeCell(); diff --git a/src/datascience-ui/history-react/redux/actions.ts b/src/datascience-ui/history-react/redux/actions.ts index b6f55eaec82d..905c40e733a2 100644 --- a/src/datascience-ui/history-react/redux/actions.ts +++ b/src/datascience-ui/history-react/redux/actions.ts @@ -3,13 +3,12 @@ 'use strict'; import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; -import { InteractiveWindowMessages, IRefreshVariablesRequest } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; +import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { IJupyterVariable, IJupyterVariablesRequest } from '../../../client/datascience/types'; +import { createIncomingAction, createIncomingActionWithPayload } from '../../interactive-common/redux/helpers'; import { CommonAction, CommonActionType, - createIncomingAction, - createIncomingActionWithPayload, ICellAction, ICodeAction, ICodeCreatedAction, @@ -21,8 +20,6 @@ import { // See https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object export const actionCreators = { - refreshVariables: (newExecutionCount?: number): CommonAction => - createIncomingActionWithPayload(CommonActionType.REFRESH_VARIABLES, { newExecutionCount }), restartKernel: (): CommonAction => createIncomingAction(CommonActionType.RESTART_KERNEL), interruptKernel: (): CommonAction => createIncomingAction(CommonActionType.INTERRUPT_KERNEL), deleteAllCells: (): CommonAction => createIncomingAction(InteractiveWindowMessages.DeleteAllCells), @@ -36,7 +33,6 @@ export const actionCreators = { copyCellCode: (cellId: string): CommonAction => createIncomingActionWithPayload(CommonActionType.COPY_CELL_CODE, { cellId }), gatherCell: (cellId: string): CommonAction => createIncomingActionWithPayload(CommonActionType.GATHER_CELL, { cellId }), clickCell: (cellId: string): CommonAction => createIncomingActionWithPayload(CommonActionType.CLICK_CELL, { cellId }), - doubleClickCell: (cellId: string): CommonAction => createIncomingActionWithPayload(CommonActionType.DOUBLE_CLICK_CELL, { cellId }), editCell: (cellId: string, changes: monacoEditor.editor.IModelContentChange[], modelId: string, code: string): CommonAction => createIncomingActionWithPayload(CommonActionType.EDIT_CELL, { cellId, changes, modelId, code }), submitInput: (code: string, cellId: string): CommonAction => createIncomingActionWithPayload(CommonActionType.SUBMIT_INPUT, { code, cellId }), diff --git a/src/datascience-ui/history-react/redux/reducers/creation.ts b/src/datascience-ui/history-react/redux/reducers/creation.ts index 607349a9fbdf..e0684a2dbd68 100644 --- a/src/datascience-ui/history-react/redux/reducers/creation.ts +++ b/src/datascience-ui/history-react/redux/reducers/creation.ts @@ -5,7 +5,7 @@ import { Identifiers } from '../../../../client/datascience/constants'; import { InteractiveWindowMessages } from '../../../../client/datascience/interactive-common/interactiveWindowTypes'; import { ICell, IDataScienceExtraSettings } from '../../../../client/datascience/types'; import { createCellVM, extractInputText, ICellViewModel, IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { IAddCellAction, ICellAction } from '../../../interactive-common/redux/reducers/types'; import { InteractiveReducerArg } from '../mapping'; diff --git a/src/datascience-ui/history-react/redux/reducers/effects.ts b/src/datascience-ui/history-react/redux/reducers/effects.ts index 9d6148d53de3..5cb22db9ae94 100644 --- a/src/datascience-ui/history-react/redux/reducers/effects.ts +++ b/src/datascience-ui/history-react/redux/reducers/effects.ts @@ -6,7 +6,7 @@ import { IScrollToCell } from '../../../../client/datascience/interactive-common import { CssMessages } from '../../../../client/datascience/messages'; import { IDataScienceExtraSettings } from '../../../../client/datascience/types'; import { IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { ICellAction, IScrollAction } from '../../../interactive-common/redux/reducers/types'; import { computeEditorOptions } from '../../../react-common/settingsReactSide'; diff --git a/src/datascience-ui/history-react/redux/reducers/execution.ts b/src/datascience-ui/history-react/redux/reducers/execution.ts index 140db015aab9..407911183a67 100644 --- a/src/datascience-ui/history-react/redux/reducers/execution.ts +++ b/src/datascience-ui/history-react/redux/reducers/execution.ts @@ -11,7 +11,7 @@ import { CellState } from '../../../../client/datascience/types'; import { generateMarkdownFromCodeLines } from '../../../common'; import { createCellFrom } from '../../../common/cellFactory'; import { createCellVM, IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { ICodeAction } from '../../../interactive-common/redux/reducers/types'; import { InteractiveReducerArg } from '../mapping'; diff --git a/src/datascience-ui/interactive-common/mainState.ts b/src/datascience-ui/interactive-common/mainState.ts index 5e2e865e2d65..e9f96d13b446 100644 --- a/src/datascience-ui/interactive-common/mainState.ts +++ b/src/datascience-ui/interactive-common/mainState.ts @@ -34,6 +34,7 @@ export interface ICellViewModel { useQuickEdit?: boolean; selected: boolean; focused: boolean; + shouldNotAutoFocus?: boolean; scrollCount: number; cursorPos: CursorPos; hasBeenRun: boolean; @@ -95,7 +96,6 @@ export function getSelectedAndFocusedInfo(state: IMainState) { return info; } - export interface IFont { size: number; family: string; diff --git a/src/datascience-ui/interactive-common/redux/helpers.ts b/src/datascience-ui/interactive-common/redux/helpers.ts new file mode 100644 index 000000000000..8dd0f6be2a4e --- /dev/null +++ b/src/datascience-ui/interactive-common/redux/helpers.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import * as Redux from 'redux'; +import { IInteractiveWindowMapping, InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; +import { shouldRebroadcast } from '../../../client/datascience/interactive-common/syncrhonization'; +import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types'; +import { CssMessages, SharedMessages } from '../../../client/datascience/messages'; +import { CommonAction, CommonActionType } from './reducers/types'; + +const AllowedMessages = [...Object.values(InteractiveWindowMessages), ...Object.values(CssMessages), ...Object.values(SharedMessages), ...Object.values(CommonActionType)]; +export function isAllowedMessage(message: string) { + // tslint:disable-next-line: no-any + return AllowedMessages.includes(message as any); +} +export function isAllowedAction(action: Redux.AnyAction) { + return isAllowedMessage(action.type); +} + +export function createIncomingActionWithPayload(type: CommonActionType | InteractiveWindowMessages, data: T): CommonAction { + // tslint:disable-next-line: no-any + return { type, payload: ({ data, messageDirection: 'incoming' } as any) as BaseReduxActionPayload }; +} +export function createIncomingAction(type: CommonActionType | InteractiveWindowMessages): CommonAction { + return { type, payload: { messageDirection: 'incoming', data: undefined } }; +} + +// Actions created from messages +export function createPostableAction(message: T, payload?: M[T]): Redux.AnyAction { + const newPayload: BaseReduxActionPayload = ({ + data: payload, + messageDirection: 'outgoing' + // tslint:disable-next-line: no-any + } as any) as BaseReduxActionPayload; + return { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } }; + } +} + +type Dispatcher = (action: Redux.AnyAction) => Redux.AnyAction; +/** + * Checks whether a message needs to be re-broadcasted. + */ +export function reBroadcastMessageIfRequired( + storeDispatcher: Dispatcher, + message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, + payload?: BaseReduxActionPayload<{}> +) { + if (typeof payload?.messageType === 'number' || payload?.messageDirection === 'outgoing' || message === InteractiveWindowMessages.Sync) { + return; + } + // Check if we need to re-broadcast this message to other editors/sessions. + // tslint:disable-next-line: no-any + const result = shouldRebroadcast(message as any); + if (result[0]) { + // Mark message as incoming, to indicate this will be sent into the other webviews. + // tslint:disable-next-line: no-any + const syncPayloadData: BaseReduxActionPayload = { data: payload?.data, messageType: result[1], messageDirection: 'incoming' }; + // tslint:disable-next-line: no-any + const syncPayload = { type: message, payload: syncPayloadData } as any; + // Send this out. + storeDispatcher(createPostableAction(InteractiveWindowMessages.Sync, syncPayload)); + } +} +export function reBroadcastMessageIfRequiredX( + storeDispatcher: Function, + message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, + payload?: BaseReduxActionPayload<{}> +) { + if (typeof payload?.messageType === 'number' || payload?.messageDirection === 'outgoing' || message === InteractiveWindowMessages.Sync) { + return; + } + // Check if we need to re-broadcast this message to other editors/sessions. + // tslint:disable-next-line: no-any + const result = shouldRebroadcast(message as any); + if (result[0]) { + // Mark message as incoming, to indicate this will be sent into the other webviews. + // tslint:disable-next-line: no-any + const syncPayloadData: BaseReduxActionPayload = { data: payload?.data, messageType: result[1], messageDirection: 'incoming' }; + // tslint:disable-next-line: no-any + const syncPayload = { type: message, payload: syncPayloadData } as any; + // Send this out. + const action = createPostableAction(InteractiveWindowMessages.Sync, syncPayload); + storeDispatcher(action.type, action.payload); + } +} diff --git a/src/datascience-ui/interactive-common/redux/postOffice.ts b/src/datascience-ui/interactive-common/redux/postOffice.ts index 12cee1739faf..ce628a570eba 100644 --- a/src/datascience-ui/interactive-common/redux/postOffice.ts +++ b/src/datascience-ui/interactive-common/redux/postOffice.ts @@ -3,24 +3,12 @@ 'use strict'; import * as Redux from 'redux'; -import { IInteractiveWindowMapping, InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; +import { IInteractiveWindowMapping } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types'; -import { CssMessages, SharedMessages } from '../../../client/datascience/messages'; import { PostOffice } from '../../react-common/postOffice'; +import { isAllowedAction, reBroadcastMessageIfRequiredX } from './helpers'; import { CommonActionType } from './reducers/types'; -export const AllowedMessages = [...Object.values(InteractiveWindowMessages), ...Object.values(CssMessages), ...Object.values(SharedMessages)]; - -// Actions created from messages -export function createPostableAction(message: T, payload?: M[T]): Redux.AnyAction { - const newPayload: BaseReduxActionPayload = ({ - data: payload, - messageDirection: 'outgoing' - // tslint:disable-next-line: no-any - } as any) as BaseReduxActionPayload; - return { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } }; -} - export function generatePostOfficeSendReducer(postOffice: PostOffice): Redux.Reducer<{}, Redux.AnyAction> { // tslint:disable-next-line: no-function-expression return function(_state: {} | undefined, action: Redux.AnyAction): {} { @@ -29,10 +17,18 @@ export function generatePostOfficeSendReducer(postOffice: PostOffice): Redux.Red // Unwrap the payload that was created in `createPostableAction`. const type = action.payload.type; const payload: BaseReduxActionPayload<{}> | undefined = action.payload.payload; - if (AllowedMessages.find(k => k === type) && payload?.messageDirection === 'outgoing') { - // Just post this to the post office. - // tslint:disable-next-line: no-any - postOffice.sendMessage(type, payload.data as any); + if (isAllowedAction(type)) { + if (payload?.messageDirection === 'outgoing') { + // Just post this to the post office. + // tslint:disable-next-line: no-any + postOffice.sendMessage(action.type, payload.data as any); + } + + if (payload?.messageDirection === 'incoming' && typeof payload?.messageType !== 'number') { + setTimeout(() => { + reBroadcastMessageIfRequiredX(postOffice.sendMessage.bind(postOffice), action.type, action?.payload); + }, 1); + } } } diff --git a/src/datascience-ui/interactive-common/redux/reducers/kernel.ts b/src/datascience-ui/interactive-common/redux/reducers/kernel.ts index 7acface3c37a..924a2489450b 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/kernel.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/kernel.ts @@ -4,7 +4,7 @@ import { InteractiveWindowMessages } from '../../../../client/datascience/interactive-common/interactiveWindowTypes'; import { CellState } from '../../../../client/datascience/types'; import { IMainState, IServerState } from '../../mainState'; -import { createPostableAction } from '../postOffice'; +import { createPostableAction } from '../helpers'; import { CommonActionType, CommonReducerArg } from './types'; export namespace Kernel { diff --git a/src/datascience-ui/interactive-common/redux/reducers/monaco.ts b/src/datascience-ui/interactive-common/redux/reducers/monaco.ts index 08d1558ed628..fc559bcaacfb 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/monaco.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/monaco.ts @@ -20,7 +20,8 @@ import { PostOffice } from '../../../react-common/postOffice'; import { combineReducers, QueuableAction, ReducerArg, ReducerFunc } from '../../../react-common/reduxUtils'; import { IntellisenseProvider } from '../../intellisenseProvider'; import { initializeTokenizer, registerMonacoLanguage } from '../../tokenizer'; -import { CommonActionType, createIncomingAction, ICodeCreatedAction, IEditCellAction } from './types'; +import { createIncomingAction } from '../helpers'; +import { CommonActionType, ICodeCreatedAction, IEditCellAction } from './types'; export interface IMonacoState { onigasmData: ArrayBuffer | undefined; diff --git a/src/datascience-ui/interactive-common/redux/reducers/transfer.ts b/src/datascience-ui/interactive-common/redux/reducers/transfer.ts index ce9852cb33e6..5aa4215e610e 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/transfer.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/transfer.ts @@ -4,7 +4,7 @@ import { InteractiveWindowMessages } from '../../../../client/datascience/interactive-common/interactiveWindowTypes'; import { CssMessages } from '../../../../client/datascience/messages'; import { extractInputText, getSelectedAndFocusedInfo, IMainState } from '../../mainState'; -import { createPostableAction } from '../postOffice'; +import { createPostableAction } from '../helpers'; import { Helpers } from './helpers'; import { CommonActionType, CommonReducerArg, ICellAction, IEditCellAction, ILinkClickAction, ISendCommandAction, IShowDataViewerAction } from './types'; diff --git a/src/datascience-ui/interactive-common/redux/reducers/types.ts b/src/datascience-ui/interactive-common/redux/reducers/types.ts index e30888cbd7a7..58bfdf71ed3f 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/types.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/types.ts @@ -29,8 +29,6 @@ export enum CommonActionType { CLICK_CELL = 'action.click_cell', CODE_CREATED = 'action.code_created', COPY_CELL_CODE = 'action.copy_cell_code', - DESELECT_CELL = 'action.deselect_cell', - DOUBLE_CLICK_CELL = 'action.double_click_cell', EDITOR_LOADED = 'action.editor_loaded', EDIT_CELL = 'action.edit_cell', EXECUTE_ABOVE = 'action.execute_above', @@ -180,11 +178,3 @@ export interface IChangeCellTypeAction { currentCode: string; } export type CommonAction = ActionWithPayload; - -export function createIncomingActionWithPayload(type: CommonActionType | InteractiveWindowMessages, data: T): CommonAction { - // tslint:disable-next-line: no-any - return { type, payload: ({ data, messageDirection: 'incoming' } as any) as BaseReduxActionPayload }; -} -export function createIncomingAction(type: CommonActionType | InteractiveWindowMessages): CommonAction { - return { type, payload: { messageDirection: 'incoming', data: undefined } }; -} diff --git a/src/datascience-ui/interactive-common/redux/reducers/variables.ts b/src/datascience-ui/interactive-common/redux/reducers/variables.ts index 6f51205a2d85..ad4120e6705d 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/variables.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/variables.ts @@ -6,7 +6,7 @@ import { IInteractiveWindowMapping, InteractiveWindowMessages } from '../../../. import { BaseReduxActionPayload } from '../../../../client/datascience/interactive-common/types'; import { ICell, IJupyterVariable, IJupyterVariablesRequest, IJupyterVariablesResponse } from '../../../../client/datascience/types'; import { combineReducers, QueuableAction, ReducerArg, ReducerFunc } from '../../../react-common/reduxUtils'; -import { createPostableAction } from '../postOffice'; +import { createPostableAction } from '../helpers'; import { CommonActionType, CommonActionTypeMapping } from './types'; export type IVariableState = { diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index b05e6e88fc95..d0eab11f2bb3 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -16,7 +16,8 @@ import { combineReducers, createQueueableActionMiddleware, QueuableAction } from import { computeEditorOptions, getDefaultSettings } from '../../react-common/settingsReactSide'; import { createEditableCellVM, generateTestState } from '../mainState'; import { forceLoad } from '../transforms'; -import { AllowedMessages, createPostableAction, generatePostOfficeSendReducer } from './postOffice'; +import { createPostableAction, isAllowedAction, isAllowedMessage } from './helpers'; +import { generatePostOfficeSendReducer } from './postOffice'; import { generateMonacoReducer, IMonacoState } from './reducers/monaco'; import { generateVariableReducer, IVariableState } from './reducers/variables'; @@ -68,8 +69,13 @@ function generateMainReducer(skipDefault: boolean, testMode: boolean, baseThe function createSendInfoMiddleware(): Redux.Middleware<{}, IStore> { return store => next => action => { - const prevState = store.getState(); const res = next(action); + // If the action is part of a sync message, then do not send it to the extension. + if (action.payload && typeof (action.payload as BaseReduxActionPayload).messageType === 'number') { + return res; + } + + const prevState = store.getState(); const afterState = store.getState(); // If cell vm count changed or selected cell changed, send the message @@ -232,6 +238,34 @@ export interface IMainWithVariables extends IMainState { variableState: IVariableState; } +/** + * Middleware that will ensure all actions have `messageDirection` property. + */ +const addMessageDirectionMiddleware: Redux.Middleware = _store => next => (action: Redux.AnyAction) => { + if (isAllowedAction(action)) { + // Ensure all dispatched messages have been flagged as `incoming`. + const payload: BaseReduxActionPayload<{}> = action.payload || {}; + if (!payload.messageDirection) { + action.payload = { ...payload, messageDirection: 'incoming' }; + } + } + + return next(action); +}; +// /** +// * Middleware that will take messages dispatched to reducers, and re-broadcast them if necessary. +// */ +// // tslint:disable-next-line: no-any +// const reBroadcasterMiddleware: Redux.Middleware = store => next => (action: Redux.AnyAction) => { +// // First let the message be processed locally before we can think of passing this onto other editors. +// const result = next(action); + +// if (isAllowedAction(action) && action?.payload?.messageDirection === 'incoming') { +// reBroadcastMessageIfRequired(store.dispatch, action.type, action?.payload); +// } +// return result; +// }; + export function createStore(skipDefault: boolean, baseTheme: string, testMode: boolean, editable: boolean, reducerMap: M) { // Create a post office to listen to store dispatches and allow reducers to // send messages @@ -258,7 +292,8 @@ export function createStore(skipDefault: boolean, baseTheme: string, testMode }); // Create our middleware - const middleware = createMiddleWare(testMode); + const middleware = createMiddleWare(testMode).concat([addMessageDirectionMiddleware]); + // const middleware = createMiddleWare(testMode).concat([addMessageDirectionMiddleware, reBroadcasterMiddleware]); // Use this reducer and middle ware to create a store const store = Redux.createStore(rootReducer, Redux.applyMiddleware(...middleware)); @@ -269,21 +304,17 @@ export function createStore(skipDefault: boolean, baseTheme: string, testMode // tslint:disable-next-line: no-any handleMessage(message: string, payload?: any): boolean { // Double check this is one of our messages. React will actually post messages here too during development - if (!AllowedMessages.find(k => k === message)) { - return true; - } - // Add `isIncomingMessage` property so we can differentiate between messages in reducers. - // This way we: - // - Have one reducer for incoming - // - Have another reducer for outgoing - let basePayload = payload as BaseReduxActionPayload<{}> | undefined; - if (!basePayload?.broadcastReason && !basePayload?.messageDirection) { - // Re-wrap to indicate this is an incoming message. - basePayload = { data: payload, messageDirection: 'incoming' }; - } - if (AllowedMessages.find(k => k === message) && basePayload.messageDirection !== 'outgoing') { + if (isAllowedMessage(message)) { + const basePayload: BaseReduxActionPayload = { data: payload }; + if (message === InteractiveWindowMessages.Sync) { + // Unwrap the message. + message = payload.type; + basePayload.messageType = payload.payload.messageType; + basePayload.data = payload.payload.data; + } store.dispatch({ type: message, payload: basePayload }); } + return true; } }); diff --git a/src/datascience-ui/native-editor/nativeCell.tsx b/src/datascience-ui/native-editor/nativeCell.tsx index c3158de886b9..a3714fd09f93 100644 --- a/src/datascience-ui/native-editor/nativeCell.tsx +++ b/src/datascience-ui/native-editor/nativeCell.tsx @@ -65,7 +65,7 @@ export class NativeCell extends React.Component { } public componentDidUpdate(prevProps: INativeCellProps) { - if (this.props.cellVM.selected && !prevProps.cellVM.selected && !this.props.cellVM.focused) { + if (this.props.cellVM.selected && !prevProps.cellVM.selected && !this.props.cellVM.focused && !this.props.cellVM.shouldNotAutoFocus) { this.giveFocus(); } diff --git a/src/datascience-ui/native-editor/redux/actions.ts b/src/datascience-ui/native-editor/redux/actions.ts index 16a38da4af97..8619a0db3e50 100644 --- a/src/datascience-ui/native-editor/redux/actions.ts +++ b/src/datascience-ui/native-editor/redux/actions.ts @@ -6,11 +6,10 @@ import * as uuid from 'uuid/v4'; import { InteractiveWindowMessages, NativeCommandType } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { IJupyterVariable, IJupyterVariablesRequest } from '../../../client/datascience/types'; import { CursorPos } from '../../interactive-common/mainState'; +import { createIncomingAction, createIncomingActionWithPayload } from '../../interactive-common/redux/helpers'; import { CommonAction, CommonActionType, - createIncomingAction, - createIncomingActionWithPayload, IAddCellAction, ICellAction, ICellAndCursorAction, @@ -20,7 +19,6 @@ import { IEditCellAction, IExecuteAction, ILinkClickAction, - IRefreshVariablesAction, ISendCommandAction, IShowDataViewerAction } from '../../interactive-common/redux/reducers/types'; @@ -49,8 +47,6 @@ export const actionCreators = { executeAbove: (cellId: string): CommonAction => createIncomingActionWithPayload(CommonActionType.EXECUTE_ABOVE, { cellId }), executeCellAndBelow: (cellId: string, code: string): CommonAction => createIncomingActionWithPayload(CommonActionType.EXECUTE_CELL_AND_BELOW, { cellId, code }), toggleVariableExplorer: (): CommonAction => createIncomingAction(CommonActionType.TOGGLE_VARIABLE_EXPLORER), - refreshVariables: (newExecutionCount?: number): CommonAction => - createIncomingActionWithPayload(CommonActionType.REFRESH_VARIABLES, { newExecutionCount }), restartKernel: (): CommonAction => createIncomingAction(CommonActionType.RESTART_KERNEL), interruptKernel: (): CommonAction => createIncomingAction(CommonActionType.INTERRUPT_KERNEL), clearAllOutputs: (): CommonAction => createIncomingAction(InteractiveWindowMessages.ClearAllOutputs), diff --git a/src/datascience-ui/native-editor/redux/reducers/creation.ts b/src/datascience-ui/native-editor/redux/reducers/creation.ts index bb1bda4e125d..2aa41b26a987 100644 --- a/src/datascience-ui/native-editor/redux/reducers/creation.ts +++ b/src/datascience-ui/native-editor/redux/reducers/creation.ts @@ -5,7 +5,7 @@ import { ILoadAllCells, InteractiveWindowMessages } from '../../../../client/datascience/interactive-common/interactiveWindowTypes'; import { ICell, IDataScienceExtraSettings } from '../../../../client/datascience/types'; import { createCellVM, createEmptyCell, CursorPos, extractInputText, getSelectedAndFocusedInfo, ICellViewModel, IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { IAddCellAction, ICellAction } from '../../../interactive-common/redux/reducers/types'; import { actionCreators } from '../actions'; diff --git a/src/datascience-ui/native-editor/redux/reducers/effects.ts b/src/datascience-ui/native-editor/redux/reducers/effects.ts index 117945eb3deb..f23436ab53fa 100644 --- a/src/datascience-ui/native-editor/redux/reducers/effects.ts +++ b/src/datascience-ui/native-editor/redux/reducers/effects.ts @@ -4,7 +4,7 @@ import { CssMessages } from '../../../../client/datascience/messages'; import { IDataScienceExtraSettings } from '../../../../client/datascience/types'; import { getSelectedAndFocusedInfo, IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { ICellAction, ICellAndCursorAction, ICodeAction } from '../../../interactive-common/redux/reducers/types'; import { computeEditorOptions } from '../../../react-common/settingsReactSide'; @@ -39,7 +39,8 @@ export namespace Effects { // Add focus on new cell const addFocusIndex = newVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); if (addFocusIndex >= 0) { - newVMs[addFocusIndex] = { ...newVMs[addFocusIndex], focused: true, selected: true, cursorPos: arg.payload.data.cursorPos }; + const focus = typeof arg.payload.messageType !== 'number'; + newVMs[addFocusIndex] = { ...newVMs[addFocusIndex], focused: focus, selected: focus, cursorPos: arg.payload.data.cursorPos }; } return { @@ -51,6 +52,34 @@ export namespace Effects { return arg.prevState; } + export function unfocusAndUnSelectAll(arg: NativeEditorReducerArg): IMainState { + const newVMs = [...arg.prevState.cellVMs]; + newVMs.forEach((item, index) => { + if (item.selected || item.focused) { + const current = newVMs[index]; + const newCell = { + ...current, + inputBlockText: arg.payload.data.code, + focused: false, + selected: false, + cell: { + ...current.cell, + data: { + ...current.cell.data, + source: arg.payload.data.code + } + } + }; + // tslint:disable-next-line: no-any + newVMs[index] = Helpers.asCellViewModel(newCell); // This is because IMessageCell doesn't fit in here + } + }); + + return { + ...arg.prevState, + cellVMs: newVMs + }; + } export function unfocusCell(arg: NativeEditorReducerArg): IMainState { // Unfocus the cell const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); @@ -155,10 +184,12 @@ export namespace Effects { } const newVMs = [...prevState.cellVMs]; + // Ensure not to focus the editor if this is a sync message. + const focus = typeof arg.payload.messageType !== 'number'; if (addIndex >= 0 && arg.payload.data.cellId !== selectionInfo.selectedCellId) { newVMs[addIndex] = { ...newVMs[addIndex], - focused: typeof shouldFocusCell === 'boolean' ? shouldFocusCell : someOtherCellWasFocusedAndSelected, + focused: focus && (typeof shouldFocusCell === 'boolean' ? shouldFocusCell : someOtherCellWasFocusedAndSelected), selected: true, cursorPos: arg.payload.data.cursorPos }; diff --git a/src/datascience-ui/native-editor/redux/reducers/execution.ts b/src/datascience-ui/native-editor/redux/reducers/execution.ts index 129378e289f0..bfeb0ebbd176 100644 --- a/src/datascience-ui/native-editor/redux/reducers/execution.ts +++ b/src/datascience-ui/native-editor/redux/reducers/execution.ts @@ -9,7 +9,7 @@ import { CellState } from '../../../../client/datascience/types'; import { concatMultilineStringInput } from '../../../common'; import { createCellFrom } from '../../../common/cellFactory'; import { CursorPos, getSelectedAndFocusedInfo, ICellViewModel, IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { CommonActionType, ICellAction, IChangeCellTypeAction, ICodeAction, IExecuteAction } from '../../../interactive-common/redux/reducers/types'; import { QueueAnotherFunc } from '../../../react-common/reduxUtils'; diff --git a/src/datascience-ui/native-editor/redux/reducers/movement.ts b/src/datascience-ui/native-editor/redux/reducers/movement.ts index 8f0b19ec8ee9..b21e8a8ab1f9 100644 --- a/src/datascience-ui/native-editor/redux/reducers/movement.ts +++ b/src/datascience-ui/native-editor/redux/reducers/movement.ts @@ -3,7 +3,7 @@ 'use strict'; import { InteractiveWindowMessages } from '../../../../client/datascience/interactive-common/interactiveWindowTypes'; import { CursorPos, IMainState } from '../../../interactive-common/mainState'; -import { createPostableAction } from '../../../interactive-common/redux/postOffice'; +import { createPostableAction } from '../../../interactive-common/redux/helpers'; import { Helpers } from '../../../interactive-common/redux/reducers/helpers'; import { ICellAction, ICodeAction } from '../../../interactive-common/redux/reducers/types'; import { NativeEditorReducerArg } from '../mapping'; From 62d3334e463c032832f9ee44f1f94c590c95f470 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 3 Feb 2020 18:13:30 -0800 Subject: [PATCH 02/14] Revert --- .../native-editor/redux/reducers/effects.ts | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/src/datascience-ui/native-editor/redux/reducers/effects.ts b/src/datascience-ui/native-editor/redux/reducers/effects.ts index f23436ab53fa..8cd09d4670bb 100644 --- a/src/datascience-ui/native-editor/redux/reducers/effects.ts +++ b/src/datascience-ui/native-editor/redux/reducers/effects.ts @@ -52,34 +52,6 @@ export namespace Effects { return arg.prevState; } - export function unfocusAndUnSelectAll(arg: NativeEditorReducerArg): IMainState { - const newVMs = [...arg.prevState.cellVMs]; - newVMs.forEach((item, index) => { - if (item.selected || item.focused) { - const current = newVMs[index]; - const newCell = { - ...current, - inputBlockText: arg.payload.data.code, - focused: false, - selected: false, - cell: { - ...current.cell, - data: { - ...current.cell.data, - source: arg.payload.data.code - } - } - }; - // tslint:disable-next-line: no-any - newVMs[index] = Helpers.asCellViewModel(newCell); // This is because IMessageCell doesn't fit in here - } - }); - - return { - ...arg.prevState, - cellVMs: newVMs - }; - } export function unfocusCell(arg: NativeEditorReducerArg): IMainState { // Unfocus the cell const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); From 14e749de5562b588d8597747d2011b3d48b7ce0d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 3 Feb 2020 18:15:18 -0800 Subject: [PATCH 03/14] Fixes --- .../datascience/interactive-common/syncrhonization.ts | 6 +++--- .../interactive-common/redux/helpers.ts | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/client/datascience/interactive-common/syncrhonization.ts b/src/client/datascience/interactive-common/syncrhonization.ts index 4edd3312ce77..4a7313305982 100644 --- a/src/client/datascience/interactive-common/syncrhonization.ts +++ b/src/client/datascience/interactive-common/syncrhonization.ts @@ -64,9 +64,9 @@ const messageWithMessageTypes: MessageMapping & Messa [CommonActionType.SHOW_DATA_VIEWER]: MessageType.other, [CommonActionType.SUBMIT_INPUT]: MessageType.other, [CommonActionType.TOGGLE_INPUT_BLOCK]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [CommonActionType.TOGGLE_LINE_NUMBERS]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [CommonActionType.TOGGLE_OUTPUT]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [CommonActionType.TOGGLE_VARIABLE_EXPLORER]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [CommonActionType.TOGGLE_LINE_NUMBERS]: MessageType.syncWithLiveShare, + [CommonActionType.TOGGLE_OUTPUT]: MessageType.syncWithLiveShare, + [CommonActionType.TOGGLE_VARIABLE_EXPLORER]: MessageType.syncWithLiveShare, [CommonActionType.UNFOCUS_CELL]: MessageType.syncWithLiveShare, [CommonActionType.UNMOUNT]: MessageType.other, diff --git a/src/datascience-ui/interactive-common/redux/helpers.ts b/src/datascience-ui/interactive-common/redux/helpers.ts index 8dd0f6be2a4e..9b2fbaa032c2 100644 --- a/src/datascience-ui/interactive-common/redux/helpers.ts +++ b/src/datascience-ui/interactive-common/redux/helpers.ts @@ -30,12 +30,11 @@ export function createIncomingAction(type: CommonActionType | InteractiveWindowM // Actions created from messages export function createPostableAction(message: T, payload?: M[T]): Redux.AnyAction { const newPayload: BaseReduxActionPayload = ({ - data: payload, - messageDirection: 'outgoing' - // tslint:disable-next-line: no-any - } as any) as BaseReduxActionPayload; - return { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } }; - } + data: payload, + messageDirection: 'outgoing' + // tslint:disable-next-line: no-any + } as any) as BaseReduxActionPayload; + return { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } }; } type Dispatcher = (action: Redux.AnyAction) => Redux.AnyAction; From ae161e1a6da355ac35c51547fc77387cd50cbf85 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 09:01:20 -0800 Subject: [PATCH 04/14] Finally --- .../interactiveWindowMessageListener.ts | 2 +- .../interactive-common/syncrhonization.ts | 7 +++- .../interactive-common/redux/helpers.ts | 38 ++++++------------- .../interactive-common/redux/postOffice.ts | 28 +++++++------- 4 files changed, 31 insertions(+), 44 deletions(-) diff --git a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts index 34273681f72a..702fcedf3260 100644 --- a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts +++ b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts @@ -59,7 +59,7 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene } const cb = InteractiveWindowMessageListener.handlers.get(item); if (cb) { - cb(InteractiveWindowMessages.Sync, { type: message, payload: syncPayload.data }); + cb(InteractiveWindowMessages.Sync, { type: message, payload: syncPayload }); } }); return; diff --git a/src/client/datascience/interactive-common/syncrhonization.ts b/src/client/datascience/interactive-common/syncrhonization.ts index 4a7313305982..78b44f56b401 100644 --- a/src/client/datascience/interactive-common/syncrhonization.ts +++ b/src/client/datascience/interactive-common/syncrhonization.ts @@ -165,9 +165,12 @@ const messageWithMessageTypes: MessageMapping & Messa }; export function shouldRebroadcast(message: keyof IInteractiveWindowMapping): [boolean, MessageType] { - if (!messageWithMessageTypes[message]) { + const messageType: MessageType | undefined = messageWithMessageTypes[message]; + // Support for liveshare is turned off for now, we can enable that later. + // I.e. we only support synchronizing across editors in the same session. + if (messageType === undefined || (messageType & MessageType.syncAcrossSameNotebooks) !== MessageType.syncAcrossSameNotebooks) { return [false, MessageType.other]; } - const messageType: MessageType = messageWithMessageTypes[message]; + return [(messageType & MessageType.syncAcrossSameNotebooks) > 0 || (messageType & MessageType.syncWithLiveShare) > 0, messageType]; } diff --git a/src/datascience-ui/interactive-common/redux/helpers.ts b/src/datascience-ui/interactive-common/redux/helpers.ts index 9b2fbaa032c2..e3dd14105da0 100644 --- a/src/datascience-ui/interactive-common/redux/helpers.ts +++ b/src/datascience-ui/interactive-common/redux/helpers.ts @@ -36,38 +36,23 @@ export function createPostableAction; return { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } }; } +export function unwrapPostableAction(action: Redux.AnyAction): { type: keyof IInteractiveWindowMapping; payload?: BaseReduxActionPayload<{}> } { + // Unwrap the payload that was created in `createPostableAction`. + const type = action.type; + const payload: BaseReduxActionPayload<{}> | undefined = action.payload; + return { type, payload }; +} + -type Dispatcher = (action: Redux.AnyAction) => Redux.AnyAction; -/** - * Checks whether a message needs to be re-broadcasted. - */ export function reBroadcastMessageIfRequired( - storeDispatcher: Dispatcher, + dispatcher: Function, message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, payload?: BaseReduxActionPayload<{}> ) { - if (typeof payload?.messageType === 'number' || payload?.messageDirection === 'outgoing' || message === InteractiveWindowMessages.Sync) { + if (typeof payload?.messageType === 'number' || message === InteractiveWindowMessages.Sync) { return; } - // Check if we need to re-broadcast this message to other editors/sessions. - // tslint:disable-next-line: no-any - const result = shouldRebroadcast(message as any); - if (result[0]) { - // Mark message as incoming, to indicate this will be sent into the other webviews. - // tslint:disable-next-line: no-any - const syncPayloadData: BaseReduxActionPayload = { data: payload?.data, messageType: result[1], messageDirection: 'incoming' }; - // tslint:disable-next-line: no-any - const syncPayload = { type: message, payload: syncPayloadData } as any; - // Send this out. - storeDispatcher(createPostableAction(InteractiveWindowMessages.Sync, syncPayload)); - } -} -export function reBroadcastMessageIfRequiredX( - storeDispatcher: Function, - message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, - payload?: BaseReduxActionPayload<{}> -) { - if (typeof payload?.messageType === 'number' || payload?.messageDirection === 'outgoing' || message === InteractiveWindowMessages.Sync) { + if (payload?.messageDirection === 'outgoing') { return; } // Check if we need to re-broadcast this message to other editors/sessions. @@ -80,7 +65,6 @@ export function reBroadcastMessageIfRequiredX( // tslint:disable-next-line: no-any const syncPayload = { type: message, payload: syncPayloadData } as any; // Send this out. - const action = createPostableAction(InteractiveWindowMessages.Sync, syncPayload); - storeDispatcher(action.type, action.payload); + dispatcher(InteractiveWindowMessages.Sync, syncPayload); } } diff --git a/src/datascience-ui/interactive-common/redux/postOffice.ts b/src/datascience-ui/interactive-common/redux/postOffice.ts index ce628a570eba..a7831b4eeb8e 100644 --- a/src/datascience-ui/interactive-common/redux/postOffice.ts +++ b/src/datascience-ui/interactive-common/redux/postOffice.ts @@ -6,27 +6,27 @@ import * as Redux from 'redux'; import { IInteractiveWindowMapping } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types'; import { PostOffice } from '../../react-common/postOffice'; -import { isAllowedAction, reBroadcastMessageIfRequiredX } from './helpers'; +import { isAllowedAction, reBroadcastMessageIfRequired, unwrapPostableAction } from './helpers'; import { CommonActionType } from './reducers/types'; export function generatePostOfficeSendReducer(postOffice: PostOffice): Redux.Reducer<{}, Redux.AnyAction> { // tslint:disable-next-line: no-function-expression return function(_state: {} | undefined, action: Redux.AnyAction): {} { - // Make sure a valid message - if (action.type === CommonActionType.PostOutgoingMessage) { - // Unwrap the payload that was created in `createPostableAction`. - const type = action.payload.type; - const payload: BaseReduxActionPayload<{}> | undefined = action.payload.payload; - if (isAllowedAction(type)) { - if (payload?.messageDirection === 'outgoing') { - // Just post this to the post office. - // tslint:disable-next-line: no-any - postOffice.sendMessage(action.type, payload.data as any); - } - + if (isAllowedAction(action)) { + // Make sure a valid message + if (action.type === CommonActionType.PostOutgoingMessage) { + const { type, payload } = unwrapPostableAction(action.payload); + // Just post this to the post office. + // tslint:disable-next-line: no-any + postOffice.sendMessage(type, payload?.data as any); + } else { + const payload: BaseReduxActionPayload<{}> | undefined = action.payload; + // Do not rebroadcast messages that have been sent through as part of a synchronization packet. + // If `messageType` is a number, then its some part of a synchronization packet. if (payload?.messageDirection === 'incoming' && typeof payload?.messageType !== 'number') { + // We can delay this, first focus on UX perf. setTimeout(() => { - reBroadcastMessageIfRequiredX(postOffice.sendMessage.bind(postOffice), action.type, action?.payload); + reBroadcastMessageIfRequired(postOffice.sendMessage.bind(postOffice), action.type, action?.payload); }, 1); } } From 174138439ff8394f2e3e7c65818ea4ee0bc8efe4 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 09:07:14 -0800 Subject: [PATCH 05/14] Misc revert --- .vscode/launch.json | 2 +- .../interactiveWindowMessageListener.ts | 11 ++++---- .../interactive-common/redux/helpers.ts | 27 ++++++++++--------- .../interactive-common/redux/store.ts | 14 ---------- 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 205ee46335a3..e972d37c7bad 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,7 +18,7 @@ "outFiles": [ "${workspaceFolder}/out/**/*" ], - // "preLaunchTask": "Compile", + "preLaunchTask": "Compile", "skipFiles": [ "/**" ], diff --git a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts index 702fcedf3260..0fb14aff0d48 100644 --- a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts +++ b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts @@ -52,15 +52,16 @@ export class InteractiveWindowMessageListener implements IWebPanelMessageListene public onMessage(message: string, payload: any) { if (message === InteractiveWindowMessages.Sync) { - const syncPayload = payload as BaseReduxActionPayload; + // const syncPayload = payload as BaseReduxActionPayload; Array.from(InteractiveWindowMessageListener.handlers.keys()).forEach(item => { if (item === this) { return; } - const cb = InteractiveWindowMessageListener.handlers.get(item); - if (cb) { - cb(InteractiveWindowMessages.Sync, { type: message, payload: syncPayload }); - } + // Temporarily disabled. + // const cb = InteractiveWindowMessageListener.handlers.get(item); + // if (cb) { + // cb(InteractiveWindowMessages.Sync, { type: message, payload: syncPayload }); + // } }); return; } diff --git a/src/datascience-ui/interactive-common/redux/helpers.ts b/src/datascience-ui/interactive-common/redux/helpers.ts index e3dd14105da0..53881e643e52 100644 --- a/src/datascience-ui/interactive-common/redux/helpers.ts +++ b/src/datascience-ui/interactive-common/redux/helpers.ts @@ -45,7 +45,7 @@ export function unwrapPostableAction(action: Redux.AnyAction): { type: keyof IIn export function reBroadcastMessageIfRequired( - dispatcher: Function, + _dispatcher: Function, message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, payload?: BaseReduxActionPayload<{}> ) { @@ -55,16 +55,17 @@ export function reBroadcastMessageIfRequired( if (payload?.messageDirection === 'outgoing') { return; } - // Check if we need to re-broadcast this message to other editors/sessions. - // tslint:disable-next-line: no-any - const result = shouldRebroadcast(message as any); - if (result[0]) { - // Mark message as incoming, to indicate this will be sent into the other webviews. - // tslint:disable-next-line: no-any - const syncPayloadData: BaseReduxActionPayload = { data: payload?.data, messageType: result[1], messageDirection: 'incoming' }; - // tslint:disable-next-line: no-any - const syncPayload = { type: message, payload: syncPayloadData } as any; - // Send this out. - dispatcher(InteractiveWindowMessages.Sync, syncPayload); - } + // Temporarily disabled. + // // Check if we need to re-broadcast this message to other editors/sessions. + // // tslint:disable-next-line: no-any + // const result = shouldRebroadcast(message as any); + // if (result[0]) { + // // Mark message as incoming, to indicate this will be sent into the other webviews. + // // tslint:disable-next-line: no-any + // const syncPayloadData: BaseReduxActionPayload = { data: payload?.data, messageType: result[1], messageDirection: 'incoming' }; + // // tslint:disable-next-line: no-any + // const syncPayload = { type: message, payload: syncPayloadData } as any; + // // Send this out. + // dispatcher(InteractiveWindowMessages.Sync, syncPayload); + // } } diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index d0eab11f2bb3..1445b070d951 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -252,19 +252,6 @@ const addMessageDirectionMiddleware: Redux.Middleware = _store => next => (actio return next(action); }; -// /** -// * Middleware that will take messages dispatched to reducers, and re-broadcast them if necessary. -// */ -// // tslint:disable-next-line: no-any -// const reBroadcasterMiddleware: Redux.Middleware = store => next => (action: Redux.AnyAction) => { -// // First let the message be processed locally before we can think of passing this onto other editors. -// const result = next(action); - -// if (isAllowedAction(action) && action?.payload?.messageDirection === 'incoming') { -// reBroadcastMessageIfRequired(store.dispatch, action.type, action?.payload); -// } -// return result; -// }; export function createStore(skipDefault: boolean, baseTheme: string, testMode: boolean, editable: boolean, reducerMap: M) { // Create a post office to listen to store dispatches and allow reducers to @@ -293,7 +280,6 @@ export function createStore(skipDefault: boolean, baseTheme: string, testMode // Create our middleware const middleware = createMiddleWare(testMode).concat([addMessageDirectionMiddleware]); - // const middleware = createMiddleWare(testMode).concat([addMessageDirectionMiddleware, reBroadcasterMiddleware]); // Use this reducer and middle ware to create a store const store = Redux.createStore(rootReducer, Redux.applyMiddleware(...middleware)); From 7c92cdb01f9fc6109237dd81f2226eb687e94cae Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 09:16:10 -0800 Subject: [PATCH 06/14] Fix linter --- .../interactiveWindowMessageListener.ts | 1 - src/datascience-ui/history-react/interactiveCell.tsx | 9 +-------- src/datascience-ui/interactive-common/redux/helpers.ts | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts index 0fb14aff0d48..2dfe92e76597 100644 --- a/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts +++ b/src/client/datascience/interactive-common/interactiveWindowMessageListener.ts @@ -10,7 +10,6 @@ import { ILiveShareApi, IWebPanel, IWebPanelMessageListener } from '../../common import { Identifiers, LiveShare } from '../constants'; import { PostOffice } from '../liveshare/postOffice'; import { InteractiveWindowMessages, InteractiveWindowRemoteMessages } from './interactiveWindowTypes'; -import { BaseReduxActionPayload } from './types'; // tslint:disable:no-any diff --git a/src/datascience-ui/history-react/interactiveCell.tsx b/src/datascience-ui/history-react/interactiveCell.tsx index 4d5c9f0cc9a5..ac429959f6b5 100644 --- a/src/datascience-ui/history-react/interactiveCell.tsx +++ b/src/datascience-ui/history-react/interactiveCell.tsx @@ -132,14 +132,7 @@ export class InteractiveCell extends React.Component { // Only render if we are allowed to. if (shouldRender) { return ( -
+
{this.renderControls()}
diff --git a/src/datascience-ui/interactive-common/redux/helpers.ts b/src/datascience-ui/interactive-common/redux/helpers.ts index 53881e643e52..685b3c251d96 100644 --- a/src/datascience-ui/interactive-common/redux/helpers.ts +++ b/src/datascience-ui/interactive-common/redux/helpers.ts @@ -5,7 +5,6 @@ import * as Redux from 'redux'; import { IInteractiveWindowMapping, InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes'; -import { shouldRebroadcast } from '../../../client/datascience/interactive-common/syncrhonization'; import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types'; import { CssMessages, SharedMessages } from '../../../client/datascience/messages'; import { CommonAction, CommonActionType } from './reducers/types'; @@ -43,7 +42,6 @@ export function unwrapPostableAction(action: Redux.AnyAction): { type: keyof IIn return { type, payload }; } - export function reBroadcastMessageIfRequired( _dispatcher: Function, message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, From daf81efc7afebf6b5219cb07545cb12a3fcfa1dc Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 11:09:52 -0800 Subject: [PATCH 07/14] Address code review comments --- .../{syncrhonization.ts => synchronization.ts} | 0 src/client/datascience/interactive-common/types.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/client/datascience/interactive-common/{syncrhonization.ts => synchronization.ts} (100%) diff --git a/src/client/datascience/interactive-common/syncrhonization.ts b/src/client/datascience/interactive-common/synchronization.ts similarity index 100% rename from src/client/datascience/interactive-common/syncrhonization.ts rename to src/client/datascience/interactive-common/synchronization.ts diff --git a/src/client/datascience/interactive-common/types.ts b/src/client/datascience/interactive-common/types.ts index bc419e929d64..38b8a48e2cf7 100644 --- a/src/client/datascience/interactive-common/types.ts +++ b/src/client/datascience/interactive-common/types.ts @@ -3,7 +3,7 @@ 'use strict'; -import { MessageType } from './syncrhonization'; +import { MessageType } from './synchronization'; // Stuff common to React and Extensions. From b08aaf7c32687ece4fd0bc65579424b8e3f2ff72 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 11:20:53 -0800 Subject: [PATCH 08/14] Fixes --- src/client/datascience/interactive-common/synchronization.ts | 5 +++++ src/datascience-ui/native-editor/redux/reducers/effects.ts | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/client/datascience/interactive-common/synchronization.ts b/src/client/datascience/interactive-common/synchronization.ts index 78b44f56b401..54c0decb7007 100644 --- a/src/client/datascience/interactive-common/synchronization.ts +++ b/src/client/datascience/interactive-common/synchronization.ts @@ -1,6 +1,7 @@ import { CommonActionType, CommonActionTypeMapping } from '../../../datascience-ui/interactive-common/redux/reducers/types'; import { CssMessages, SharedMessages } from '../messages'; import { IInteractiveWindowMapping, InteractiveWindowMessages } from './interactiveWindowTypes'; +import { BaseReduxActionPayload } from './types'; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -164,6 +165,10 @@ const messageWithMessageTypes: MessageMapping & Messa [SharedMessages.UpdateSettings]: MessageType.other }; +export function isActionPerformedByUser(action: BaseReduxActionPayload<{}> | BaseReduxActionPayload) { + return action.messageType === undefined; +} + export function shouldRebroadcast(message: keyof IInteractiveWindowMapping): [boolean, MessageType] { const messageType: MessageType | undefined = messageWithMessageTypes[message]; // Support for liveshare is turned off for now, we can enable that later. diff --git a/src/datascience-ui/native-editor/redux/reducers/effects.ts b/src/datascience-ui/native-editor/redux/reducers/effects.ts index 8cd09d4670bb..8f3e565cb7b1 100644 --- a/src/datascience-ui/native-editor/redux/reducers/effects.ts +++ b/src/datascience-ui/native-editor/redux/reducers/effects.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. 'use strict'; +import { isActionPerformedByUser } from '../../../../client/datascience/interactive-common/synchronization'; import { CssMessages } from '../../../../client/datascience/messages'; import { IDataScienceExtraSettings } from '../../../../client/datascience/types'; import { getSelectedAndFocusedInfo, IMainState } from '../../../interactive-common/mainState'; @@ -39,7 +40,7 @@ export namespace Effects { // Add focus on new cell const addFocusIndex = newVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); if (addFocusIndex >= 0) { - const focus = typeof arg.payload.messageType !== 'number'; + const focus = isActionPerformedByUser(arg.payload); newVMs[addFocusIndex] = { ...newVMs[addFocusIndex], focused: focus, selected: focus, cursorPos: arg.payload.data.cursorPos }; } @@ -157,7 +158,7 @@ export namespace Effects { const newVMs = [...prevState.cellVMs]; // Ensure not to focus the editor if this is a sync message. - const focus = typeof arg.payload.messageType !== 'number'; + const focus = isActionPerformedByUser(arg.payload); if (addIndex >= 0 && arg.payload.data.cellId !== selectionInfo.selectedCellId) { newVMs[addIndex] = { ...newVMs[addIndex], From b0e1e65ce2864a2b3fa8accc2ab4b91aa90e28a1 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 12:11:26 -0800 Subject: [PATCH 09/14] Fix typo --- .../datascience/interactive-common/interactiveWindowTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/datascience/interactive-common/interactiveWindowTypes.ts b/src/client/datascience/interactive-common/interactiveWindowTypes.ts index 10e70f2a2ee7..d5772c5af964 100644 --- a/src/client/datascience/interactive-common/interactiveWindowTypes.ts +++ b/src/client/datascience/interactive-common/interactiveWindowTypes.ts @@ -59,7 +59,7 @@ export enum InteractiveWindowMessages { EditCell = 'edit_cell', RemoveCell = 'remove_cell', SwapCells = 'swap_cells', - Sync = 'sync_message_used_to_broadcasst_and_sync_editors', + Sync = 'sync_message_used_to_broadcast_and_sync_editors', InsertCell = 'insert_cell', LoadOnigasmAssemblyRequest = 'load_onigasm_assembly_request', LoadOnigasmAssemblyResponse = 'load_onigasm_assembly_response', From d88e0dd215f7084f066f071e5fc05de8e44ac013 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 12:30:12 -0800 Subject: [PATCH 10/14] Fix tests --- src/datascience-ui/native-editor/nativeCell.tsx | 2 +- src/datascience-ui/native-editor/redux/reducers/effects.ts | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/datascience-ui/native-editor/nativeCell.tsx b/src/datascience-ui/native-editor/nativeCell.tsx index a3714fd09f93..c3158de886b9 100644 --- a/src/datascience-ui/native-editor/nativeCell.tsx +++ b/src/datascience-ui/native-editor/nativeCell.tsx @@ -65,7 +65,7 @@ export class NativeCell extends React.Component { } public componentDidUpdate(prevProps: INativeCellProps) { - if (this.props.cellVM.selected && !prevProps.cellVM.selected && !this.props.cellVM.focused && !this.props.cellVM.shouldNotAutoFocus) { + if (this.props.cellVM.selected && !prevProps.cellVM.selected && !this.props.cellVM.focused) { this.giveFocus(); } diff --git a/src/datascience-ui/native-editor/redux/reducers/effects.ts b/src/datascience-ui/native-editor/redux/reducers/effects.ts index 8f3e565cb7b1..025631838b91 100644 --- a/src/datascience-ui/native-editor/redux/reducers/effects.ts +++ b/src/datascience-ui/native-editor/redux/reducers/effects.ts @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. 'use strict'; -import { isActionPerformedByUser } from '../../../../client/datascience/interactive-common/synchronization'; import { CssMessages } from '../../../../client/datascience/messages'; import { IDataScienceExtraSettings } from '../../../../client/datascience/types'; import { getSelectedAndFocusedInfo, IMainState } from '../../../interactive-common/mainState'; @@ -40,8 +39,7 @@ export namespace Effects { // Add focus on new cell const addFocusIndex = newVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); if (addFocusIndex >= 0) { - const focus = isActionPerformedByUser(arg.payload); - newVMs[addFocusIndex] = { ...newVMs[addFocusIndex], focused: focus, selected: focus, cursorPos: arg.payload.data.cursorPos }; + newVMs[addFocusIndex] = { ...newVMs[addFocusIndex], focused: true, selected: true, cursorPos: arg.payload.data.cursorPos }; } return { @@ -158,11 +156,10 @@ export namespace Effects { const newVMs = [...prevState.cellVMs]; // Ensure not to focus the editor if this is a sync message. - const focus = isActionPerformedByUser(arg.payload); if (addIndex >= 0 && arg.payload.data.cellId !== selectionInfo.selectedCellId) { newVMs[addIndex] = { ...newVMs[addIndex], - focused: focus && (typeof shouldFocusCell === 'boolean' ? shouldFocusCell : someOtherCellWasFocusedAndSelected), + focused: typeof shouldFocusCell === 'boolean' ? shouldFocusCell : someOtherCellWasFocusedAndSelected, selected: true, cursorPos: arg.payload.data.cursorPos }; From 068d72b24fccd2868bda2cd3f8b56f20643cf9b7 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Feb 2020 14:41:41 -0800 Subject: [PATCH 11/14] Fix test --- src/datascience-ui/interactive-common/redux/store.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index 1445b070d951..0451e8faba00 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -69,14 +69,15 @@ function generateMainReducer(skipDefault: boolean, testMode: boolean, baseThe function createSendInfoMiddleware(): Redux.Middleware<{}, IStore> { return store => next => action => { + const prevState = store.getState(); const res = next(action); + const afterState = store.getState(); + // If the action is part of a sync message, then do not send it to the extension. if (action.payload && typeof (action.payload as BaseReduxActionPayload).messageType === 'number') { return res; } - const prevState = store.getState(); - const afterState = store.getState(); // If cell vm count changed or selected cell changed, send the message const currentSelection = getSelectedAndFocusedInfo(afterState.main); From abfcd364efe6e0b9c268c5ff922b174ccc210d5c Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 5 Feb 2020 08:37:06 -0800 Subject: [PATCH 12/14] Fix linter --- src/datascience-ui/interactive-common/redux/store.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index 0451e8faba00..e00aaefde666 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -78,7 +78,6 @@ function createSendInfoMiddleware(): Redux.Middleware<{}, IStore> { return res; } - // If cell vm count changed or selected cell changed, send the message const currentSelection = getSelectedAndFocusedInfo(afterState.main); if ( From cec8b3ebe4f719bd5b8482c0b13bbb976b52b58d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 5 Feb 2020 09:05:50 -0800 Subject: [PATCH 13/14] Remove unused property --- src/datascience-ui/interactive-common/mainState.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/datascience-ui/interactive-common/mainState.ts b/src/datascience-ui/interactive-common/mainState.ts index e9f96d13b446..0b85b8580a73 100644 --- a/src/datascience-ui/interactive-common/mainState.ts +++ b/src/datascience-ui/interactive-common/mainState.ts @@ -34,7 +34,6 @@ export interface ICellViewModel { useQuickEdit?: boolean; selected: boolean; focused: boolean; - shouldNotAutoFocus?: boolean; scrollCount: number; cursorPos: CursorPos; hasBeenRun: boolean; From d83dd706b97ab0c73b43e4b070faedd3ef08b23d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 5 Feb 2020 09:07:34 -0800 Subject: [PATCH 14/14] More changes --- src/datascience-ui/interactive-common/mainState.ts | 1 + src/datascience-ui/native-editor/redux/reducers/effects.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datascience-ui/interactive-common/mainState.ts b/src/datascience-ui/interactive-common/mainState.ts index 0b85b8580a73..5e2e865e2d65 100644 --- a/src/datascience-ui/interactive-common/mainState.ts +++ b/src/datascience-ui/interactive-common/mainState.ts @@ -95,6 +95,7 @@ export function getSelectedAndFocusedInfo(state: IMainState) { return info; } + export interface IFont { size: number; family: string; diff --git a/src/datascience-ui/native-editor/redux/reducers/effects.ts b/src/datascience-ui/native-editor/redux/reducers/effects.ts index 025631838b91..6bfa022ef4c8 100644 --- a/src/datascience-ui/native-editor/redux/reducers/effects.ts +++ b/src/datascience-ui/native-editor/redux/reducers/effects.ts @@ -155,7 +155,6 @@ export namespace Effects { } const newVMs = [...prevState.cellVMs]; - // Ensure not to focus the editor if this is a sync message. if (addIndex >= 0 && arg.payload.data.cellId !== selectionInfo.selectedCellId) { newVMs[addIndex] = { ...newVMs[addIndex],