Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search/filter command by pattern, function name, and effect type #21

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion src/components/Dock/Dock.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Dock extends React.Component {
node = null

state = {
visible: false,
visible: true,
width: 500,
isResizing: false
}
Expand Down
62 changes: 48 additions & 14 deletions src/components/Effect/Effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,63 @@ class Effect extends React.Component {

effectId = this.props.effect.effectId

highlightFilter = (text) => {
const highlight = this.props.filter
const parts = text.split(new RegExp(`(${highlight})`, 'gi'))

return <span>{parts.map((part, i) =>
<span key={i} style={part.toLowerCase() === highlight.toLowerCase() ? { fontWeight: 'bold', background: '#ffcc00' } : {} }>
{part}
</span>)
} </span>
}

renderResult(status, result, error, winner) {
return <Result status={status} result={result} error={error} winner={winner} />
}

render() {
const {effect} = this.props
const {effect, filter} = this.props
const { status, result, error, winner } = effect


let nodes = []
let data

if(effect.root) {
nodes = nodes.concat(
renderFuncCall(effect.effect.saga, effect.effect.args),
renderFuncCall(effect.effect.saga, effect.effect.args, this.highlightFilter),
this.renderResult(status, result, error)
)
}

else if((data = asEffect.take(effect.effect))) {
nodes = nodes.concat(
renderEffectType('take'),
<SagaValue value={data.pattern || data.channel} isIdentifier={true} />,
<SagaValue
value={data.pattern || data.channel}
isIdentifier={true}
highlighter={this.highlightFilter}
/>,
this.renderResult(status, result, error, winner)
)
}

else if((data = asEffect.put(effect.effect))) {
nodes = nodes.concat(
renderEffectType('put'),
<SagaValue value={data.channel || data.action} label={data.action.type} isIdentifier={true} />
<SagaValue
value={data.channel || data.action}
label={data.action.type}
isIdentifier={true}
highlighter={this.highlightFilter}
/>
)
}

else if((data = asEffect.call(effect.effect))) {
nodes = nodes.concat(
renderEffectType('call'),
renderFuncCall(data.fn, data.args),
renderFuncCall(data.fn, data.args, this.highlightFilter),
this.renderResult(status, result, error, winner)
)
}
Expand All @@ -71,23 +90,33 @@ class Effect extends React.Component {
else if((data = asEffect.fork(effect.effect))) {
nodes = nodes.concat(
renderEffectType('fork'),
renderFuncCall(data.fn, data.args),
renderFuncCall(data.fn, data.args, this.highlightFilter),
this.renderResult(status, result, error, winner)
)
}

else if((data = asEffect.join(effect.effect))) {
nodes = nodes.concat(
renderEffectType('join'),
<SagaValue value={data} isIdentifier={true} label={data.name} />,
<SagaValue
value={data}
isIdentifier={true}
label={data.name}
highlighter={this.highlightFilter}
/>,
this.renderResult(status, result, error, winner)
)
}

else if((data = asEffect.cancel(effect.effect))) {
nodes = nodes.concat(
renderEffectType('cancel'),
<SagaValue value={data} isIdentifier={true} label={data.name} />,
<SagaValue
value={data}
isIdentifier={true}
label={data.name}
highlighter={this.highlightFilter}
/>,
)
}

Expand All @@ -108,15 +137,19 @@ class Effect extends React.Component {
else if((data = asEffect.select(effect.effect))) {
nodes = nodes.concat(
renderEffectType('select'),
renderFuncCall(data.selector, data.args),
renderFuncCall(data.selector, data.args, this.highlightFilter),
this.renderResult(status, result, error, winner)
)
}

else if((data = asEffect.actionChannel(effect.effect))) {
nodes = nodes.concat(
renderEffectType('actionChannel'),
<SagaValue value={data.action} isIdentifier={true} />,
<SagaValue
value={data.action}
isIdentifier={true}
highlighter={this.highlightFilter}
/>,
this.renderResult(status, result, error, winner)
)
}
Expand Down Expand Up @@ -172,13 +205,13 @@ function renderEffectType(type) {
)
}

function renderFuncCall(fn, args) {
function renderFuncCall(fn, args, highlighter = val => val) {
if(!args.length) {
return <span>{fn.name}()</span>
return <span>{highlighter(fn.name)}()</span>
}

return [
<span>{fn.name}(</span>,
<span>{highlighter(fn.name)}(</span>,
...renderFuncArgs(args),
<span>)</span>
]
Expand All @@ -197,6 +230,7 @@ function renderFuncArgs(args) {

Effect.propTypes = {
effect: PropTypes.object.isRequired,
filter: PropTypes.string.isRequired,
}

export default Effect
20 changes: 11 additions & 9 deletions src/components/JSValue/JSValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
const vnull = <VNull />
const vfuncKeyword = <Keyword>function</Keyword>

function renderValue(value, isIdentifier, label, onlyPrimitive) {
function renderValue(value, isIdentifier, label, onlyPrimitive, highlighter = val => val) {

if(value === null || value === undefined) {
return vnull
Expand All @@ -25,24 +25,24 @@ function renderValue(value, isIdentifier, label, onlyPrimitive) {
const type = typeof value
if(type === 'string') {
if(isIdentifier) {
return <Identifier>{value}</Identifier>
return <Identifier>{highlighter(value)}</Identifier>
} else {
return <VQuoted>'{value}'</VQuoted>
return <VQuoted>'{highlighter(value)}'</VQuoted>
}
}
if(
type === 'symbol' ||
type === 'number' ||
type === 'boolean'
) {
return <VUnquoted>{String(value)}</VUnquoted>
return <VUnquoted>{highlighter(String(value))}</VUnquoted>
}

else if(type === 'function') {
return (
<VFunction>
{vfuncKeyword}
{value.name}()
{highlighter(value.name)}()
</VFunction>
)
}
Expand All @@ -52,7 +52,7 @@ function renderValue(value, isIdentifier, label, onlyPrimitive) {
<Identifier>{label}</Identifier>
}
return (
<JSObject data={value} preview={label} />
<JSObject data={value} preview={label} highlighter={highlighter} />
)
}
}
Expand All @@ -65,17 +65,18 @@ function getObjectSummary(obj) {
)
}

function JSValue({value, isIdentifier, label}) {
return renderValue(value, isIdentifier, label, false)
function JSValue({value, isIdentifier, label, highlighter = val => val}) {
return renderValue(value, isIdentifier, label, false, highlighter)
}

JSValue.propTypes = {
value: PropTypes.any,
isIdentifier: PropTypes.bool,
label: PropTypes.any,
highlighter: PropTypes.func,
}

export function JSObject({data, renderLabel, preview, ignoreLabelClick}) {
export function JSObject({data, renderLabel, preview, ignoreLabelClick, highlighter = val => val}) {
const keys = Object.keys(data)
if(!keys.length) {
return renderLabel ? renderLabel() : <span>'{}'</span>
Expand Down Expand Up @@ -137,6 +138,7 @@ JSObject.propTypes = {
renderLabel: PropTypes.func,
preview: PropTypes.any,
ignoreLabelClick: PropTypes.bool,
highlighter: PropTypes.func,
}

export default JSValue
4 changes: 2 additions & 2 deletions src/components/SagaValue/SagaValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { is, CHANNEL_END } from 'redux-saga/utils'
import JSValue from '../JSValue'
import SagaRef from '../../containers/SagaRef'

export default function SagaValue({value, label, isIdentifier}) {
export default function SagaValue({value, label, isIdentifier, highlighter}) {
if(is.channel(value)) {
return <SagaRef object={value}>{label || 'Channel'}</SagaRef>
}
else if(CHANNEL_END && value === CHANNEL_END) {
return <JSValue value={'END'} isIdentifier={true} />
}
else {
return <JSValue value={value} label={label} isIdentifier={isIdentifier} />
return <JSValue value={value} label={label} isIdentifier={isIdentifier} highlighter={highlighter} />
}
}
11 changes: 8 additions & 3 deletions src/containers/EffectEntry/EffectEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class EffectEntry extends React.Component {
onUnpin = () => this.props.onUnpin(-1)

render() {
const {effect, collapsed, pinned, hasChildren} = this.props
const {effect, collapsed, pinned, hasChildren, passFilter, filter} = this.props

let pinNode
if(!effect.root) {
Expand All @@ -60,6 +60,7 @@ class EffectEntry extends React.Component {
}

return (
passFilter ?
<EffectEntryContainer>
<Row onMouseDown={this.onSelect}>
<Cell>
Expand All @@ -70,7 +71,7 @@ class EffectEntry extends React.Component {
/>
</Cell>
<Cell>
<Effect effect={effect}/>
<Effect effect={effect} filter={filter}/>
</Cell>
</Row>
{
Expand All @@ -88,6 +89,8 @@ class EffectEntry extends React.Component {
)
}
</EffectEntryContainer>
:
null
)
}
}
Expand All @@ -99,6 +102,8 @@ EffectEntry.propTypes = {
collapsed: PropTypes.bool.isRequired,
onCollapse: PropTypes.func.isRequired,
onSelect: PropTypes.func.isRequired,
filter: PropTypes.string.isRequired,
passFilter: PropTypes.bool.isRequired,
// injected by Redux store
effect: PropTypes.object.isRequired,
hasChildren: PropTypes.bool.isRequired,
Expand All @@ -109,7 +114,7 @@ export default connect(
const effect = state.effectsById[effectId]
return {
effect,
hasChildren: state.effectsByParentId[effectId]
hasChildren: !!state.effectsByParentId[effectId]
}
}
)(EffectEntry)
44 changes: 43 additions & 1 deletion src/containers/EffectList/EffectList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import styled, { css } from 'styled-components'
import { connect } from 'react-redux'
import { asEffect } from 'redux-saga/utils'
import { matchCurrentAction } from '../../store/selectors'
import {
KEY_ARROW_DOWN,
Expand All @@ -25,14 +26,51 @@ const cssMatchAction = css`
margin-top: -1px;
`

const EFFECT_TYPES = ['take', 'put', 'fork', 'call', 'cps', 'join', 'cancel']

class EffectList extends React.Component {

state = {
collapsedEffects: {}
}

componentDidMount() {
this.props.setFilterOptions(EFFECT_TYPES, { allCaps: true })
}

isCollapsed = effectId => {
return this.state.collapsedEffects[effectId]
return !!this.state.collapsedEffects[effectId]
}

passFilter = effectId => {
const { filter } = this.props

if (!filter.word && !filter.type) return true

let data = {}, description
const effect = this.props.effectsById[effectId]

if (effect.root) {
data = { type: 'root', name: effect.effect.saga.name }
}

else { // Currently only filters those with string data.name
EFFECT_TYPES.forEach(type => {
if (description = asEffect[type](effect.effect)) {
data = {
type,
name: description.pattern
|| description.channel
|| description.action
|| (description.fn && description.fn.name)
}
}
})
}

return typeof data.name === 'string'
&& data.name.toLowerCase().includes(filter.word.toLowerCase())
&& (!filter.type || data.type === filter.type)
}

collapseEffect = (effectId, collapsed) => {
Expand Down Expand Up @@ -121,6 +159,8 @@ class EffectList extends React.Component {
selected={this.props.selectedEffectId === effectId}
pinned={this.props.pinnedEffectId === effectId}
collapsed={this.isCollapsed(effectId)}
filter={this.props.filter.word}
passFilter={this.passFilter(effectId)}
onCollapse={this.collapseEffect}
onPin={this.props.onPin}
onUnpin={this.props.onUnpin}
Expand Down Expand Up @@ -154,6 +194,8 @@ EffectList.propTypes = {
selectedEffectId: PropTypes.number,
onSelectionChange: PropTypes.func.isRequired,
rootEffectIds: PropTypes.array.isRequired,
filter: PropTypes.object.isRequired,
setFilterOptions: PropTypes.func.isRequired,
// Injected by redux
effectsById: PropTypes.object.isRequired,
effectsByParentId: PropTypes.object.isRequired,
Expand Down
Loading