Skip to content

Commit

Permalink
Component stack and dependency graphs work more like Call Stack
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn committed Jul 17, 2024
1 parent c0b288e commit c10515b
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 50 deletions.
5 changes: 1 addition & 4 deletions packages/replay-next/components/console/StackRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { CallStack, Frame, FrameId } from "@replayio/protocol";
import React, { Suspense, memo, useContext } from "react";
import { Suspense, memo } from "react";

import Loader from "replay-next/components/Loader";
import { ReplayClientContext } from "shared/client/ReplayClientContext";

import Source from "./Source";
import styles from "./StackRenderer.module.css";
Expand All @@ -26,8 +25,6 @@ function StackRenderer({ frames, stack }: { frames: Frame[]; stack: CallStack })
}

function StackFrameRenderer({ frameId, frames }: { frameId: FrameId; frames: Frame[] }) {
const client = useContext(ReplayClientContext);

const frame = frames.find(frame => frame.frameId === frameId);
if (frame == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { ExecutionPoint } from "@replayio/protocol";
import { ExecutionPoint, Location } from "@replayio/protocol";
import { Suspense, useContext } from "react";

import { PanelLoader } from "replay-next/components/PanelLoader";
import { sourcesByIdCache, sourcesByUrlCache } from "replay-next/src/suspense/SourcesCache";
import { getSourceToDisplayForUrl } from "replay-next/src/utils/sources";
import { suspendInParallel } from "replay-next/src/utils/suspense";
import { ReplayClientContext } from "shared/client/ReplayClientContext";
import { DependencyGraphMode } from "shared/client/types";
import { depGraphCache } from "ui/suspense/depGraphCache";
Expand All @@ -25,27 +28,48 @@ export function DependencyGraph(props: Props) {
function DependencyGraphSuspends({ mode, point }: Props) {
const replayClient = useContext(ReplayClientContext);

const depGraphValue = depGraphCache.read(replayClient, point ?? null, mode ?? null);
const [depGraphValue, sourcesById, sourcesByUrl] = suspendInParallel(
() => depGraphCache.read(replayClient, point ?? null, mode ?? null),
() => sourcesByIdCache.read(replayClient),
() => sourcesByUrlCache.read(replayClient)
);

const valueDescending = depGraphValue?.slice().reverse();

return (
<div className={styles.Panel}>
{valueDescending?.map((entry, index) => (
<Item
key={index}
isCurrent={index === 0}
location={null}
name={entry.code}
timeStampedPoint={
entry.point != null && entry.time != null
? {
point: entry.point,
time: entry.time,
}
: null
{valueDescending?.map((entry, index) => {
let location: Location | null = null;
if ("calleeLocation" in entry && entry.calleeLocation != null) {
const source = getSourceToDisplayForUrl(
sourcesById,
sourcesByUrl,
entry.calleeLocation.url
);
if (source) {
location = {
...entry.calleeLocation,
sourceId: source.sourceId,
};
}
/>
))}
}

return (
<Item
key={index}
location={location}
name={entry.code}
timeStampedPoint={
entry.point != null && entry.time != null
? {
point: entry.point,
time: entry.time,
}
: null
}
/>
);
})}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@
background-color: var(--theme-selection-background-hover);
cursor: pointer;
}
.Item[data-selected] {
background-color: var(--theme-selection-background);
color: white;
}
.Item[data-disabled] {
color: var(--color-dim);
}
.Item[data-disabled]:hover {
background-color: transparent;
cursor: default;
}
.Item[data-selected],
.Item[data-selected]:hover {
background-color: var(--theme-selection-background);
color: white;
}

.NameColumn {
overflow: hidden;
Expand All @@ -41,5 +42,5 @@
text-overflow: ellipsis;
}
.Item[data-selected] .LocationColumn {
display: none;
color: white;
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
import { TimeStampedPoint } from "@replayio/protocol";
import { Location, TimeStampedPoint } from "@replayio/protocol";

import { selectDependencyGraphNode } from "devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/selectDependencyGraphNode";
import { useIsCurrentItem } from "devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/useIsCurrentItem";
import { getRawSourceURL } from "devtools/client/debugger/src/utils/source";
import { getURL } from "devtools/client/debugger/src/utils/sources-tree";
import { formatTimestamp } from "replay-next/src/utils/time";
import { seek } from "ui/actions/timeline";
import { useAppDispatch } from "ui/setup/hooks";
import { LocationWithUrl } from "ui/suspense/depGraphCache";

import styles from "./Item.module.css";

export function Item({
isCurrent,
name,
location,
timeStampedPoint,
}: {
name: string;
isCurrent?: boolean;
location: LocationWithUrl | null;
location: Location | LocationWithUrl | null;
timeStampedPoint: TimeStampedPoint | null;
}) {
const dispatch = useAppDispatch();

const onClick = () => {
timeStampedPoint &&
dispatch(
seek({
executionPoint: timeStampedPoint.point,
openSource: true,
time: timeStampedPoint.time,
})
);
const isCurrent = useIsCurrentItem(timeStampedPoint, location);

const isDisabled = !location || !timeStampedPoint;

const onClick = async () => {
if (!location || !timeStampedPoint) {
return;
}

dispatch(selectDependencyGraphNode(location, timeStampedPoint));
};

let source = null;
if (location) {
if (location && "url" in location) {
source = `${getRawSourceURL(getURL(location).filename)}:${location.line}`;
}

return (
<div
className={styles.Item}
data-selected={isCurrent || undefined}
data-disabled={!timeStampedPoint || undefined}
data-disabled={isDisabled || undefined}
onClick={isCurrent ? undefined : onClick}
title={location?.url}
>
<div className={styles.NameColumn}>{name}</div>
{timeStampedPoint && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,15 @@ function ReactComponentStackSuspends({ timeStampedPoint }: Props) {
return <Message>No frames to display</Message>;
}

const firstItem = reactStackValue[0];
const leafNode = reactStackValue[0];

return (
<>
{firstItem && (
{leafNode && (
<Item
isCurrent
location={firstItem.componentLocation}
name={firstItem.componentName}
timeStampedPoint={firstItem}
location={leafNode.componentLocation}
name={leafNode.componentName}
timeStampedPoint={leafNode}
/>
)}
{reactStackValue?.map((entry, index) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Location, TimeStampedPoint } from "@replayio/protocol";

import { selectLocation } from "devtools/client/debugger/src/actions/sources";
import { frameSelected, getThreadContext } from "devtools/client/debugger/src/selectors";
import { pauseIdCache } from "replay-next/src/suspense/PauseCache";
import { UIThunkAction } from "ui/actions";

export function selectDependencyGraphNode(
location: Location,
timeStampedPoint: TimeStampedPoint
): UIThunkAction {
return async (dispatch, getState, { replayClient }) => {
const pauseId = await pauseIdCache.readAsync(
replayClient,
timeStampedPoint.point,
timeStampedPoint.time
);

const context = getThreadContext(getState());

// TODO Is this legit?
const frameId = "0";

dispatch(frameSelected({ cx: context, pauseId, frameId }));
dispatch(selectLocation(context, location));
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Location, TimeStampedPoint } from "@replayio/protocol";

import { getSelectedFrameId } from "devtools/client/debugger/src/selectors";
import { comparePosition } from "devtools/client/debugger/src/utils/location";
import { getPointAndTimeForPauseId } from "replay-next/src/suspense/PauseCache";
import { getSelectedLocation } from "ui/reducers/sources";
import { useAppSelector } from "ui/setup/hooks";

export function useIsCurrentItem(
timeStampedPoint: TimeStampedPoint | null,
location: Location | null
) {
const { pauseId } = useAppSelector(getSelectedFrameId) ?? {};
const selectedLocation = useAppSelector(getSelectedLocation);

const [selectedExecutionPoint] = pauseId ? getPointAndTimeForPauseId(pauseId) : [];

const isMatchingExecutionPoint =
selectedExecutionPoint != null && timeStampedPoint?.point === selectedExecutionPoint;
const isMatchingLocation =
location && selectedLocation && comparePosition(location, selectedLocation);

return isMatchingExecutionPoint && isMatchingLocation;
}
2 changes: 1 addition & 1 deletion src/devtools/client/debugger/src/utils/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import type { Location, SourceLocation } from "@replayio/protocol";
import sortBy from "lodash/sortBy";

export function comparePosition(a: Location, b: Location) {
export function comparePosition(a: Partial<Location>, b: Partial<Location>) {
return a && b && a.line == b.line && a.column == b.column;
}

Expand Down

0 comments on commit c10515b

Please sign in to comment.