diff --git a/src/instrument.js b/src/instrument.js index fa2ce7d3..60d88762 100644 --- a/src/instrument.js +++ b/src/instrument.js @@ -1,4 +1,5 @@ import difference from 'lodash/difference'; +import union from 'lodash/union'; export const ActionTypes = { PERFORM_ACTION: 'PERFORM_ACTION', @@ -7,6 +8,7 @@ export const ActionTypes = { COMMIT: 'COMMIT', SWEEP: 'SWEEP', TOGGLE_ACTION: 'TOGGLE_ACTION', + SET_ACTIONS_ACTIVE: 'SET_ACTIONS_ACTIVE', JUMP_TO_STATE: 'JUMP_TO_STATE', IMPORT_STATE: 'IMPORT_STATE' }; @@ -45,6 +47,10 @@ export const ActionCreators = { return { type: ActionTypes.TOGGLE_ACTION, id }; }, + setActionsActive(start, end, active=true) { + return { type: ActionTypes.SET_ACTIONS_ACTIVE, start, end, active }; + }, + jumpToState(index) { return { type: ActionTypes.JUMP_TO_STATE, index }; }, @@ -244,6 +250,22 @@ function liftReducerWith(reducer, initialCommittedState, monitorReducer, options minInvalidatedStateIndex = stagedActionIds.indexOf(actionId); break; } + case ActionTypes.SET_ACTIONS_ACTIVE: { + // Toggle whether an action with given ID is skipped. + // Being skipped means it is a no-op during the computation. + const { start, end, active } = liftedAction; + const actionIds = []; + for (let i = start; i < end; i++) actionIds.push(i); + if (active) { + skippedActionIds = difference(skippedActionIds, actionIds); + } else { + skippedActionIds = union(skippedActionIds, actionIds); + } + + // Optimization: we know history before this action hasn't changed + minInvalidatedStateIndex = stagedActionIds.indexOf(start); + break; + } case ActionTypes.JUMP_TO_STATE: { // Without recomputing anything, move the pointer that tell us // which state is considered the current one. Useful for sliders. diff --git a/test/instrument.spec.js b/test/instrument.spec.js index 6d78f1f8..919b5b28 100644 --- a/test/instrument.spec.js +++ b/test/instrument.spec.js @@ -109,6 +109,23 @@ describe('instrument', () => { expect(store.getState()).toBe(1); }); + it('should set multiple action skip', () => { + // actionId 0 = @@INIT + store.dispatch({ type: 'INCREMENT' }); + store.dispatch({ type: 'INCREMENT' }); + store.dispatch({ type: 'INCREMENT' }); + expect(store.getState()).toBe(3); + + liftedStore.dispatch(ActionCreators.setActionsActive(1, 3, false)); + expect(store.getState()).toBe(1); + + liftedStore.dispatch(ActionCreators.setActionsActive(0, 2, true)); + expect(store.getState()).toBe(2); + + liftedStore.dispatch(ActionCreators.setActionsActive(0, 1, true)); + expect(store.getState()).toBe(2); + }); + it('should sweep disabled actions', () => { // actionId 0 = @@INIT store.dispatch({ type: 'INCREMENT' });