mirror of
https://github.com/xodio/xod.git
synced 2026-03-07 01:06:53 +01:00
Merge pull request #809 from xodio/feat-780-debug-drill-down-2
Drill down in debug tab
This commit is contained in:
@@ -323,8 +323,10 @@ export const startDebugSessionHandler = (storeFn, onCloseCb) => (event, { port }
|
||||
|
||||
const intervalId = setInterval(
|
||||
() => {
|
||||
event.sender.send(EVENTS.DEBUG_SESSION, messageCollector);
|
||||
messageCollector = [];
|
||||
if (messageCollector.length > 0) {
|
||||
event.sender.send(EVENTS.DEBUG_SESSION, messageCollector);
|
||||
messageCollector = [];
|
||||
}
|
||||
},
|
||||
throttleDelay
|
||||
);
|
||||
|
||||
@@ -205,7 +205,11 @@ class App extends client.App {
|
||||
foldEither(
|
||||
error => this.props.actions.addError(error.message),
|
||||
(nodeIdsMap) => {
|
||||
this.props.actions.startDebuggerSession(createSystemMessage('Debug session started'), nodeIdsMap);
|
||||
this.props.actions.startDebuggerSession(
|
||||
createSystemMessage('Debug session started'),
|
||||
nodeIdsMap,
|
||||
this.props.currentPatchPath
|
||||
);
|
||||
debuggerIPC.sendStartDebuggerSession(ipcRenderer, port);
|
||||
},
|
||||
R.map(getNodeIdsMap, eitherTProject)
|
||||
|
||||
@@ -48,7 +48,7 @@ $color-tabs-unactive-background: #2b2b2b;
|
||||
$color-tabs-hover-background: #444444;
|
||||
$color-tabs-active-background: $color-canvas-background;
|
||||
$color-tabs-unactive-text: #cccccc;
|
||||
$color-tabs-active-text: #4ed5ed;
|
||||
$color-tabs-active-text: $color-canvas-selected;
|
||||
$color-tabs-hover-text: #FFF;
|
||||
|
||||
$color-tabs-debugger-background: #3d4931;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
.Breadcrumbs {
|
||||
z-index: 9;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
display: block;
|
||||
list-style: none;
|
||||
box-sizing: border-box;
|
||||
padding: 1px;
|
||||
margin: 0;
|
||||
|
||||
background: $color-tabs-unactive-background;
|
||||
border-top: 1px solid #3e3e3e;
|
||||
border-bottom: 1px solid #3e3e3e;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0 7px 0 0;
|
||||
}
|
||||
|
||||
li:nth-child(1) button:before { display: none; }
|
||||
|
||||
button {
|
||||
&:after, &:before {
|
||||
display: block;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
&:before {
|
||||
border-top: 12px solid $sidebar-color-bg-even;
|
||||
border-bottom: 12px solid $sidebar-color-bg-even;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 0;
|
||||
left: -6px;
|
||||
}
|
||||
&:after {
|
||||
border-top: 12px solid transparent;
|
||||
border-bottom: 12px solid transparent;
|
||||
border-left: 6px solid $sidebar-color-bg-even;
|
||||
border-right: 0;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background: $sidebar-color-bg-even;
|
||||
border: none;
|
||||
color: $sidebar-color-bg-tabs-selected;
|
||||
outline: none;
|
||||
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
|
||||
&:hover {
|
||||
background: $input-option-highlighted;
|
||||
&:before {
|
||||
border-top-color: $input-option-highlighted;
|
||||
border-bottom-color: $input-option-highlighted;
|
||||
}
|
||||
&:after {
|
||||
border-left-color: $input-option-highlighted;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: $color-canvas-selected;
|
||||
}
|
||||
&.is-tail {
|
||||
color: $button-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
.debug-session-stop-button {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
right: 20px;
|
||||
top: 64px;
|
||||
right: 6px;
|
||||
|
||||
padding: 8px 15px 10px 15px;
|
||||
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
@import
|
||||
'components/BackgroundLayer',
|
||||
'components/Button',
|
||||
'components/Breadcrumbs',
|
||||
'components/Comment',
|
||||
'components/Inspector',
|
||||
'components/Debugger',
|
||||
'components/Inspector',
|
||||
'components/Helpbar',
|
||||
'components/Link',
|
||||
'components/Modals',
|
||||
|
||||
@@ -7,3 +7,5 @@ export const DEBUGGER_LOG_CLEAR = 'DEBUGGER_LOG_CLEAR';
|
||||
|
||||
export const DEBUG_SESSION_STARTED = 'DEBUG_SESSION_STARTED';
|
||||
export const DEBUG_SESSION_STOPPED = 'DEBUG_SESSION_STOPPED';
|
||||
|
||||
export const DEBUG_DRILL_DOWN = 'DEBUG_DRILL_DOWN';
|
||||
|
||||
@@ -21,11 +21,12 @@ export const clearDebuggerLog = () => ({
|
||||
type: AT.DEBUGGER_LOG_CLEAR,
|
||||
});
|
||||
|
||||
export const startDebuggerSession = (message, nodeIdsMap) => ({
|
||||
export const startDebuggerSession = (message, nodeIdsMap, currentPatchPath) => ({
|
||||
type: AT.DEBUG_SESSION_STARTED,
|
||||
payload: {
|
||||
message,
|
||||
nodeIdsMap,
|
||||
patchPath: currentPatchPath,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -35,3 +36,11 @@ export const stopDebuggerSession = message => ({
|
||||
message,
|
||||
},
|
||||
});
|
||||
|
||||
export const drillDown = (patchPath, nodeId) => ({
|
||||
type: AT.DEBUG_DRILL_DOWN,
|
||||
payload: {
|
||||
patchPath,
|
||||
nodeId,
|
||||
},
|
||||
});
|
||||
|
||||
49
packages/xod-client/src/debugger/containers/Breadcrumbs.jsx
Normal file
49
packages/xod-client/src/debugger/containers/Breadcrumbs.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import R from 'ramda';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { drillDown } from '../actions';
|
||||
import { getRenerableBreadcrumbChunks, getBreadcrumbActiveIndex } from '../../editor/selectors';
|
||||
|
||||
const Breadcrumbs = ({ chunks, activeIndex, actions }) => (
|
||||
<ul className="Breadcrumbs">
|
||||
{chunks.map((chunk, i) => {
|
||||
const cls = classNames('Breadcrumbs-chunk-button', {
|
||||
'is-active': (i === activeIndex),
|
||||
'is-tail': (i > activeIndex),
|
||||
});
|
||||
return (
|
||||
<li key={chunk.nodeId}>
|
||||
<button
|
||||
className={cls}
|
||||
onClick={() => actions.drillDown(chunk.patchPath, chunk.nodeId)}
|
||||
>
|
||||
{chunk.label}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
|
||||
Breadcrumbs.propTypes = {
|
||||
chunks: PropTypes.arrayOf(PropTypes.object),
|
||||
activeIndex: PropTypes.number,
|
||||
actions: PropTypes.objectOf(PropTypes.func),
|
||||
};
|
||||
|
||||
const mapStateToProps = R.applySpec({
|
||||
chunks: getRenerableBreadcrumbChunks,
|
||||
activeIndex: getBreadcrumbActiveIndex,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators({
|
||||
drillDown,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Breadcrumbs);
|
||||
@@ -17,6 +17,12 @@ import initialState from './state';
|
||||
|
||||
const MAX_LOG_MESSAGES = 1000;
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
// Utils
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
const addToLog = R.over(R.lensProp('log'));
|
||||
|
||||
const addMessageToLog = R.curry(
|
||||
@@ -62,6 +68,12 @@ const updateWatchNodeValues = R.curry(
|
||||
const showDebuggerPane = R.assoc('isVisible', true);
|
||||
const hideDebuggerPane = R.assoc('isVisible', false);
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
// Reducer
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case SHOW_DEBUGGER_PANEL:
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import R from 'ramda';
|
||||
import { createSelector } from 'reselect';
|
||||
import {
|
||||
getCurrentTabId,
|
||||
getBreadcrumbChunks,
|
||||
getBreadcrumbActiveIndex,
|
||||
} from '../editor/selectors';
|
||||
import { DEBUGGER_TAB_ID } from '../editor/constants';
|
||||
|
||||
export const getDebuggerState = R.prop('debugger');
|
||||
|
||||
@@ -26,3 +33,40 @@ export const getWatchNodeValues = R.compose(
|
||||
R.prop('watchNodeValues'),
|
||||
getDebuggerState
|
||||
);
|
||||
|
||||
export const getWatchNodeValuesForCurrentPatch = createSelector(
|
||||
[getCurrentTabId, getWatchNodeValues, getBreadcrumbChunks, getBreadcrumbActiveIndex],
|
||||
(tabId, nodeValues, chunks, activeIndex) => {
|
||||
if (tabId !== DEBUGGER_TAB_ID) return {};
|
||||
|
||||
const nodeIdPath = R.compose(
|
||||
R.join('~'),
|
||||
R.append(''),
|
||||
R.map(R.prop('nodeId')),
|
||||
R.tail, // remove first cause it's entry patch without any nodeId
|
||||
R.take(activeIndex + 1)
|
||||
)(chunks);
|
||||
|
||||
return R.compose(
|
||||
R.fromPairs,
|
||||
R.when(
|
||||
() => (activeIndex !== 0),
|
||||
R.map(
|
||||
R.over(
|
||||
R.lensIndex(0),
|
||||
R.replace(nodeIdPath, '')
|
||||
)
|
||||
)
|
||||
),
|
||||
R.filter(R.compose(
|
||||
R.ifElse(
|
||||
() => (activeIndex === 0),
|
||||
R.complement(R.contains)('~'),
|
||||
R.startsWith(nodeIdPath),
|
||||
),
|
||||
R.nth(0)
|
||||
)),
|
||||
R.toPairs
|
||||
)(nodeValues);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -67,3 +67,5 @@ export const TAB_TYPES = {
|
||||
PATCH: 'PATCH',
|
||||
DEBUGGER: 'DEBUGGER',
|
||||
};
|
||||
|
||||
export const DEBUGGER_TAB_ID = 'debugger';
|
||||
|
||||
@@ -19,13 +19,14 @@ import * as EditorSelectors from '../selectors';
|
||||
|
||||
import { isInput } from '../../utils/browser';
|
||||
import { COMMAND } from '../../utils/constants';
|
||||
import { FOCUS_AREAS } from '../constants';
|
||||
import { FOCUS_AREAS, DEBUGGER_TAB_ID } from '../constants';
|
||||
|
||||
import Patch from './Patch';
|
||||
import NoPatch from '../components/NoPatch';
|
||||
import Suggester from '../components/Suggester';
|
||||
import Inspector from '../components/Inspector';
|
||||
import Debugger from '../../debugger/containers/Debugger';
|
||||
import Breadcrumbs from '../../debugger/containers/Breadcrumbs';
|
||||
import Sidebar from '../../utils/components/Sidebar';
|
||||
import Workarea from '../../utils/components/Workarea';
|
||||
|
||||
@@ -119,6 +120,10 @@ class Editor extends React.Component {
|
||||
) : null;
|
||||
|
||||
const DebuggerContainer = (this.props.isDebuggerVisible) ? <Debugger /> : null;
|
||||
const BreadcrumbsContainer = (
|
||||
this.props.isDebuggerVisible &&
|
||||
this.props.currentTabId === DEBUGGER_TAB_ID
|
||||
) ? <Breadcrumbs /> : null;
|
||||
|
||||
const DebugSessionStopButton = (
|
||||
this.props.isDebugSessionRunning &&
|
||||
@@ -154,6 +159,7 @@ class Editor extends React.Component {
|
||||
{openedPatch}
|
||||
{suggester}
|
||||
{DebuggerContainer}
|
||||
{BreadcrumbsContainer}
|
||||
</Workarea>
|
||||
</FocusTrap>
|
||||
<Helpbar />
|
||||
@@ -168,6 +174,7 @@ Editor.propTypes = {
|
||||
selection: sanctuaryPropType($.Array(RenderableSelection)),
|
||||
currentPatchPath: PropTypes.string,
|
||||
currentPatch: sanctuaryPropType($Maybe(PatchType)),
|
||||
currentTabId: PropTypes.string,
|
||||
patchesIndex: PropTypes.object,
|
||||
isHelpbarVisible: PropTypes.bool,
|
||||
isDebuggerVisible: PropTypes.bool,
|
||||
@@ -194,6 +201,7 @@ const mapStateToProps = R.applySpec({
|
||||
selection: ProjectSelectors.getRenderableSelection,
|
||||
currentPatch: ProjectSelectors.getCurrentPatch,
|
||||
currentPatchPath: EditorSelectors.getCurrentPatchPath,
|
||||
currentTabId: EditorSelectors.getCurrentTabId,
|
||||
patchesIndex: ProjectSelectors.getPatchSearchIndex,
|
||||
suggesterIsVisible: EditorSelectors.isSuggesterVisible,
|
||||
suggesterPlacePosition: EditorSelectors.getSuggesterPlacePosition,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { bindActionCreators } from 'redux';
|
||||
|
||||
import * as EditorActions from '../../actions';
|
||||
import * as ProjectActions from '../../../project/actions';
|
||||
import * as DebuggerActions from '../../../debugger/actions';
|
||||
|
||||
import * as EditorSelectors from '../../selectors';
|
||||
import * as ProjectSelectors from '../../../project/selectors';
|
||||
@@ -153,7 +154,7 @@ const mapStateToProps = R.applySpec({
|
||||
offset: EditorSelectors.getCurrentPatchOffset,
|
||||
draggedPreviewSize: EditorSelectors.getDraggedPreviewSize,
|
||||
isDebugSession: DebugSelectors.isDebugSession,
|
||||
nodeValues: DebugSelectors.getWatchNodeValues,
|
||||
nodeValues: DebugSelectors.getWatchNodeValuesForCurrentPatch,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
@@ -174,6 +175,7 @@ const mapDispatchToProps = dispatch => ({
|
||||
linkPin: EditorActions.linkPin,
|
||||
setOffset: EditorActions.setCurrentPatchOffset,
|
||||
switchPatch: EditorActions.switchPatch,
|
||||
drillDown: DebuggerActions.drillDown,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ const debuggingMode = {
|
||||
|
||||
api.props.actions.deselectAll();
|
||||
},
|
||||
onNodeDoubleClick(api, nodeId, patchPath) {
|
||||
api.props.actions.drillDown(patchPath, nodeId);
|
||||
},
|
||||
getHotkeyHandlers(api) {
|
||||
return {
|
||||
[COMMAND.DESELECT]: api.props.actions.deselectAll,
|
||||
@@ -112,6 +115,7 @@ const debuggingMode = {
|
||||
linkingPin={api.props.linkingPin}
|
||||
onMouseDown={R.partial(this.onEntityMouseDown, [api, SELECTION_ENTITY_TYPE.NODE])}
|
||||
onMouseUp={R.partial(this.onEntityMouseUp, [api, SELECTION_ENTITY_TYPE.NODE])}
|
||||
onDoubleClick={bindApi(api, this.onNodeDoubleClick)}
|
||||
/>
|
||||
<Layers.LinksOverlay
|
||||
links={api.props.links}
|
||||
|
||||
@@ -38,10 +38,11 @@ import {
|
||||
import {
|
||||
DEBUG_SESSION_STARTED,
|
||||
DEBUG_SESSION_STOPPED,
|
||||
DEBUG_DRILL_DOWN,
|
||||
} from '../debugger/actionTypes';
|
||||
|
||||
import { DEFAULT_PANNING_OFFSET } from '../project/nodeLayout';
|
||||
import { TAB_TYPES, EDITOR_MODE, SELECTION_ENTITY_TYPE } from './constants';
|
||||
import { TAB_TYPES, EDITOR_MODE, SELECTION_ENTITY_TYPE, DEBUGGER_TAB_ID } from './constants';
|
||||
import { getTabByPatchPath } from './selectors';
|
||||
import { switchPatchUnsafe } from './actions';
|
||||
|
||||
@@ -52,6 +53,7 @@ import { switchPatchUnsafe } from './actions';
|
||||
// =============================================================================
|
||||
|
||||
const getTabs = R.prop('tabs');
|
||||
const getCurrentTabId = R.prop('currentTabId');
|
||||
|
||||
const getTabById = R.curry(
|
||||
(tabId, state) => R.compose(
|
||||
@@ -60,6 +62,27 @@ const getTabById = R.curry(
|
||||
)(state)
|
||||
);
|
||||
|
||||
const getCurrentTab = R.converge(
|
||||
getTabById,
|
||||
[
|
||||
getCurrentTabId,
|
||||
R.identity,
|
||||
]
|
||||
);
|
||||
|
||||
const getBreadcrumbs = R.compose(
|
||||
R.prop('breadcrumbs'),
|
||||
getCurrentTab
|
||||
);
|
||||
const getBreadcrumbActiveIndex = R.compose(
|
||||
R.propOr(-1, 'activeIndex'),
|
||||
getBreadcrumbs
|
||||
);
|
||||
const getBreadcrumbChunks = R.compose(
|
||||
R.propOr([], 'chunks'),
|
||||
getBreadcrumbs
|
||||
);
|
||||
|
||||
const isTabOpened = R.curry(
|
||||
(tabId, state) => R.compose(
|
||||
R.complement(R.isNil),
|
||||
@@ -76,11 +99,20 @@ const isPatchOpened = R.curry(
|
||||
);
|
||||
|
||||
const setTabOffset = R.curry(
|
||||
(offset, tabId, state) => R.assocPath(
|
||||
['tabs', tabId, 'offset'],
|
||||
offset,
|
||||
state
|
||||
)
|
||||
(offset, tabId, state) => R.compose(
|
||||
R.when(
|
||||
() => (tabId === DEBUGGER_TAB_ID),
|
||||
newState => R.assocPath(
|
||||
['tabs', tabId, 'breadcrumbs', 'chunks', getBreadcrumbActiveIndex(newState), 'offset'],
|
||||
offset,
|
||||
newState
|
||||
)
|
||||
),
|
||||
R.assocPath(
|
||||
['tabs', tabId, 'offset'],
|
||||
offset
|
||||
)
|
||||
)(state)
|
||||
);
|
||||
|
||||
const getTabIdbyPatchPath = R.curry(
|
||||
@@ -117,6 +149,14 @@ const syncTabOffset = R.curry(
|
||||
}
|
||||
);
|
||||
|
||||
const setPropsToTab = R.curry(
|
||||
(id, props, state) => R.compose(
|
||||
R.assocPath(['tabs', id], R.__, state),
|
||||
R.merge(R.__, props),
|
||||
getTabById(id)
|
||||
)(state)
|
||||
);
|
||||
|
||||
const addTabWithProps = R.curry(
|
||||
(id, type, patchPath, state) => {
|
||||
const tabs = R.prop('tabs')(state);
|
||||
@@ -130,7 +170,7 @@ const addTabWithProps = R.curry(
|
||||
);
|
||||
const newIndex = R.inc(lastIndex);
|
||||
|
||||
return R.assocPath(['tabs', id], {
|
||||
return setPropsToTab(id, {
|
||||
id,
|
||||
patchPath,
|
||||
index: newIndex,
|
||||
@@ -225,6 +265,10 @@ const closeTabById = R.curry(
|
||||
);
|
||||
|
||||
return R.compose(
|
||||
R.when(
|
||||
isCurrentDebuggerTabClosing,
|
||||
clearSelection
|
||||
),
|
||||
R.cond([
|
||||
[isCurrentDebuggerTabClosing, openOriginalPatch(tabToClose.patchPath)],
|
||||
[isCurrentTabClosing, openLatestOpenedTab],
|
||||
@@ -250,6 +294,77 @@ const renamePatchInTabs = (newPatchPath, oldPatchPath, state) => {
|
||||
|
||||
const createSelectionEntity = R.curry((entityType, id) => ({ entity: entityType, id }));
|
||||
|
||||
const findChunkIndex = R.curry(
|
||||
(patchPath, nodeId, chunks) =>
|
||||
R.findIndex(R.both(
|
||||
R.propEq('patchPath', patchPath),
|
||||
R.propEq('nodeId', nodeId),
|
||||
),
|
||||
chunks
|
||||
)
|
||||
);
|
||||
|
||||
const createChunk = (patchPath, nodeId) => ({
|
||||
patchPath,
|
||||
nodeId,
|
||||
offset: DEFAULT_PANNING_OFFSET,
|
||||
});
|
||||
|
||||
const setActiveIndex = R.curry(
|
||||
(index, state) => {
|
||||
const curTabId = getCurrentTabId(state);
|
||||
return R.assocPath(['tabs', curTabId, 'breadcrumbs', 'activeIndex'], index, state);
|
||||
}
|
||||
);
|
||||
|
||||
const drillDown = R.curry(
|
||||
(patchPath, nodeId, state) => {
|
||||
const currentTabId = getCurrentTabId(state);
|
||||
if (currentTabId !== DEBUGGER_TAB_ID) return state;
|
||||
|
||||
const activeIndex = getBreadcrumbActiveIndex(state);
|
||||
const chunks = getBreadcrumbChunks(state);
|
||||
const index = findChunkIndex(patchPath, nodeId, chunks);
|
||||
|
||||
if (index > -1) {
|
||||
const chunkOffset = getBreadcrumbChunks(state)[index].offset;
|
||||
return R.compose(
|
||||
setTabOffset(chunkOffset, currentTabId),
|
||||
setActiveIndex(index)
|
||||
)(state);
|
||||
}
|
||||
|
||||
const shouldResetTail = (activeIndex < (chunks.length - 1));
|
||||
const newChunk = createChunk(patchPath, nodeId);
|
||||
const newChunkOffset = newChunk.offset;
|
||||
|
||||
return R.compose(
|
||||
setTabOffset(newChunkOffset, currentTabId),
|
||||
R.converge(
|
||||
setActiveIndex,
|
||||
[
|
||||
R.compose(
|
||||
R.dec,
|
||||
R.length,
|
||||
getBreadcrumbChunks
|
||||
),
|
||||
R.identity,
|
||||
]
|
||||
),
|
||||
R.over(
|
||||
R.lensPath(['tabs', currentTabId, 'breadcrumbs', 'chunks']),
|
||||
R.compose(
|
||||
R.append(newChunk),
|
||||
R.when(
|
||||
() => shouldResetTail,
|
||||
R.take((activeIndex + 1))
|
||||
)
|
||||
)
|
||||
),
|
||||
)(state);
|
||||
}
|
||||
);
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
// Reducer
|
||||
@@ -363,11 +478,13 @@ const editorReducer = (state = {}, action) => {
|
||||
case EDITOR_SWITCH_PATCH:
|
||||
return openPatchByPath(action.payload.patchPath, state);
|
||||
case EDITOR_SWITCH_TAB:
|
||||
return R.assoc(
|
||||
'currentTabId',
|
||||
action.payload.tabId,
|
||||
state
|
||||
);
|
||||
return R.compose(
|
||||
R.assoc(
|
||||
'currentTabId',
|
||||
action.payload.tabId
|
||||
),
|
||||
clearSelection
|
||||
)(state);
|
||||
case PATCH_RENAME:
|
||||
return renamePatchInTabs(
|
||||
action.payload.newPatchPath,
|
||||
@@ -419,16 +536,24 @@ const editorReducer = (state = {}, action) => {
|
||||
const currentPatchPath = currentTab.patchPath;
|
||||
const currentOffset = currentTab.offset;
|
||||
return R.compose(
|
||||
R.assoc('currentTabId', 'debugger'),
|
||||
drillDown(action.payload.patchPath, null),
|
||||
R.assoc('currentTabId', DEBUGGER_TAB_ID),
|
||||
R.assoc('mode', EDITOR_MODE.DEBUGGING),
|
||||
setTabOffset(currentOffset, 'debugger'),
|
||||
addTabWithProps('debugger', TAB_TYPES.DEBUGGER, currentPatchPath)
|
||||
setTabOffset(currentOffset, DEBUGGER_TAB_ID),
|
||||
clearSelection,
|
||||
addTabWithProps(DEBUGGER_TAB_ID, TAB_TYPES.DEBUGGER, currentPatchPath)
|
||||
)(state);
|
||||
}
|
||||
case DEBUG_SESSION_STOPPED:
|
||||
return R.when(
|
||||
isTabOpened('debugger'),
|
||||
closeTabById('debugger')
|
||||
isTabOpened(DEBUGGER_TAB_ID),
|
||||
closeTabById(DEBUGGER_TAB_ID)
|
||||
)(state);
|
||||
case DEBUG_DRILL_DOWN:
|
||||
return R.compose(
|
||||
drillDown(action.payload.patchPath, action.payload.nodeId),
|
||||
setPropsToTab(DEBUGGER_TAB_ID, { patchPath: action.payload.patchPath }),
|
||||
clearSelection
|
||||
)(state);
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import R from 'ramda';
|
||||
import { Maybe } from 'ramda-fantasy';
|
||||
import { createSelector } from 'reselect';
|
||||
import { mapIndexed } from 'xod-func-tools';
|
||||
import * as XP from 'xod-project';
|
||||
import { addPoints, subtractPoints, DEFAULT_PANNING_OFFSET } from '../project/nodeLayout';
|
||||
|
||||
const getProject = R.prop('project'); // Problem of cycle imports...
|
||||
|
||||
export const getEditor = R.prop('editor');
|
||||
|
||||
|
||||
@@ -141,3 +146,56 @@ export const getSuggesterHighlightedPatchPath = R.pipe(
|
||||
getSuggester,
|
||||
R.prop('highlightedPatchPath')
|
||||
);
|
||||
|
||||
|
||||
export const getBreadcrumbs = R.compose(
|
||||
R.prop('breadcrumbs'),
|
||||
getCurrentTab
|
||||
);
|
||||
|
||||
export const getBreadcrumbChunks = R.compose(
|
||||
R.propOr([], 'chunks'),
|
||||
getBreadcrumbs
|
||||
);
|
||||
|
||||
export const getBreadcrumbActiveIndex = R.compose(
|
||||
R.propOr(-1, 'activeIndex'),
|
||||
getBreadcrumbs
|
||||
);
|
||||
|
||||
export const getActiveBreadcrumb = createSelector(
|
||||
[getBreadcrumbActiveIndex, getBreadcrumbChunks],
|
||||
R.ifElse(
|
||||
R.equals(-1),
|
||||
R.always(null),
|
||||
R.nth
|
||||
)
|
||||
);
|
||||
|
||||
export const getRenerableBreadcrumbChunks = createSelector(
|
||||
[getProject, getBreadcrumbChunks],
|
||||
(project, chunks) => mapIndexed(
|
||||
(chunk, i) => {
|
||||
const patchName = XP.getBaseName(chunk.patchPath);
|
||||
if (chunk.nodeId === null) {
|
||||
return R.assoc('label', patchName, chunk);
|
||||
}
|
||||
|
||||
const parentPatchPath = chunks[i - 1].patchPath;
|
||||
return R.compose(
|
||||
R.assoc('label', R.__, chunk),
|
||||
R.when(
|
||||
R.isEmpty,
|
||||
R.always(patchName)
|
||||
),
|
||||
Maybe.maybe(
|
||||
patchName,
|
||||
XP.getNodeLabel
|
||||
),
|
||||
XP.getNodeById(chunk.nodeId),
|
||||
XP.getPatchByPathUnsafe(parentPatchPath)
|
||||
)(project);
|
||||
},
|
||||
chunks
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user