Bug 1541631 - Part 8: Store breakable-line and breakpoint-position data in the source-actor store. r=jlast

Differential Revision: https://phabricator.services.mozilla.com/D42033

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Logan Smyth 2019-08-15 16:29:44 +00:00
Родитель c4521e96b2
Коммит b5f7346637
23 изменённых файлов: 351 добавлений и 124 удалений

Просмотреть файл

@ -28,8 +28,8 @@ const mockCommandClient = {
}),
evaluateExpressions: async () => {},
getFrameScopes: async () => {},
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
};
describe("getInScopeLine", () => {

Просмотреть файл

@ -33,6 +33,7 @@ import {
} from "../../utils/memoizableAction";
import { fulfilled } from "../../utils/async-value";
import type { ThunkArgs } from "../../actions/types";
import { loadSourceActorBreakpointColumns } from "../source-actors";
async function mapLocations(
generatedLocations: SourceLocation[],
@ -113,7 +114,7 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
return;
}
let results = {};
const results = {};
if (isOriginalId(sourceId)) {
// Explicitly typing ranges is required to work around the following issue
// https://github.com/facebook/flow/issues/5294
@ -139,12 +140,22 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
};
}
const bps = await client.getBreakpointPositions(
getSourceActorsForSource(getState(), generatedSource.id),
range
const actorBps = await Promise.all(
getSourceActorsForSource(getState(), generatedSource.id).map(actor =>
client.getSourceActorBreakpointPositions(actor, range)
)
);
for (const bpLine in bps) {
results[bpLine] = (results[bpLine] || []).concat(bps[bpLine]);
for (const actorPositions of actorBps) {
for (const rangeLine of Object.keys(actorPositions)) {
let columns = actorPositions[parseInt(rangeLine, 10)];
const existing = results[rangeLine];
if (existing) {
columns = [...new Set([...existing, ...columns])];
}
results[rangeLine] = columns;
}
}
}
} else {
@ -152,10 +163,15 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
throw new Error("Line is required for generated sources");
}
results = await client.getBreakpointPositions(
getSourceActorsForSource(getState(), generatedSource.id),
{ start: { line, column: 0 }, end: { line: line + 1, column: 0 } }
const actorColumns = await Promise.all(
getSourceActorsForSource(getState(), generatedSource.id).map(actor =>
dispatch(loadSourceActorBreakpointColumns({ id: actor.id, line }))
)
);
for (const columns of actorColumns) {
results[line] = (results[line] || []).concat(columns);
}
}
let positions = convertToList(results, generatedSource);

Просмотреть файл

@ -18,8 +18,8 @@ describe("breakpointPositions", () => {
const fooContent = createSource("foo", "");
const store = createStore({
getBreakpointPositions: async () => ({ "9": [1] }),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({ "9": [1] }),
getSourceActorBreakableLines: async () => [],
sourceContents: async () => fooContent,
});
@ -63,12 +63,12 @@ describe("breakpointPositions", () => {
let resolve = _ => {};
let count = 0;
const store = createStore({
getBreakpointPositions: () =>
getSourceActorBreakpointPositions: () =>
new Promise(r => {
count++;
resolve = r;
}),
getBreakableLines: async () => [],
getSourceActorBreakableLines: async () => [],
sourceContents: async () => fooContent,
});

Просмотреть файл

@ -17,8 +17,8 @@ import { mockCommandClient } from "../../tests/helpers/mockCommandClient";
function mockClient(positionsResponse = {}) {
return {
...mockCommandClient,
getBreakpointPositions: async () => positionsResponse,
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => positionsResponse,
getSourceActorBreakableLines: async () => [],
};
}

Просмотреть файл

@ -71,8 +71,8 @@ const mockCommandClient = {
}
});
},
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
actorID: "threadActorID",
};
@ -176,7 +176,7 @@ describe("pause", () => {
it("should step over when paused after an await", async () => {
const store = createStore({
...mockCommandClient,
getBreakpointPositions: async () => ({ [2]: [1] }),
getSourceActorBreakpointPositions: async () => ({ [2]: [1] }),
});
const { dispatch, getState } = store;
const mockPauseInfo = createPauseInfo({

Просмотреть файл

@ -5,7 +5,18 @@
// @flow
import type { ThunkArgs } from "./types";
import type { SourceActor } from "../reducers/source-actors";
import {
getSourceActor,
getSourceActorBreakableLines,
getSourceActorBreakpointColumns,
type SourceActorId,
type SourceActor,
} from "../reducers/source-actors";
import {
memoizeableAction,
type MemoizedAction,
} from "../utils/memoizableAction";
import { PROMISE } from "./utils/middleware/promise";
export function insertSourceActor(item: SourceActor) {
return insertSourceActors([item]);
@ -30,3 +41,48 @@ export function removeSourceActors(items: Array<SourceActor>) {
});
};
}
export const loadSourceActorBreakpointColumns: MemoizedAction<
{ id: SourceActorId, line: number },
Array<number>
> = memoizeableAction("loadSourceActorBreakpointColumns", {
createKey: ({ id, line }) => `${id}:${line}`,
getValue: ({ id, line }, { getState }) =>
getSourceActorBreakpointColumns(getState(), id, line),
action: async ({ id, line }, { dispatch, getState, client }) => {
await dispatch({
type: "SET_SOURCE_ACTOR_BREAKPOINT_COLUMNS",
sourceId: id,
line,
[PROMISE]: (async () => {
const positions = await client.getSourceActorBreakpointPositions(
getSourceActor(getState(), id),
{
start: { line, column: 0 },
end: { line: line + 1, column: 0 },
}
);
return positions[line] || [];
})(),
});
},
});
export const loadSourceActorBreakableLines: MemoizedAction<
{ id: SourceActorId },
Array<number>
> = memoizeableAction("loadSourceActorBreakableLines", {
createKey: args => args.id,
getValue: ({ id }, { getState }) =>
getSourceActorBreakableLines(getState(), id),
action: async ({ id }, { dispatch, getState, client }) => {
await dispatch({
type: "SET_SOURCE_ACTOR_BREAKABLE_LINES",
sourceId: id,
[PROMISE]: client.getSourceActorBreakableLines(
getSourceActor(getState(), id)
),
});
},
});

Просмотреть файл

@ -10,6 +10,7 @@ import { setBreakpointPositions } from "../breakpoints/breakpointPositions";
import { union } from "lodash";
import type { Context } from "../../types";
import type { ThunkArgs } from "../../actions/types";
import { loadSourceActorBreakableLines } from "../source-actors";
function calculateBreakableLines(positions) {
const lines = [];
@ -30,11 +31,6 @@ export function setBreakableLines(cx: Context, sourceId: string) {
setBreakpointPositions({ cx, sourceId })
);
breakableLines = calculateBreakableLines(positions);
} else {
breakableLines = await client.getBreakableLines(
getSourceActorsForSource(getState(), sourceId)
);
}
const existingBreakableLines = getBreakableLines(getState(), sourceId);
if (existingBreakableLines) {
@ -42,10 +38,19 @@ export function setBreakableLines(cx: Context, sourceId: string) {
}
dispatch({
type: "SET_BREAKABLE_LINES",
type: "SET_ORIGINAL_BREAKABLE_LINES",
cx,
sourceId,
breakableLines,
});
} else {
const actors = getSourceActorsForSource(getState(), sourceId);
await Promise.all(
actors.map(actor =>
dispatch(loadSourceActorBreakableLines({ id: actor.id }))
)
);
}
};
}

Просмотреть файл

@ -15,7 +15,7 @@ describe("blackbox", () => {
it("should blackbox a source", async () => {
const store = createStore({
blackBox: async () => true,
getBreakableLines: async () => [],
getSourceActorBreakableLines: async () => [],
});
const { dispatch, getState, cx } = store;

Просмотреть файл

@ -61,8 +61,8 @@ describe("loadSourceText", () => {
{
...mockCommandClient,
sourceContents: async () => fooGenContent,
getBreakpointPositions: async () => ({ "1": [0] }),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({ "1": [0] }),
getSourceActorBreakableLines: async () => [],
},
{},
{
@ -156,8 +156,8 @@ describe("loadSourceText", () => {
count++;
resolve = r;
}),
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
});
const id = "foo";
@ -194,8 +194,8 @@ describe("loadSourceText", () => {
count++;
resolve = r;
}),
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
});
const id = "foo";

Просмотреть файл

@ -17,7 +17,7 @@ import {
import readFixture from "./helpers/readFixture";
const { getSymbols, isSymbolsLoading, getFramework } = selectors;
const threadFront = {
const mockCommandClient = {
sourceContents: async ({ source }) => ({
source: sourceTexts[source],
contentType: "text/javascript",
@ -26,8 +26,8 @@ const threadFront = {
evaluate: async expression => ({ result: evaluationResult[expression] }),
evaluateExpressions: async expressions =>
expressions.map(expression => ({ result: evaluationResult[expression] })),
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
};
const sourceMaps = {
@ -56,7 +56,7 @@ describe("ast", () => {
describe("setSymbols", () => {
describe("when the source is loaded", () => {
it("should be able to set symbols", async () => {
const store = createStore(threadFront);
const store = createStore(mockCommandClient);
const { dispatch, getState, cx } = store;
const base = await dispatch(
actions.newGeneratedSource(makeSource("base.js"))
@ -74,7 +74,7 @@ describe("ast", () => {
describe("when the source is not loaded", () => {
it("should return null", async () => {
const { getState, dispatch } = createStore(threadFront);
const { getState, dispatch } = createStore(mockCommandClient);
const base = await dispatch(
actions.newGeneratedSource(makeSource("base.js"))
);
@ -86,7 +86,7 @@ describe("ast", () => {
describe("when there is no source", () => {
it("should return null", async () => {
const { getState } = createStore(threadFront);
const { getState } = createStore(mockCommandClient);
const baseSymbols = getSymbols(getState());
expect(baseSymbols).toEqual(null);
});
@ -94,7 +94,7 @@ describe("ast", () => {
describe("frameworks", () => {
it("should detect react components", async () => {
const store = createStore(threadFront, {}, sourceMaps);
const store = createStore(mockCommandClient, {}, sourceMaps);
const { cx, dispatch, getState } = store;
const genSource = await dispatch(
@ -113,7 +113,7 @@ describe("ast", () => {
});
it("should not give false positive on non react components", async () => {
const store = createStore(threadFront);
const store = createStore(mockCommandClient);
const { cx, dispatch, getState } = store;
const base = await dispatch(
actions.newGeneratedSource(makeSource("base.js"))

Просмотреть файл

@ -37,8 +37,8 @@ const mockThreadFront = {
),
getFrameScopes: async () => {},
sourceContents: () => ({ source: "", contentType: "text/javascript" }),
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
autocomplete: () => {
return new Promise(resolve => {
resolve({

Просмотреть файл

@ -49,6 +49,6 @@ export const mockCommandClient = {
threadFront: async () => {},
getFrameScopes: async () => {},
evaluateExpressions: async () => {},
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
};

Просмотреть файл

@ -27,8 +27,8 @@ const threadFront = {
source: "function foo1() {\n const foo = 5; return foo;\n}",
contentType: "text/javascript",
}),
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
detachWorkers: () => {},
};

Просмотреть файл

@ -49,8 +49,8 @@ function mockClient(bpPos = {}) {
return {
...mockCommandClient,
getBreakpointPositions: async () => bpPos,
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => bpPos,
getSourceActorBreakableLines: async () => [],
};
}

Просмотреть файл

@ -31,8 +31,8 @@ function mockThreadFront(overrides) {
source: "",
contentType: "text/javascript",
}),
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
evaluateExpressions: async () => [],
loadObjectProperties: async () => ({}),
...overrides,

Просмотреть файл

@ -39,8 +39,8 @@ const sources = {
const threadFront = {
sourceContents: async ({ source }) => sources[source],
getBreakpointPositions: async () => ({}),
getBreakableLines: async () => [],
getSourceActorBreakpointPositions: async () => ({}),
getSourceActorBreakableLines: async () => [],
};
describe("project text search", () => {

Просмотреть файл

@ -43,7 +43,7 @@ describe("setProjectDirectoryRoot", () => {
it("should filter sources", async () => {
const store = createStore({
getBreakableLines: async () => [],
getSourceActorBreakableLines: async () => [],
});
const { dispatch, getState, cx } = store;
await dispatch(actions.newGeneratedSource(makeSource("js/scopes.js")));
@ -65,7 +65,7 @@ describe("setProjectDirectoryRoot", () => {
it("should update the child directory ", () => {
const { dispatch, getState, cx } = createStore({
getBreakableLines: async () => [],
getSourceActorBreakableLines: async () => [],
});
dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
dispatch(actions.setProjectDirectoryRoot(cx, "example.com/foo/bar"));
@ -74,7 +74,7 @@ describe("setProjectDirectoryRoot", () => {
it("should update the child directory when domain name is Webpack://", () => {
const { dispatch, getState, cx } = createStore({
getBreakableLines: async () => [],
getSourceActorBreakableLines: async () => [],
});
dispatch(actions.setProjectDirectoryRoot(cx, "webpack://"));
dispatch(actions.setProjectDirectoryRoot(cx, "webpack:///app"));

Просмотреть файл

@ -77,7 +77,7 @@ export type SourceAction =
+tabs: any,
|}
| {|
type: "SET_BREAKABLE_LINES",
type: "SET_ORIGINAL_BREAKABLE_LINES",
+cx: Context,
breakableLines: number[],
sourceId: string,

Просмотреть файл

@ -4,7 +4,11 @@
// @flow
import type { SourceActor } from "../../reducers/source-actors.js";
import { type PromiseAction } from "../utils/middleware/promise";
import type {
SourceActorId,
SourceActor,
} from "../../reducers/source-actors.js";
export type SourceActorsInsertAction = {|
type: "INSERT_SOURCE_ACTORS",
@ -15,6 +19,25 @@ export type SourceActorsRemoveAction = {|
items: Array<SourceActor>,
|};
export type SourceActorBreakpointColumnsAction = PromiseAction<
{|
type: "SET_SOURCE_ACTOR_BREAKPOINT_COLUMNS",
sourceId: SourceActorId,
line: number,
|},
Array<number>
>;
export type SourceActorBreakableLinesAction = PromiseAction<
{|
type: "SET_SOURCE_ACTOR_BREAKABLE_LINES",
sourceId: SourceActorId,
|},
Array<number>
>;
export type SourceActorAction =
| SourceActorsInsertAction
| SourceActorsRemoveAction;
| SourceActorsRemoveAction
| SourceActorBreakpointColumnsAction
| SourceActorBreakableLinesAction;

Просмотреть файл

@ -6,7 +6,6 @@
import { fromPairs, toPairs } from "lodash";
import { executeSoon } from "../../../utils/DevToolsUtils";
import type { ThunkArgs } from "../../types";
type BasePromiseAction = {|
@ -30,7 +29,25 @@ export type ErrorPromiseAction = {|
+error: any,
|};
export type PromiseAction<Action, Value = any> =
import {
pending,
rejected,
fulfilled,
type AsyncValue,
} from "../../../utils/async-value";
export function asyncActionAsValue<T>(
action: PromiseAction<mixed, T>
): AsyncValue<T> {
if (action.status === "start") {
return pending();
}
if (action.status === "error") {
return rejected(action.error);
}
return fulfilled(action.value);
}
export type PromiseAction<+Action, Value = any> =
// | {| ...Action, "@@dispatch/promise": Promise<Object> |}
| {|
...BasePromiseAction,

Просмотреть файл

@ -449,33 +449,19 @@ function getMainThread() {
return threadFront.actor;
}
async function getBreakpointPositions(
actors: Array<SourceActor>,
range: ?Range
): Promise<{ [string]: number[] }> {
const sourcePositions = {};
for (const { thread, actor } of actors) {
async function getSourceActorBreakpointPositions(
{ thread, actor }: SourceActor,
range: Range
): Promise<{ [number]: number[] }> {
const sourceThreadFront = lookupThreadFront(thread);
const sourceFront = sourceThreadFront.source({ actor });
const positions = await sourceFront.getBreakpointPositionsCompressed(range);
for (const line of Object.keys(positions)) {
let columns = positions[line];
const existing = sourcePositions[line];
if (existing) {
columns = [...new Set([...existing, ...columns])];
return sourceFront.getBreakpointPositionsCompressed(range);
}
sourcePositions[line] = columns;
}
}
return sourcePositions;
}
async function getBreakableLines(actors: Array<SourceActor>) {
let lines = [];
for (const { thread, actor } of actors) {
async function getSourceActorBreakableLines({
thread,
actor,
}: SourceActor): Promise<Array<number>> {
const sourceThreadFront = lookupThreadFront(thread);
const sourceFront = sourceThreadFront.source({ actor });
let actorLines = [];
@ -494,10 +480,7 @@ async function getBreakableLines(actors: Array<SourceActor>) {
}
}
lines = [...new Set([...lines, ...actorLines])];
}
return lines;
return actorLines;
}
const clientCommands = {
@ -517,8 +500,8 @@ const clientCommands = {
breakOnNext,
sourceContents,
getSourceForActor,
getBreakpointPositions,
getBreakableLines,
getSourceActorBreakpointPositions,
getSourceActorBreakableLines,
hasBreakpoint,
setBreakpoint,
setXHRBreakpoint,

Просмотреть файл

@ -6,20 +6,35 @@
import type { Action } from "../actions/types";
import type { SourceId, ThreadId } from "../types";
import {
asSettled,
type AsyncValue,
type SettledValue,
} from "../utils/async-value";
import {
createInitial,
insertResources,
updateResources,
removeResources,
hasResource,
getResource,
getMappedResource,
makeWeakQuery,
makeIdQuery,
makeReduceAllQuery,
type Resource,
type ResourceState,
type WeakQuery,
type IdQuery,
type ReduceAllQuery,
} from "../utils/resource";
import { asyncActionAsValue } from "../actions/utils/middleware/promise";
import type {
SourceActorBreakpointColumnsAction,
SourceActorBreakableLinesAction,
} from "../actions/types/SourceActorAction";
export opaque type SourceActorId: string = string;
export type SourceActor = {|
+id: SourceActorId,
@ -47,6 +62,12 @@ export type SourceActor = {|
type SourceActorResource = Resource<{
...SourceActor,
// The list of breakpoint positions on each line of the file.
breakpointPositions: Map<number, AsyncValue<Array<number>>>,
// The list of lines that contain breakpoints.
breakableLines: AsyncValue<Array<number>> | null,
}>;
export type SourceActorsState = ResourceState<SourceActorResource>;
export type SourceActorOuterState = { sourceActors: SourceActorsState };
@ -60,7 +81,14 @@ export default function update(
switch (action.type) {
case "INSERT_SOURCE_ACTORS": {
const { items } = action;
state = insertResources(state, items);
state = insertResources(
state,
items.map(item => ({
...item,
breakpointPositions: new Map(),
breakableLines: null,
}))
);
break;
}
case "REMOVE_SOURCE_ACTORS": {
@ -73,13 +101,58 @@ export default function update(
state = initial;
break;
}
case "SET_SOURCE_ACTOR_BREAKPOINT_COLUMNS":
state = updateBreakpointColumns(state, action);
break;
case "SET_SOURCE_ACTOR_BREAKABLE_LINES":
state = updateBreakableLines(state, action);
break;
}
return state;
}
export function resourceAsSourceActor(r: SourceActorResource): SourceActor {
return r;
function updateBreakpointColumns(
state: SourceActorsState,
action: SourceActorBreakpointColumnsAction
): SourceActorsState {
const { sourceId, line } = action;
const value = asyncActionAsValue(action);
if (!hasResource(state, sourceId)) {
return state;
}
const breakpointPositions = new Map(
getResource(state, sourceId).breakpointPositions
);
breakpointPositions.set(line, value);
return updateResources(state, [{ id: sourceId, breakpointPositions }]);
}
function updateBreakableLines(
state: SourceActorsState,
action: SourceActorBreakableLinesAction
): SourceActorsState {
const value = asyncActionAsValue(action);
const { sourceId } = action;
if (!hasResource(state, sourceId)) {
return state;
}
return updateResources(state, [{ id: sourceId, breakableLines: value }]);
}
export function resourceAsSourceActor({
breakpointPositions,
breakableLines,
...sourceActor
}: SourceActorResource): SourceActor {
return sourceActor;
}
// Because we are using an opaque type for our source actor IDs, these
@ -101,7 +174,7 @@ export function getSourceActor(
state: SourceActorOuterState,
id: SourceActorId
): SourceActor {
return getResource(state.sourceActors, id);
return getMappedResource(state.sourceActors, id, resourceAsSourceActor);
}
/**
@ -166,3 +239,40 @@ export function getThreadsBySource(
): { [SourceId]: Array<ThreadId> } {
return queryThreadsBySourceObject(state.sourceActors);
}
export function getSourceActorBreakableLines(
state: SourceActorOuterState,
id: SourceActorId
): SettledValue<Array<number>> | null {
const { breakableLines } = getResource(state.sourceActors, id);
return asSettled(breakableLines);
}
export function getSourceActorBreakpointColumns(
state: SourceActorOuterState,
id: SourceActorId,
line: number
): SettledValue<Array<number>> | null {
const { breakpointPositions } = getResource(state.sourceActors, id);
return asSettled(breakpointPositions.get(line) || null);
}
export const getBreakableLinesForSourceActors: WeakQuery<
SourceActorResource,
Array<SourceActorId>,
Array<number>
> = makeWeakQuery({
filter: (state, ids) => ids,
map: ({ breakableLines }) => breakableLines,
reduce: items =>
Array.from(
items.reduce((acc, item) => {
if (item && item.state === "fulfilled") {
acc = acc.concat(item.value);
}
return acc;
}, [])
),
});

Просмотреть файл

@ -47,6 +47,7 @@ import {
getSourceActor,
getSourceActors,
getThreadsBySource,
getBreakableLinesForSourceActors,
type SourceActorId,
type SourceActorOuterState,
} from "./source-actors";
@ -216,7 +217,7 @@ function update(
case "SET_PROJECT_DIRECTORY_ROOT":
return updateProjectDirectoryRoot(state, action.url);
case "SET_BREAKABLE_LINES": {
case "SET_ORIGINAL_BREAKABLE_LINES": {
const { breakableLines, sourceId } = action;
return {
...state,
@ -964,18 +965,34 @@ export function getBreakpointPositionsForLocation(
return findPosition(positions, location);
}
export function getBreakableLines(state: OuterState, sourceId: string) {
export function getBreakableLines(
state: OuterState & SourceActorOuterState,
sourceId: string
): ?Array<number> {
if (!sourceId) {
return null;
}
const source = getSource(state, sourceId);
if (!source) {
return null;
}
if (isOriginalSource(source)) {
return state.sources.breakableLines[sourceId];
}
// We pull generated file breakable lines directly from the source actors
// so that breakable lines can be added as new source actors on HTML loads.
return getBreakableLinesForSourceActors(
state.sourceActors,
state.sources.actors[sourceId]
);
}
export const getSelectedBreakableLines: Selector<Set<number>> = createSelector(
state => {
const sourceId = getSelectedSourceId(state);
return sourceId && state.sources.breakableLines[sourceId];
return sourceId && getBreakableLines(state, sourceId);
},
breakableLines => new Set(breakableLines || [])
);