123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import {FlamegraphFrame} from '../flamegraphFrame';
- export type DirectionX = 'left' | 'right';
- export type DirectionY = 'up' | 'down';
- export type Direction = DirectionY | DirectionX;
- /**
- * selectNearestFrame walks a FlamegraphFrame tree the direction specified and returns the nearest
- * FlamegraphFrame relative to the starting node.
- */
- export function selectNearestFrame(frame: FlamegraphFrame, direction: Direction) {
- if (direction === 'up') {
- const parent = frame.parent;
- // if there is an immediate parent, goto parent
- if (parent) {
- return parent;
- }
- // if there is no parent, we attempt to move left to the next stack
- const leftSibling = getSibling(frame, 'left');
- if (leftSibling) {
- return leftSibling;
- }
- return frame;
- }
- if (direction === 'down') {
- // if there is an immediate child, goto child
- const child = frame.children?.[0];
- if (child) {
- return child;
- }
- // if there is no child, we attempt to move right to the next stack
- const sibling = scanForNearestSibling(frame, 'right');
- if (sibling) {
- return sibling;
- }
- return frame;
- }
- // scan for the nearest sibling in either the left or right direction
- // this function will walk us up the tree to a new branch
- const sibling = scanForNearestSibling(frame, direction);
- // if we find an adjacent sibling we attempt to walk down to a depth that
- // matches our targetDepth
- if (sibling) {
- const targetDepth = frame.depth;
- const nodeWithDepth = scanForNearestFrameWithDepth(sibling, targetDepth, direction);
- if (nodeWithDepth) {
- return nodeWithDepth;
- }
- }
- return frame;
- }
- /**
- * scanForNearestSibling will walk up a branch looking for an adjacent sibling
- */
- function scanForNearestSibling(frame: FlamegraphFrame, directionX: DirectionX) {
- let node = frame;
- while (node) {
- const sibling = getSibling(node, directionX);
- if (sibling) {
- return sibling;
- }
- if (!node.parent) {
- break;
- }
- node = node.parent;
- }
- return frame;
- }
- /**
- * scanForNearestFrameWithDepth will walk down a FlamegraphFrame looking for a target depth.
- * it will follow either the furthest left or right branch based on direction.
- * if target depth is not found, we return the frame with the max depth along that branch.
- */
- function scanForNearestFrameWithDepth(
- frame: FlamegraphFrame,
- depth: number,
- directionX: DirectionX
- ) {
- let node = frame;
- let nextNode = node.children[directionX === 'right' ? 0 : node.children.length - 1];
- while (node && nextNode) {
- if (node.depth === depth) {
- return node;
- }
- nextNode = node.children[directionX === 'right' ? 0 : node.children.length - 1];
- if (nextNode) {
- node = nextNode;
- }
- }
- return node;
- }
- function getSibling(frame: FlamegraphFrame, directionX: DirectionX) {
- const parent = frame.parent;
- if (!parent) {
- return null;
- }
- // get the index of the current frame relative to its siblings
- const indexOfFrame = parent.children.indexOf(frame);
- // this should never happen, it would make our data structure invalid
- if (indexOfFrame === -1) {
- throw Error('frame.parent.children does not include current frame');
- }
- if (directionX === 'right') {
- const hasRightSiblings = indexOfFrame < parent.children.length - 1;
- if (hasRightSiblings) {
- return parent.children[indexOfFrame + 1];
- }
- return null;
- }
- const hasLeftSiblings = indexOfFrame > 0;
- if (hasLeftSiblings) {
- return parent.children[indexOfFrame - 1];
- }
- return null;
- }
|