Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions src/client/datascience/interactive-common/interactiveBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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';
Expand Down Expand Up @@ -44,7 +43,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, SharedMessages } from '../messages';
import { CssMessages } from '../messages';
import { ProgressReporter } from '../progress/progressReporter';
import {
CellState,
Expand Down Expand Up @@ -74,7 +73,6 @@ import {
} from '../types';
import { WebViewHost } from '../webViewHost';
import { InteractiveWindowMessageListener } from './interactiveWindowMessageListener';
import { BaseReduxActionPayload } from './types';

@injectable()
export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapping> implements IInteractiveBase {
Expand Down Expand Up @@ -178,11 +176,6 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
// tslint:disable-next-line: no-any no-empty cyclomatic-complexity max-func-body-length
public onMessage(message: string, payload: any) {
switch (message) {
case InteractiveWindowMessages.Sync:
// tslint:disable-next-line: no-any
const syncPayload = payload as { type: InteractiveWindowMessages | SharedMessages | CommonActionType; payload: BaseReduxActionPayload<any> };
this.postMessageInternal(syncPayload.type, syncPayload.payload).ignoreErrors();
break;
case InteractiveWindowMessages.GotoCodeCell:
this.handleMessage(message, payload, this.gotoCode);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { InteractiveWindowMessages, InteractiveWindowRemoteMessages } from './in

// This class listens to messages that come from the local Python Interactive window
export class InteractiveWindowMessageListener implements IWebPanelMessageListener {
private static handlers = new Map<InteractiveWindowMessageListener, (message: string, payload: any) => void>();
private postOffice: PostOffice;
private disposedCallback: () => void;
private callback: (message: string, payload: any) => void;
Expand All @@ -41,7 +40,6 @@ 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() {
Expand All @@ -50,20 +48,6 @@ 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;
}
// Temporarily disabled.
// const cb = InteractiveWindowMessageListener.handlers.get(item);
// if (cb) {
// cb(InteractiveWindowMessages.Sync, { type: message, payload: syncPayload });
// }
});
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();
Expand Down
24 changes: 21 additions & 3 deletions src/client/datascience/interactive-common/synchronization.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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.
Expand Down Expand Up @@ -172,11 +171,30 @@ const messageWithMessageTypes: MessageMapping<IInteractiveWindowMapping> & Messa
[SharedMessages.UpdateSettings]: MessageType.userAction
};

export function isActionPerformedByUser(action: BaseReduxActionPayload<{}> | BaseReduxActionPayload<never>) {
return action.messageType === undefined;
/**
* If the original message was a sync message, then do not send messages to extension.
* We allow messages to be sent to extension ONLY when the original message was triggered by the user.
*
* @export
* @param {MessageType} [messageType]
* @returns
*/
export function checkToPostBasedOnOriginalMessageType(messageType?: MessageType): boolean {
if (!messageType) {
return true;
}
if (
(messageType & MessageType.syncAcrossSameNotebooks) === MessageType.syncAcrossSameNotebooks ||
(messageType & MessageType.syncWithLiveShare) === MessageType.syncWithLiveShare
) {
return false;
}

return true;
}

export function shouldRebroadcast(message: keyof IInteractiveWindowMapping): [boolean, MessageType] {
// Get the configured type for this message (whether it should be re-broadcasted or not).
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.
Expand Down
14 changes: 12 additions & 2 deletions src/datascience-ui/history-react/redux/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
// Licensed under the MIT License.
'use strict';

import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes';
import { IInteractiveWindowMapping, 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,
CommonActionTypeMapping,
ICellAction,
ICodeAction,
ICodeCreatedAction,
Expand All @@ -18,6 +18,16 @@ import {
} from '../../interactive-common/redux/reducers/types';
import { IMonacoModelContentChangeEvent } from '../../react-common/monacoHelpers';

// This function isn't made common and not exported, to ensure it isn't used elsewhere.
function createIncomingActionWithPayload<M extends IInteractiveWindowMapping & CommonActionTypeMapping, K extends keyof M>(type: K, data: M[K]): CommonAction<M[K]> {
// tslint:disable-next-line: no-any
return { type, payload: { data, messageDirection: 'incoming' } as any } as any;
}
// This function isn't made common and not exported, to ensure it isn't used elsewhere.
function createIncomingAction(type: CommonActionType | InteractiveWindowMessages): CommonAction {
return { type, payload: { messageDirection: 'incoming', data: undefined } };
}

// See https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object
export const actionCreators = {
restartKernel: (): CommonAction => createIncomingAction(CommonActionType.RESTART_KERNEL),
Expand Down
73 changes: 46 additions & 27 deletions src/datascience-ui/interactive-common/redux/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

import * as Redux from 'redux';
import { IInteractiveWindowMapping, InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes';
import { checkToPostBasedOnOriginalMessageType, MessageType, shouldRebroadcast } from '../../../client/datascience/interactive-common/synchronization';
import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types';
import { CssMessages, SharedMessages } from '../../../client/datascience/messages';
import { QueueAnotherFunc } from '../../react-common/reduxUtils';
import { CommonAction, CommonActionType, CommonActionTypeMapping } from './reducers/types';
import { CommonActionType, CommonActionTypeMapping } from './reducers/types';

const AllowedMessages = [...Object.values(InteractiveWindowMessages), ...Object.values(CssMessages), ...Object.values(SharedMessages), ...Object.values(CommonActionType)];
export function isAllowedMessage(message: string) {
Expand All @@ -19,20 +20,32 @@ export function isAllowedAction(action: Redux.AnyAction) {
return isAllowedMessage(action.type);
}

export function createIncomingActionWithPayload<M extends IInteractiveWindowMapping & CommonActionTypeMapping, K extends keyof M>(type: K, data: M[K]): CommonAction<M[K]> {
// tslint:disable-next-line: no-any
return { type, payload: { data, messageDirection: 'incoming' } as any } as any;
}
export function createIncomingAction(type: CommonActionType | InteractiveWindowMessages): CommonAction {
return { type, payload: { messageDirection: 'incoming', data: undefined } };
}

type ReducerArg = {
// tslint:disable-next-line: no-any
queueAction: QueueAnotherFunc<any>;
// tslint:disable-next-line: no-any
payload?: BaseReduxActionPayload<any>;
};

export function queueIncomingActionWithPayload<M extends IInteractiveWindowMapping & CommonActionTypeMapping, K extends keyof M>(
originalReducerArg: ReducerArg,
type: K,
data: M[K]
): void {
if (!checkToPostBasedOnOriginalMessageType(originalReducerArg.payload?.messageType)) {
return;
}

// tslint:disable-next-line: no-any
const action = { type, payload: { data, messageDirection: 'incoming' } as any } as any;
originalReducerArg.queueAction(action);
}

export function queueIncomingAction<M extends IInteractiveWindowMapping & CommonActionTypeMapping, K extends keyof M>(originalReducerArg: ReducerArg, type: K): void {
// tslint:disable-next-line: no-any
queueIncomingActionWithPayload(originalReducerArg, type as any, undefined);
}

/**
* Post a message to the extension (via dispatcher actions).
*/
Expand All @@ -44,10 +57,15 @@ export function postActionToExtension<K, M extends IInteractiveWindowMapping, T
export function postActionToExtension<K, M extends IInteractiveWindowMapping, T extends keyof M = keyof M>(originalReducerArg: ReducerArg, message: T, payload?: M[T]): void;
// tslint:disable-next-line: no-any
export function postActionToExtension(originalReducerArg: ReducerArg, message: any, payload?: any) {
if (!checkToPostBasedOnOriginalMessageType(originalReducerArg.payload?.messageType)) {
return;
}

// tslint:disable-next-line: no-any
const newPayload: BaseReduxActionPayload<any> = ({
data: payload,
messageDirection: 'outgoing'
messageDirection: 'outgoing',
messageType: MessageType.userAction
// tslint:disable-next-line: no-any
} as any) as BaseReduxActionPayload<any>;
const action = { type: CommonActionType.PostOutgoingMessage, payload: { payload: newPayload, type: message } };
Expand All @@ -61,27 +79,28 @@ export function unwrapPostableAction(action: Redux.AnyAction): { type: keyof IIn
}

export function reBroadcastMessageIfRequired(
_dispatcher: Function,
dispatcher: Function,
message: InteractiveWindowMessages | SharedMessages | CommonActionType | CssMessages,
payload?: BaseReduxActionPayload<{}>
) {
if (typeof payload?.messageType === 'number' || message === InteractiveWindowMessages.Sync) {
if (
message === InteractiveWindowMessages.Sync ||
payload?.messageType === MessageType.syncAcrossSameNotebooks ||
payload?.messageType === MessageType.syncWithLiveShare ||
payload?.messageDirection === 'outgoing'
) {
return;
}
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<any> = { 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<any> = { 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);
// }
}
2 changes: 1 addition & 1 deletion src/datascience-ui/interactive-common/redux/postOffice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function generatePostOfficeSendReducer(postOffice: PostOffice): Redux.Red
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') {
if (payload?.messageDirection === 'incoming') {
// We can delay this, first focus on UX perf.
setTimeout(() => {
reBroadcastMessageIfRequired(postOffice.sendMessage.bind(postOffice), action.type, action?.payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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 { createIncomingAction } from '../helpers';
import { queueIncomingAction } from '../helpers';
import { CommonActionType, ICodeCreatedAction, IEditCellAction } from './types';

export interface IMonacoState {
Expand Down Expand Up @@ -63,7 +63,7 @@ function finishTokenizer<T>(buffer: ArrayBuffer, tmJson: string, arg: MonacoRedu
if (e) {
logMessage(`ERROR from onigasm: ${e}`);
}
arg.queueAction(createIncomingAction(InteractiveWindowMessages.MonacoReady));
queueIncomingAction(arg, InteractiveWindowMessages.MonacoReady);
}).ignoreErrors();
}

Expand Down
13 changes: 11 additions & 2 deletions src/datascience-ui/interactive-common/redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as Redux from 'redux';
import { createLogger } from 'redux-logger';
import { Identifiers } from '../../../client/datascience/constants';
import { InteractiveWindowMessages } from '../../../client/datascience/interactive-common/interactiveWindowTypes';
import { MessageType } from '../../../client/datascience/interactive-common/synchronization';
import { BaseReduxActionPayload } from '../../../client/datascience/interactive-common/types';
import { CssMessages } from '../../../client/datascience/messages';
import { CellState } from '../../../client/datascience/types';
Expand Down Expand Up @@ -75,7 +76,11 @@ 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.
if (action.payload && typeof (action.payload as BaseReduxActionPayload).messageType === 'number') {
const messageType = (action?.payload as BaseReduxActionPayload).messageType ?? MessageType.userAction;
const isSyncMessage =
(messageType & MessageType.syncAcrossSameNotebooks) === MessageType.syncAcrossSameNotebooks &&
(messageType & MessageType.syncAcrossSameNotebooks) === MessageType.syncWithLiveShare;
if (isSyncMessage) {
return res;
}

Expand Down Expand Up @@ -296,10 +301,14 @@ export function createStore<M>(skipDefault: boolean, baseTheme: string, testMode
if (isAllowedMessage(message)) {
const basePayload: BaseReduxActionPayload = { data: payload };
if (message === InteractiveWindowMessages.Sync) {
// This is a message that has been sent from extension purely for synchronization purposes.
// Unwrap the message.
message = payload.type;
basePayload.messageType = payload.payload.messageType;
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;
}
store.dispatch({ type: message, payload: basePayload });
}
Expand Down
14 changes: 12 additions & 2 deletions src/datascience-ui/native-editor/redux/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// Licensed under the MIT License.
'use strict';
import * as uuid from 'uuid/v4';
import { InteractiveWindowMessages, NativeCommandType } from '../../../client/datascience/interactive-common/interactiveWindowTypes';
import { IInteractiveWindowMapping, 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,
CommonActionTypeMapping,
ICellAction,
ICellAndCursorAction,
ICodeAction,
Expand All @@ -20,6 +20,16 @@ import {
} from '../../interactive-common/redux/reducers/types';
import { IMonacoModelContentChangeEvent } from '../../react-common/monacoHelpers';

// This function isn't made common and not exported, to ensure it isn't used elsewhere.
function createIncomingActionWithPayload<M extends IInteractiveWindowMapping & CommonActionTypeMapping, K extends keyof M>(type: K, data: M[K]): CommonAction<M[K]> {
// tslint:disable-next-line: no-any
return { type, payload: { data, messageDirection: 'incoming' } as any } as any;
}
// This function isn't made common and not exported, to ensure it isn't used elsewhere.
function createIncomingAction(type: CommonActionType | InteractiveWindowMessages): CommonAction {
return { type, payload: { messageDirection: 'incoming', data: undefined } };
}

// See https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object
export const actionCreators = {
addCell: () => createIncomingActionWithPayload(CommonActionType.ADD_AND_FOCUS_NEW_CELL, { newCellId: uuid() }),
Expand Down
Loading