diff --git a/src/client/datascience/interactive-common/synchronization.ts b/src/client/datascience/interactive-common/synchronization.ts index 03c4741f443b..285973753e19 100644 --- a/src/client/datascience/interactive-common/synchronization.ts +++ b/src/client/datascience/interactive-common/synchronization.ts @@ -12,7 +12,7 @@ export enum MessageType { /** * Action dispatched as result of some user action. */ - userAction = 0, + other = 0, /** * Action dispatched to re-broadcast a message across other editors of the same file in the same session. */ @@ -35,147 +35,147 @@ export type IInteractiveActionMapping = MessageMapping & MessageMapping = { - [CommonActionType.ADD_AND_FOCUS_NEW_CELL]: MessageType.userAction, + [CommonActionType.ADD_AND_FOCUS_NEW_CELL]: MessageType.other, [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.DELETE_CELL]: MessageType.syncWithLiveShare, + [CommonActionType.DELETE_CELL]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, [CommonActionType.CODE_CREATED]: MessageType.noIdea, - [CommonActionType.COPY_CELL_CODE]: MessageType.userAction, - [CommonActionType.EDITOR_LOADED]: MessageType.userAction, + [CommonActionType.COPY_CELL_CODE]: MessageType.other, + [CommonActionType.EDITOR_LOADED]: MessageType.other, [CommonActionType.EDIT_CELL]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [CommonActionType.EXECUTE_CELL_AND_ADVANCE]: MessageType.userAction, - [CommonActionType.EXECUTE_ABOVE]: MessageType.userAction, - [CommonActionType.EXECUTE_ALL_CELLS]: MessageType.userAction, - [CommonActionType.EXECUTE_CELL]: MessageType.userAction, - [CommonActionType.EXECUTE_CELL_AND_BELOW]: MessageType.userAction, - [CommonActionType.EXPORT]: MessageType.userAction, + [CommonActionType.EXECUTE_CELL_AND_ADVANCE]: MessageType.other, + [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.userAction, - [CommonActionType.GET_VARIABLE_DATA]: MessageType.userAction, + [CommonActionType.GATHER_CELL]: MessageType.other, + [CommonActionType.GET_VARIABLE_DATA]: MessageType.other, [CommonActionType.GOTO_CELL]: MessageType.syncWithLiveShare, - [CommonActionType.INSERT_ABOVE_AND_FOCUS_NEW_CELL]: MessageType.userAction, + [CommonActionType.INSERT_ABOVE_AND_FOCUS_NEW_CELL]: MessageType.other, [CommonActionType.INSERT_ABOVE]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [CommonActionType.INSERT_ABOVE_FIRST_AND_FOCUS_NEW_CELL]: MessageType.userAction, + [CommonActionType.INSERT_ABOVE_FIRST_AND_FOCUS_NEW_CELL]: MessageType.other, [CommonActionType.INSERT_ABOVE_FIRST]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, [CommonActionType.INSERT_BELOW]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [CommonActionType.INSERT_BELOW_AND_FOCUS_NEW_CELL]: MessageType.userAction, - [CommonActionType.INTERRUPT_KERNEL]: MessageType.userAction, - [CommonActionType.LOADED_ALL_CELLS]: MessageType.userAction, - [CommonActionType.LINK_CLICK]: MessageType.userAction, + [CommonActionType.INSERT_BELOW_AND_FOCUS_NEW_CELL]: MessageType.other, + [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.OPEN_SETTINGS]: MessageType.userAction, - [CommonActionType.RESTART_KERNEL]: MessageType.userAction, - [CommonActionType.SAVE]: MessageType.userAction, + [CommonActionType.OPEN_SETTINGS]: MessageType.other, + [CommonActionType.RESTART_KERNEL]: MessageType.other, + [CommonActionType.SAVE]: MessageType.other, [CommonActionType.SCROLL]: MessageType.syncWithLiveShare, [CommonActionType.SELECT_CELL]: MessageType.syncWithLiveShare, - [CommonActionType.SELECT_SERVER]: MessageType.userAction, - [CommonActionType.SEND_COMMAND]: MessageType.userAction, - [CommonActionType.SHOW_DATA_VIEWER]: MessageType.userAction, - [CommonActionType.SUBMIT_INPUT]: MessageType.userAction, + [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.syncWithLiveShare, [CommonActionType.TOGGLE_OUTPUT]: MessageType.syncWithLiveShare, [CommonActionType.TOGGLE_VARIABLE_EXPLORER]: MessageType.syncWithLiveShare, [CommonActionType.UNFOCUS_CELL]: MessageType.syncWithLiveShare, - [CommonActionType.UNMOUNT]: MessageType.userAction, - [CommonActionType.PostOutgoingMessage]: MessageType.userAction, - [CommonActionType.REFRESH_VARIABLES]: MessageType.userAction, - [CommonActionType.FOCUS_INPUT]: MessageType.userAction, + [CommonActionType.UNMOUNT]: MessageType.other, + [CommonActionType.PostOutgoingMessage]: MessageType.other, + [CommonActionType.REFRESH_VARIABLES]: MessageType.other, + [CommonActionType.FOCUS_INPUT]: MessageType.other, // Types from InteractiveWindowMessages - [InteractiveWindowMessages.Activate]: MessageType.userAction, - [InteractiveWindowMessages.AddedSysInfo]: MessageType.userAction, - [InteractiveWindowMessages.CancelCompletionItemsRequest]: MessageType.userAction, - [InteractiveWindowMessages.CancelHoverRequest]: MessageType.userAction, - [InteractiveWindowMessages.CancelResolveCompletionItemRequest]: MessageType.userAction, - [InteractiveWindowMessages.CancelSignatureHelpRequest]: MessageType.userAction, + [InteractiveWindowMessages.Activate]: MessageType.other, + [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.userAction, + [InteractiveWindowMessages.CopyCodeCell]: MessageType.other, [InteractiveWindowMessages.DeleteAllCells]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [InteractiveWindowMessages.DoSave]: MessageType.userAction, - [InteractiveWindowMessages.ExecutionRendered]: MessageType.userAction, + [InteractiveWindowMessages.DoSave]: MessageType.other, + [InteractiveWindowMessages.ExecutionRendered]: MessageType.other, [InteractiveWindowMessages.ExpandAll]: MessageType.syncWithLiveShare, - [InteractiveWindowMessages.Export]: MessageType.userAction, - [InteractiveWindowMessages.FinishCell]: MessageType.userAction, + [InteractiveWindowMessages.Export]: MessageType.other, + [InteractiveWindowMessages.FinishCell]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, [InteractiveWindowMessages.FocusedCellEditor]: MessageType.syncWithLiveShare, - [InteractiveWindowMessages.GatherCodeRequest]: MessageType.userAction, - [InteractiveWindowMessages.GetAllCells]: MessageType.userAction, - [InteractiveWindowMessages.GetVariablesRequest]: MessageType.userAction, - [InteractiveWindowMessages.GetVariablesResponse]: MessageType.userAction, + [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.Interrupt]: MessageType.userAction, - [InteractiveWindowMessages.LoadAllCells]: MessageType.userAction, - [InteractiveWindowMessages.LoadAllCellsComplete]: MessageType.userAction, - [InteractiveWindowMessages.LoadOnigasmAssemblyRequest]: MessageType.userAction, - [InteractiveWindowMessages.LoadOnigasmAssemblyResponse]: MessageType.userAction, - [InteractiveWindowMessages.LoadTmLanguageRequest]: MessageType.userAction, - [InteractiveWindowMessages.LoadTmLanguageResponse]: MessageType.userAction, - [InteractiveWindowMessages.MonacoReady]: MessageType.userAction, - [InteractiveWindowMessages.NativeCommand]: MessageType.userAction, + [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.userAction, - [InteractiveWindowMessages.NotebookDirty]: MessageType.userAction, - [InteractiveWindowMessages.NotebookExecutionActivated]: MessageType.userAction, - [InteractiveWindowMessages.NotebookIdentity]: MessageType.userAction, - [InteractiveWindowMessages.NotebookRunAllCells]: MessageType.userAction, - [InteractiveWindowMessages.NotebookRunSelectedCell]: MessageType.userAction, - [InteractiveWindowMessages.OpenLink]: MessageType.userAction, - [InteractiveWindowMessages.OpenSettings]: MessageType.userAction, - [InteractiveWindowMessages.ProvideCompletionItemsRequest]: MessageType.userAction, - [InteractiveWindowMessages.ProvideCompletionItemsResponse]: MessageType.userAction, - [InteractiveWindowMessages.ProvideHoverRequest]: MessageType.userAction, - [InteractiveWindowMessages.ProvideHoverResponse]: MessageType.userAction, - [InteractiveWindowMessages.ProvideSignatureHelpRequest]: MessageType.userAction, - [InteractiveWindowMessages.ProvideSignatureHelpResponse]: MessageType.userAction, - [InteractiveWindowMessages.ReExecuteCells]: MessageType.userAction, - [InteractiveWindowMessages.Redo]: MessageType.userAction, - [InteractiveWindowMessages.RemoteAddCode]: MessageType.userAction, - [InteractiveWindowMessages.ReceivedUpdateModel]: MessageType.userAction, - [InteractiveWindowMessages.RemoteReexecuteCode]: MessageType.userAction, - [InteractiveWindowMessages.ResolveCompletionItemRequest]: MessageType.userAction, - [InteractiveWindowMessages.ResolveCompletionItemResponse]: MessageType.userAction, - [InteractiveWindowMessages.RestartKernel]: MessageType.userAction, - [InteractiveWindowMessages.ReturnAllCells]: MessageType.userAction, - [InteractiveWindowMessages.SaveAll]: MessageType.userAction, - [InteractiveWindowMessages.SavePng]: MessageType.userAction, + [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.OpenSettings]: 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.ReExecuteCells]: MessageType.other, + [InteractiveWindowMessages.Redo]: MessageType.other, + [InteractiveWindowMessages.RemoteAddCode]: MessageType.other, + [InteractiveWindowMessages.ReceivedUpdateModel]: MessageType.other, + [InteractiveWindowMessages.RemoteReexecuteCode]: MessageType.other, + [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.userAction, - [InteractiveWindowMessages.SelectKernel]: MessageType.userAction, - [InteractiveWindowMessages.SendInfo]: MessageType.userAction, - [InteractiveWindowMessages.SettingsUpdated]: MessageType.userAction, - [InteractiveWindowMessages.ShowDataViewer]: MessageType.userAction, - [InteractiveWindowMessages.ShowPlot]: MessageType.userAction, - [InteractiveWindowMessages.StartCell]: MessageType.userAction, - [InteractiveWindowMessages.StartDebugging]: MessageType.userAction, - [InteractiveWindowMessages.StartProgress]: MessageType.userAction, - [InteractiveWindowMessages.Started]: MessageType.userAction, - [InteractiveWindowMessages.StopDebugging]: MessageType.userAction, - [InteractiveWindowMessages.StopProgress]: MessageType.userAction, - [InteractiveWindowMessages.SubmitNewCell]: MessageType.userAction, - [InteractiveWindowMessages.Sync]: MessageType.userAction, - [InteractiveWindowMessages.Undo]: MessageType.userAction, + [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.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [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.Sync]: MessageType.other, + [InteractiveWindowMessages.Undo]: MessageType.other, [InteractiveWindowMessages.UnfocusedCellEditor]: MessageType.syncWithLiveShare, - [InteractiveWindowMessages.UpdateCell]: MessageType.userAction, + [InteractiveWindowMessages.UpdateCell]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, [InteractiveWindowMessages.UpdateModel]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, - [InteractiveWindowMessages.UpdateKernel]: MessageType.userAction, - [InteractiveWindowMessages.VariableExplorerToggle]: MessageType.userAction, - [InteractiveWindowMessages.VariablesComplete]: MessageType.userAction, + [InteractiveWindowMessages.UpdateKernel]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare, + [InteractiveWindowMessages.VariableExplorerToggle]: MessageType.other, + [InteractiveWindowMessages.VariablesComplete]: MessageType.other, // Types from CssMessages - [CssMessages.GetCssRequest]: MessageType.userAction, - [CssMessages.GetCssResponse]: MessageType.userAction, - [CssMessages.GetMonacoThemeRequest]: MessageType.userAction, - [CssMessages.GetMonacoThemeResponse]: MessageType.userAction, + [CssMessages.GetCssRequest]: MessageType.other, + [CssMessages.GetCssResponse]: MessageType.other, + [CssMessages.GetMonacoThemeRequest]: MessageType.other, + [CssMessages.GetMonacoThemeResponse]: MessageType.other, // Types from Shared Messages - [SharedMessages.LocInit]: MessageType.userAction, - [SharedMessages.Started]: MessageType.userAction, - [SharedMessages.UpdateSettings]: MessageType.userAction + [SharedMessages.LocInit]: MessageType.other, + [SharedMessages.Started]: MessageType.other, + [SharedMessages.UpdateSettings]: MessageType.other }; /** @@ -209,7 +209,7 @@ export function shouldRebroadcast(message: keyof IInteractiveWindowMapping): [bo messageType === undefined || (messageType & MessageType.syncAcrossSameNotebooks) !== MessageType.syncAcrossSameNotebooks ) { - return [false, MessageType.userAction]; + return [false, MessageType.other]; } return [ diff --git a/src/client/datascience/interactive-common/types.ts b/src/client/datascience/interactive-common/types.ts index bab7a55d2e09..3c22935868a2 100644 --- a/src/client/datascience/interactive-common/types.ts +++ b/src/client/datascience/interactive-common/types.ts @@ -3,6 +3,9 @@ 'use strict'; +import { CommonActionType } from '../../../datascience-ui/interactive-common/redux/reducers/types'; +import { CssMessages, SharedMessages } from '../messages'; +import { InteractiveWindowMessages } from './interactiveWindowTypes'; import { MessageType } from './synchronization'; // Stuff common to React and Extensions. @@ -32,3 +35,8 @@ export type BaseReduxActionPayload = T extends never ? BaseData : BaseDataWithPayload : BaseDataWithPayload; +export type SyncPayload = { + type: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages; + // tslint:disable-next-line: no-any + payload: BaseReduxActionPayload; +}; diff --git a/src/client/datascience/interactive-ipynb/nativeEditor.ts b/src/client/datascience/interactive-ipynb/nativeEditor.ts index 125a4a385cd5..065bd6ea9068 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditor.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditor.ts @@ -83,6 +83,7 @@ import { IThemeFinder, WebViewViewChangeEventArgs } from '../types'; +import { NativeEditorSynchronizer } from './nativeEditorSynchronizer'; import { nbformat } from '@jupyterlab/coreutils'; // tslint:disable-next-line: no-require-imports @@ -164,6 +165,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { @inject(ICommandManager) commandManager: ICommandManager, @inject(INotebookExporter) jupyterExporter: INotebookExporter, @inject(IWorkspaceService) workspaceService: IWorkspaceService, + @inject(NativeEditorSynchronizer) private readonly synchronizer: NativeEditorSynchronizer, @inject(INotebookEditorProvider) private editorProvider: INotebookEditorProvider, @inject(IDataViewerProvider) dataExplorerProvider: IDataViewerProvider, @inject(IJupyterVariables) jupyterVariables: IJupyterVariables, @@ -211,6 +213,8 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { switcher ); asyncRegistry.push(this); + + this.synchronizer.subscribeToUserActions(this, this.postMessage.bind(this)); } public dispose(): Promise { @@ -247,6 +251,10 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { public onMessage(message: string, payload: any) { super.onMessage(message, payload); switch (message) { + case InteractiveWindowMessages.Sync: + this.synchronizer.notifyUserAction(payload, this); + break; + case InteractiveWindowMessages.ReExecuteCells: this.executedEvent.fire(this); break; diff --git a/src/client/datascience/interactive-ipynb/nativeEditorOldWebView.ts b/src/client/datascience/interactive-ipynb/nativeEditorOldWebView.ts index 88abc82cb188..b870fd65e294 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditorOldWebView.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditorOldWebView.ts @@ -50,6 +50,7 @@ import { } from '../types'; import { NativeEditor } from './nativeEditor'; import { NativeEditorStorage } from './nativeEditorStorage'; +import { NativeEditorSynchronizer } from './nativeEditorSynchronizer'; enum AskForSaveResult { Yes, @@ -85,6 +86,7 @@ export class NativeEditorOldWebView extends NativeEditor { @inject(ICommandManager) commandManager: ICommandManager, @inject(INotebookExporter) jupyterExporter: INotebookExporter, @inject(IWorkspaceService) workspaceService: IWorkspaceService, + @inject(NativeEditorSynchronizer) synchronizer: NativeEditorSynchronizer, @inject(INotebookEditorProvider) editorProvider: INotebookEditorProvider, @inject(IDataViewerProvider) dataExplorerProvider: IDataViewerProvider, @inject(IJupyterVariables) jupyterVariables: IJupyterVariables, @@ -114,6 +116,7 @@ export class NativeEditorOldWebView extends NativeEditor { commandManager, jupyterExporter, workspaceService, + synchronizer, editorProvider, dataExplorerProvider, jupyterVariables, @@ -127,6 +130,8 @@ export class NativeEditorOldWebView extends NativeEditor { switcher ); asyncRegistry.push(this); + // No ui syncing in old notebooks. + synchronizer.disable(); } public async load(model: INotebookModel, webViewPanel: WebviewPanel): Promise { await super.load(model, webViewPanel); diff --git a/src/client/datascience/interactive-ipynb/nativeEditorSynchronizer.ts b/src/client/datascience/interactive-ipynb/nativeEditorSynchronizer.ts new file mode 100644 index 000000000000..435d02f9163c --- /dev/null +++ b/src/client/datascience/interactive-ipynb/nativeEditorSynchronizer.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { IFileSystem } from '../../common/platform/types'; +import { IInteractiveWindowMapping, InteractiveWindowMessages } from '../interactive-common/interactiveWindowTypes'; +import { SyncPayload } from '../interactive-common/types'; +import { INotebookEditor } from '../types'; + +// tslint:disable: no-any + +type UserActionNotificationCallback = ( + type: T, + payload?: M[T] +) => void; + +@injectable() +export class NativeEditorSynchronizer { + private registeredNotebooks = new Map(); + private enabled = true; + constructor(@inject(IFileSystem) private readonly fs: IFileSystem) {} + public notifyUserAction(message: SyncPayload, editor: INotebookEditor) { + if (!this.enabled) { + return; + } + this.registeredNotebooks.forEach((cb, item) => { + if (item !== editor && this.fs.arePathsSame(item.file.fsPath, editor.file.fsPath)) { + cb(InteractiveWindowMessages.Sync, message as any); + } + }); + } + public subscribeToUserActions(editor: INotebookEditor, cb: UserActionNotificationCallback) { + this.registeredNotebooks.set(editor, cb); + } + public disable() { + this.enabled = false; + this.registeredNotebooks.clear(); + } +} diff --git a/src/client/datascience/serviceRegistry.ts b/src/client/datascience/serviceRegistry.ts index 0fcd8fb9f30c..eb8e120414ef 100644 --- a/src/client/datascience/serviceRegistry.ts +++ b/src/client/datascience/serviceRegistry.ts @@ -38,6 +38,7 @@ import { NativeEditorOldWebView } from './interactive-ipynb/nativeEditorOldWebVi import { NativeEditorProvider } from './interactive-ipynb/nativeEditorProvider'; import { NativeEditorProviderOld } from './interactive-ipynb/nativeEditorProviderOld'; import { NativeEditorStorage } from './interactive-ipynb/nativeEditorStorage'; +import { NativeEditorSynchronizer } from './interactive-ipynb/nativeEditorSynchronizer'; import { InteractiveWindow } from './interactive-window/interactiveWindow'; import { InteractiveWindowCommandListener } from './interactive-window/interactiveWindowCommandListener'; import { InteractiveWindowProvider } from './interactive-window/interactiveWindowProvider'; @@ -158,8 +159,6 @@ export function registerTypes(serviceManager: IServiceManager) { serviceManager.addSingleton(IExtensionSingleActivationService, JupyterInterpreterSelectionCommand); serviceManager.addSingleton(IExtensionSingleActivationService, PreWarmActivatedJupyterEnvironmentVariables); serviceManager.addSingleton(IExtensionSingleActivationService, ServerPreload); - serviceManager.addSingleton(IExtensionSingleActivationService, ServerPreload); - serviceManager.addSingleton(IExtensionSingleActivationService, ServerPreload); serviceManager.addSingleton(IInteractiveWindowListener, DataScienceSurveyBannerLogger); serviceManager.addSingleton(IInteractiveWindowProvider, InteractiveWindowProvider); serviceManager.addSingleton(IJupyterDebugger, JupyterDebugger, undefined, [ICellHashListener]); @@ -188,6 +187,7 @@ export function registerTypes(serviceManager: IServiceManager) { serviceManager.addSingleton(KernelSwitcherCommand, KernelSwitcherCommand); serviceManager.addSingleton(NotebookStarter, NotebookStarter); serviceManager.addSingleton(ProgressReporter, ProgressReporter); + serviceManager.addSingleton(NativeEditorSynchronizer, NativeEditorSynchronizer); // Temporary code, to allow users to revert to the old behavior. const cfg = serviceManager.get(IWorkspaceService).getConfiguration('python.dataScience', undefined); diff --git a/src/datascience-ui/interactive-common/redux/helpers.ts b/src/datascience-ui/interactive-common/redux/helpers.ts index ee880e687c2d..e68b91d0c2a9 100644 --- a/src/datascience-ui/interactive-common/redux/helpers.ts +++ b/src/datascience-ui/interactive-common/redux/helpers.ts @@ -13,7 +13,7 @@ import { MessageType, shouldRebroadcast } from '../../../client/datascience/interactive-common/synchronization'; -import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types'; +import { BaseReduxActionPayload, SyncPayload } from '../../../client/datascience/interactive-common/types'; import { CssMessages, SharedMessages } from '../../../client/datascience/messages'; import { QueueAnotherFunc } from '../../react-common/reduxUtils'; import { CommonActionType, CommonActionTypeMapping } from './reducers/types'; @@ -87,7 +87,7 @@ export function postActionToExtension(originalReducerArg: ReducerArg, message: a const newPayload: BaseReduxActionPayload = ({ data: payload, messageDirection: 'outgoing', - messageType: MessageType.userAction + messageType: MessageType.other // tslint:disable-next-line: no-any } as any) as BaseReduxActionPayload; const action = { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } }; @@ -102,15 +102,29 @@ export function unwrapPostableAction( return { type, payload }; } +/** + * Whether this is a message type that indicates it is part of a scynchronization message. + */ +export function isSyncingMessage(messageType?: MessageType) { + if (!messageType) { + return false; + } + + return ( + (messageType && MessageType.syncAcrossSameNotebooks) === MessageType.syncAcrossSameNotebooks || + (messageType && MessageType.syncWithLiveShare) === MessageType.syncWithLiveShare + ); +} export function reBroadcastMessageIfRequired( dispatcher: Function, message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages, payload?: BaseReduxActionPayload<{}> ) { + const messageType = payload?.messageType || 0; if ( message === InteractiveWindowMessages.Sync || - payload?.messageType === MessageType.syncAcrossSameNotebooks || - payload?.messageType === MessageType.syncWithLiveShare || + (messageType && MessageType.syncAcrossSameNotebooks) === MessageType.syncAcrossSameNotebooks || + (messageType && MessageType.syncWithLiveShare) === MessageType.syncWithLiveShare || payload?.messageDirection === 'outgoing' ) { return; @@ -127,8 +141,8 @@ export function reBroadcastMessageIfRequired( messageDirection: 'incoming' }; // tslint:disable-next-line: no-any - const syncPayload = { type: message, payload: syncPayloadData } as any; - // Send this out. - dispatcher(InteractiveWindowMessages.Sync, syncPayload); + const syncPayload: SyncPayload = { type: message, payload: syncPayloadData }; + // First focus on UX perf, hence the setTimeout (i.e. ensure other code in event loop executes). + setTimeout(() => dispatcher(InteractiveWindowMessages.Sync, syncPayload), 1); } } diff --git a/src/datascience-ui/interactive-common/redux/postOffice.ts b/src/datascience-ui/interactive-common/redux/postOffice.ts index 4ba3a4d25a2a..5f231921a4d2 100644 --- a/src/datascience-ui/interactive-common/redux/postOffice.ts +++ b/src/datascience-ui/interactive-common/redux/postOffice.ts @@ -24,14 +24,7 @@ export function generatePostOfficeSendReducer(postOffice: PostOffice): Redux.Red // 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') { - // We can delay this, first focus on UX perf. - setTimeout(() => { - reBroadcastMessageIfRequired( - postOffice.sendMessage.bind(postOffice), - action.type, - action?.payload - ); - }, 1); + reBroadcastMessageIfRequired(postOffice.sendMessage.bind(postOffice), action.type, action?.payload); } } } diff --git a/src/datascience-ui/interactive-common/redux/reducers/transfer.ts b/src/datascience-ui/interactive-common/redux/reducers/transfer.ts index 02894f56296b..f39d35bac67c 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/transfer.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/transfer.ts @@ -10,7 +10,7 @@ import { import { CssMessages } from '../../../../client/datascience/messages'; import { ICell } from '../../../../client/datascience/types'; import { extractInputText, getSelectedAndFocusedInfo, IMainState } from '../../mainState'; -import { postActionToExtension } from '../helpers'; +import { isSyncingMessage, postActionToExtension } from '../helpers'; import { Helpers } from './helpers'; import { CommonActionType, @@ -231,7 +231,11 @@ export namespace Transfer { // when focus is lost const index = arg.prevState.cellVMs.findIndex(c => c.cell.id === arg.payload.data.cellId); const selectionInfo = getSelectedAndFocusedInfo(arg.prevState); - if (index >= 0 && selectionInfo.focusedCellId === arg.payload.data.cellId) { + // If this is the focused cell, then user is editing it, hence it needs to be updated. + const isThisTheFocusedCell = selectionInfo.focusedCellId === arg.payload.data.cellId; + // If this edit is part of a sycning comging from another notebook, then we need to update it again. + const isSyncFromAnotherNotebook = isSyncingMessage(arg.payload.messageType); + if (index >= 0 && (isThisTheFocusedCell || isSyncFromAnotherNotebook)) { const newVMs = [...arg.prevState.cellVMs]; const current = arg.prevState.cellVMs[index]; const newCell = { diff --git a/src/datascience-ui/interactive-common/redux/store.ts b/src/datascience-ui/interactive-common/redux/store.ts index 04a41739f9e8..2ca41392c136 100644 --- a/src/datascience-ui/interactive-common/redux/store.ts +++ b/src/datascience-ui/interactive-common/redux/store.ts @@ -87,7 +87,7 @@ function createSendInfoMiddleware(): Redux.Middleware<{}, IStore> { const afterState = store.getState(); // If the action is part of a sync message, then do not send it to the extension. - const messageType = (action?.payload as BaseReduxActionPayload).messageType ?? MessageType.userAction; + const messageType = (action?.payload as BaseReduxActionPayload).messageType ?? MessageType.other; const isSyncMessage = (messageType & MessageType.syncAcrossSameNotebooks) === MessageType.syncAcrossSameNotebooks && (messageType & MessageType.syncAcrossSameNotebooks) === MessageType.syncWithLiveShare; @@ -341,11 +341,13 @@ export function createStore( // This is a message that has been sent from extension purely for synchronization purposes. // Unwrap the message. message = payload.type; + // This is a message that came in as a result of an outgoing message from another view. + basePayload.messageDirection = 'outgoing'; basePayload.messageType = payload.payload.messageType ?? MessageType.syncAcrossSameNotebooks; basePayload.data = payload.payload.data; } else { // Messages result of some user action. - basePayload.messageType = basePayload.messageType ?? MessageType.userAction; + basePayload.messageType = basePayload.messageType ?? MessageType.other; } store.dispatch({ type: message, payload: basePayload }); } diff --git a/src/test/datascience/dataScienceIocContainer.ts b/src/test/datascience/dataScienceIocContainer.ts index 8ec338021b60..30a6f12336db 100644 --- a/src/test/datascience/dataScienceIocContainer.ts +++ b/src/test/datascience/dataScienceIocContainer.ts @@ -181,6 +181,7 @@ import { NativeEditor } from '../../client/datascience/interactive-ipynb/nativeE import { NativeEditorCommandListener } from '../../client/datascience/interactive-ipynb/nativeEditorCommandListener'; import { NativeEditorOldWebView } from '../../client/datascience/interactive-ipynb/nativeEditorOldWebView'; import { NativeEditorStorage } from '../../client/datascience/interactive-ipynb/nativeEditorStorage'; +import { NativeEditorSynchronizer } from '../../client/datascience/interactive-ipynb/nativeEditorSynchronizer'; import { InteractiveWindow } from '../../client/datascience/interactive-window/interactiveWindow'; import { InteractiveWindowCommandListener } from '../../client/datascience/interactive-window/interactiveWindowCommandListener'; import { JupyterCommandFactory } from '../../client/datascience/jupyter/interpreter/jupyterCommand'; @@ -1061,7 +1062,9 @@ export class DataScienceIocContainer extends UnitTestIocContainer { this.serviceManager.addSingleton(IJupyterPasswordConnect, JupyterPasswordConnect); this.serviceManager.addSingleton(IProcessLogger, ProcessLogger); } - + this.serviceManager.addSingleton(NativeEditorSynchronizer, NativeEditorSynchronizer); + // Disable syncrhonizing edits + this.serviceContainer.get(NativeEditorSynchronizer).disable(); const dummyDisposable = { dispose: () => { return;