mirror of
https://github.com/davidmarkclements/0x.git
synced 2026-01-25 14:47:55 +00:00
201 lines
5.6 KiB
JavaScript
201 lines
5.6 KiB
JavaScript
'use strict'
|
|
|
|
module.exports = createActions
|
|
|
|
function createActions ({flamegraph, state}, emit) {
|
|
state.typeFilters.bgs = state.typeFilters.unhighlighted
|
|
|
|
const unmergedTags = tagNodesWithIds(state.trees.unmerged)
|
|
const mergedTags = tagNodesWithIds(state.trees.merged)
|
|
|
|
return {
|
|
focusNode,
|
|
search, control, zoom, typeFilters,
|
|
pushState, jumpToState
|
|
}
|
|
|
|
function focusNode () {
|
|
const save = pushState()
|
|
return (node) => {
|
|
const { merged } = state.control
|
|
const { nodesToIds } = merged ? mergedTags : unmergedTags
|
|
|
|
state.focusedNodeId = nodesToIds.get(node)
|
|
save()
|
|
}
|
|
}
|
|
|
|
function search () {
|
|
return ({type, value}) => {
|
|
if (type === 'key') {
|
|
if (!value) return flamegraph.clear()
|
|
flamegraph.search(value, 'cyan')
|
|
}
|
|
}
|
|
}
|
|
|
|
function highlightTypeFilters () {
|
|
return Object.assign(
|
|
{},
|
|
state.typeFilters.highlighted,
|
|
[...Array.from(state.typeFilters.exclude),
|
|
(state.typeFilters.enableInlinable ? '' : 'inlinable')
|
|
].reduce((o, k) => {
|
|
o[k] = state.typeFilters.unhighlighted[k]
|
|
return o
|
|
}, {}))
|
|
}
|
|
|
|
function control () {
|
|
return ({type}) => {
|
|
switch (type) {
|
|
case 'tiers':
|
|
state.control.tiers = !state.control.tiers
|
|
flamegraph.tiers(state.control.tiers)
|
|
state.typeFilters.bgs = state.control.tiers
|
|
? highlightTypeFilters()
|
|
: state.typeFilters.unhighlighted
|
|
emit(state)
|
|
return
|
|
case 'view':
|
|
state.control.merged = !state.control.merged
|
|
state.typeFilters.enableInlinable = !state.control.merged
|
|
state.key.enableOptUnopt = !state.control.merged
|
|
state.typeFilters.bgs = state.control.tiers
|
|
? highlightTypeFilters()
|
|
: state.typeFilters.unhighlighted
|
|
emit(state)
|
|
if (state.control.merged) flamegraph.renderTree(state.trees.merged)
|
|
else flamegraph.renderTree(state.trees.unmerged)
|
|
return
|
|
case 'optimized':
|
|
state.control.optimized = !state.control.optimized
|
|
// Deactivate the unoptimized button--d3-fg will auto clear its colours
|
|
state.control.unoptimized = false
|
|
emit(state)
|
|
if (!state.control.optimized) return flamegraph.clear('yellow')
|
|
flamegraph.search(RegExp('^\\*'), 'yellow')
|
|
return
|
|
case 'not-optimized':
|
|
state.control.unoptimized = !state.control.unoptimized
|
|
// Deactivate the optimized button--d3-fg will auto clear its colours
|
|
state.control.optimized = false
|
|
emit(state)
|
|
if (!state.control.unoptimized) return flamegraph.clear('lime')
|
|
flamegraph.search(/^~/, 'lime')
|
|
}
|
|
}
|
|
}
|
|
|
|
function zoom () {
|
|
var zoomLevel = 1
|
|
return ({type}) => {
|
|
switch (type) {
|
|
case 'in':
|
|
zoomLevel += 0.3
|
|
// Some ludicrous max just in case
|
|
if (zoomLevel > 10) zoomLevel = 10
|
|
flamegraph.setGraphZoom(zoomLevel)
|
|
return
|
|
case 'out':
|
|
zoomLevel -= 0.3
|
|
if (zoomLevel < 0.1) zoomLevel = 0.1
|
|
flamegraph.setGraphZoom(zoomLevel)
|
|
}
|
|
}
|
|
}
|
|
|
|
function typeFilters () {
|
|
const save = pushState()
|
|
return ({name}) => {
|
|
const checked = state.typeFilters.exclude.has(name)
|
|
if (checked) {
|
|
flamegraph.typeShow(name)
|
|
state.typeFilters.exclude.delete(name)
|
|
} else {
|
|
flamegraph.typeHide(name)
|
|
state.typeFilters.exclude.add(name)
|
|
}
|
|
if (state.control.tiers) state.typeFilters.bgs = highlightTypeFilters()
|
|
save()
|
|
emit(state)
|
|
}
|
|
}
|
|
|
|
function pushState () {
|
|
return () => {
|
|
const { merged } = state.control
|
|
const excludeTypes = Array.from(state.typeFilters.exclude)
|
|
const historyState = {
|
|
merged,
|
|
nodeId: state.focusedNodeId,
|
|
excludeTypes
|
|
}
|
|
window.history.pushState(historyState, '', `#${stringifyHistoryState(historyState)}`)
|
|
}
|
|
}
|
|
|
|
// Jump to a state based on a history entry.
|
|
function jumpToState () {
|
|
return ({merged, nodeId, excludeTypes}) => {
|
|
state.focusedNodeId = nodeId
|
|
state.control.merged = merged
|
|
state.typeFilters.enableInlinable = !merged
|
|
state.key.enableOptUnopt = !merged
|
|
|
|
// Diff type exclude state to reach the one described by the entry
|
|
const oldExclude = state.typeFilters.exclude
|
|
const newExclude = new Set(excludeTypes)
|
|
oldExclude.forEach((name) => {
|
|
if (!newExclude.has(name)) {
|
|
flamegraph.typeShow(name)
|
|
}
|
|
})
|
|
newExclude.forEach((name) => {
|
|
if (!oldExclude.has(name)) {
|
|
flamegraph.typeHide(name)
|
|
}
|
|
})
|
|
state.typeFilters.exclude = newExclude
|
|
state.typeFilters.bgs = state.control.tiers
|
|
? highlightTypeFilters()
|
|
: state.typeFilters.unhighlighted
|
|
|
|
flamegraph.renderTree(merged ? state.trees.merged : state.trees.unmerged)
|
|
const { idsToNodes } = merged ? mergedTags : unmergedTags
|
|
flamegraph.zoom(idsToNodes.get(nodeId))
|
|
|
|
emit(state)
|
|
}
|
|
}
|
|
}
|
|
|
|
// This just uses incrementing IDs but it will only
|
|
// be used for a single dataset, and it's deterministic enough for that
|
|
function tagNodesWithIds (data) {
|
|
let id = 0
|
|
const idsToNodes = new Map()
|
|
const nodesToIds = new Map()
|
|
tagNodes(data)
|
|
|
|
return {
|
|
idsToNodes,
|
|
nodesToIds
|
|
}
|
|
|
|
function tag (node) {
|
|
idsToNodes.set(id, node)
|
|
nodesToIds.set(node, id)
|
|
id++
|
|
}
|
|
function tagNodes (node) {
|
|
tag(node)
|
|
if (node.children) node.children.forEach(tagNodes)
|
|
}
|
|
}
|
|
|
|
function stringifyHistoryState (state) {
|
|
// Just use JSON I guess
|
|
return JSON.stringify(state)
|
|
}
|