зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 8 changesets (bug 1541631) for permafailing in debugger/cold-open.js CLOSED TREE
Backed out changeset bc99fb7a9d12 (bug 1541631) Backed out changeset 696e24030686 (bug 1541631) Backed out changeset 6db731f26958 (bug 1541631) Backed out changeset 9f928da98d32 (bug 1541631) Backed out changeset 53b83b8e37e0 (bug 1541631) Backed out changeset 1d7a76a1fac8 (bug 1541631) Backed out changeset 4f94c700f977 (bug 1541631) Backed out changeset 41f6c078e950 (bug 1541631) --HG-- rename : devtools/client/debugger/src/actions/tests/helpers/mockCommandClient.js => devtools/client/debugger/src/actions/tests/helpers/threadFront.js
This commit is contained in:
Родитель
5e47b0f499
Коммит
5759dc0940
|
@ -32,7 +32,10 @@ function getOutOfScopeLines(outOfScopeLocations: ?(AstLocation[])) {
|
|||
}
|
||||
|
||||
async function getInScopeLines(cx, location, { dispatch, getState, parser }) {
|
||||
const source = getSourceWithContent(getState(), location.sourceId);
|
||||
const { source, content } = getSourceWithContent(
|
||||
getState(),
|
||||
location.sourceId
|
||||
);
|
||||
|
||||
let locations = null;
|
||||
if (location.line && source && !source.isWasm) {
|
||||
|
@ -44,9 +47,7 @@ async function getInScopeLines(cx, location, { dispatch, getState, parser }) {
|
|||
|
||||
const linesOutOfScope = getOutOfScopeLines(locations);
|
||||
const sourceNumLines =
|
||||
!source.content || !isFulfilled(source.content)
|
||||
? 0
|
||||
: getSourceLineCount(source.content.value);
|
||||
!content || !isFulfilled(content) ? 0 : getSourceLineCount(content.value);
|
||||
|
||||
const sourceLines = range(1, sourceNumLines + 1);
|
||||
|
||||
|
|
|
@ -21,20 +21,20 @@ const sourceTexts = {
|
|||
"scopes.js": readFixture("scopes.js"),
|
||||
};
|
||||
|
||||
const mockCommandClient = {
|
||||
const threadFront = {
|
||||
sourceContents: async ({ source }) => ({
|
||||
source: sourceTexts[source],
|
||||
contentType: "text/javascript",
|
||||
}),
|
||||
evaluateExpressions: async () => {},
|
||||
getFrameScopes: async () => {},
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
};
|
||||
|
||||
describe("getInScopeLine", () => {
|
||||
it("with selected line", async () => {
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(threadFront);
|
||||
const { dispatch, getState } = store;
|
||||
const source = makeMockSource("scopes.js", "scopes.js");
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import { uniqBy, zip } from "lodash";
|
|||
import {
|
||||
getSource,
|
||||
getSourceFromId,
|
||||
hasBreakpointPositions,
|
||||
hasBreakpointPositionsForLine,
|
||||
getBreakpointPositionsForSource,
|
||||
getSourceActorsForSource,
|
||||
} from "../../selectors";
|
||||
|
@ -31,9 +33,7 @@ import {
|
|||
memoizeableAction,
|
||||
type MemoizedAction,
|
||||
} 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[],
|
||||
|
@ -114,7 +114,7 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
|
|||
return;
|
||||
}
|
||||
|
||||
const results = {};
|
||||
let results = {};
|
||||
if (isOriginalId(sourceId)) {
|
||||
// Explicitly typing ranges is required to work around the following issue
|
||||
// https://github.com/facebook/flow/issues/5294
|
||||
|
@ -140,22 +140,12 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
|
|||
};
|
||||
}
|
||||
|
||||
const actorBps = await Promise.all(
|
||||
getSourceActorsForSource(getState(), generatedSource.id).map(actor =>
|
||||
client.getSourceActorBreakpointPositions(actor, range)
|
||||
)
|
||||
const bps = await client.getBreakpointPositions(
|
||||
getSourceActorsForSource(getState(), generatedSource.id),
|
||||
range
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
for (const bpLine in bps) {
|
||||
results[bpLine] = (results[bpLine] || []).concat(bps[bpLine]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -163,15 +153,10 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
|
|||
throw new Error("Line is required for generated sources");
|
||||
}
|
||||
|
||||
const actorColumns = await Promise.all(
|
||||
getSourceActorsForSource(getState(), generatedSource.id).map(actor =>
|
||||
dispatch(loadSourceActorBreakpointColumns({ id: actor.id, line }))
|
||||
)
|
||||
results = await client.getBreakpointPositions(
|
||||
getSourceActorsForSource(getState(), generatedSource.id),
|
||||
{ start: { line, column: 0 }, end: { line: line + 1, column: 0 } }
|
||||
);
|
||||
|
||||
for (const columns of actorColumns) {
|
||||
results[line] = (results[line] || []).concat(columns);
|
||||
}
|
||||
}
|
||||
|
||||
let positions = convertToList(results, generatedSource);
|
||||
|
@ -193,6 +178,8 @@ async function _setBreakpointPositions(cx, sourceId, line, thunkArgs) {
|
|||
source: source,
|
||||
positions,
|
||||
});
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
function generatedSourceActorKey(state, sourceId) {
|
||||
|
@ -212,20 +199,12 @@ export const setBreakpointPositions: MemoizedAction<
|
|||
{ cx: Context, sourceId: string, line?: number },
|
||||
?BreakpointPositions
|
||||
> = memoizeableAction("setBreakpointPositions", {
|
||||
getValue: ({ sourceId, line }, { getState }) => {
|
||||
const positions = getBreakpointPositionsForSource(getState(), sourceId);
|
||||
if (!positions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isGeneratedId(sourceId) && line && !positions[line]) {
|
||||
// We always return the full position dataset, but if a given line is
|
||||
// not available, we treat the whole set as loading.
|
||||
return null;
|
||||
}
|
||||
|
||||
return fulfilled(positions);
|
||||
},
|
||||
hasValue: ({ sourceId, line }, { getState }) =>
|
||||
isGeneratedId(sourceId) && line
|
||||
? hasBreakpointPositionsForLine(getState(), sourceId, line)
|
||||
: hasBreakpointPositions(getState(), sourceId),
|
||||
getValue: ({ sourceId, line }, { getState }) =>
|
||||
getBreakpointPositionsForSource(getState(), sourceId),
|
||||
createKey({ sourceId, line }, { getState }) {
|
||||
const key = generatedSourceActorKey(getState(), sourceId);
|
||||
return isGeneratedId(sourceId) && line ? `${key}-${line}` : key;
|
||||
|
|
|
@ -11,15 +11,15 @@ import {
|
|||
makeSource,
|
||||
waitForState,
|
||||
} from "../../../utils/test-head";
|
||||
import { createSource } from "../../tests/helpers/mockCommandClient";
|
||||
import { createSource } from "../../tests/helpers/threadFront";
|
||||
|
||||
describe("breakpointPositions", () => {
|
||||
it("fetches positions", async () => {
|
||||
const fooContent = createSource("foo", "");
|
||||
|
||||
const store = createStore({
|
||||
getSourceActorBreakpointPositions: async () => ({ "9": [1] }),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({ "9": [1] }),
|
||||
getBreakableLines: async () => [],
|
||||
sourceContents: async () => fooContent,
|
||||
});
|
||||
|
||||
|
@ -63,12 +63,12 @@ describe("breakpointPositions", () => {
|
|||
let resolve = _ => {};
|
||||
let count = 0;
|
||||
const store = createStore({
|
||||
getSourceActorBreakpointPositions: () =>
|
||||
getBreakpointPositions: () =>
|
||||
new Promise(r => {
|
||||
count++;
|
||||
resolve = r;
|
||||
}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakableLines: async () => [],
|
||||
sourceContents: async () => fooContent,
|
||||
});
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@ import {
|
|||
getTelemetryEvents,
|
||||
} from "../../../utils/test-head";
|
||||
|
||||
import { mockCommandClient } from "../../tests/helpers/mockCommandClient";
|
||||
import { simpleMockThreadFront } from "../../tests/helpers/threadFront.js";
|
||||
|
||||
function mockClient(positionsResponse = {}) {
|
||||
return {
|
||||
...mockCommandClient,
|
||||
getSourceActorBreakpointPositions: async () => positionsResponse,
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
...simpleMockThreadFront,
|
||||
getBreakpointPositions: async () => positionsResponse,
|
||||
getBreakableLines: async () => [],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ type Match = Object;
|
|||
|
||||
export function doSearch(cx: Context, query: string, editor: Editor) {
|
||||
return ({ getState, dispatch }: ThunkArgs) => {
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
if (!selectedSource || !selectedSource.content) {
|
||||
const selectedSourceWithContent = getSelectedSourceWithContent(getState());
|
||||
if (!selectedSourceWithContent || !selectedSourceWithContent.content) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,8 @@ export function doSearchForHighlight(
|
|||
ch: number
|
||||
) {
|
||||
return async ({ getState, dispatch }: ThunkArgs) => {
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
if (!selectedSource || !selectedSource.content) {
|
||||
const selectedSourceWithContent = getSelectedSourceWithContent(getState());
|
||||
if (!selectedSourceWithContent || !selectedSourceWithContent.content) {
|
||||
return;
|
||||
}
|
||||
dispatch(searchContentsForHighlight(query, editor, line, ch));
|
||||
|
@ -105,18 +105,19 @@ export function searchContents(
|
|||
) {
|
||||
return async ({ getState, dispatch }: ThunkArgs) => {
|
||||
const modifiers = getFileSearchModifiers(getState());
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
const selectedSourceWithContent = getSelectedSourceWithContent(getState());
|
||||
|
||||
if (
|
||||
!editor ||
|
||||
!selectedSource ||
|
||||
!selectedSource.content ||
|
||||
!isFulfilled(selectedSource.content) ||
|
||||
!selectedSourceWithContent ||
|
||||
!selectedSourceWithContent.content ||
|
||||
!isFulfilled(selectedSourceWithContent.content) ||
|
||||
!modifiers
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const selectedContent = selectedSource.content.value;
|
||||
const selectedSource = selectedSourceWithContent.source;
|
||||
const selectedContent = selectedSourceWithContent.content.value;
|
||||
|
||||
const ctx = { ed: editor, cm: editor.codeMirror };
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import { features } from "../../../utils/prefs";
|
|||
const { isStepping } = selectors;
|
||||
|
||||
let stepInResolve = null;
|
||||
const mockCommandClient = {
|
||||
const mockThreadFront = {
|
||||
stepIn: () =>
|
||||
new Promise(_resolve => {
|
||||
stepInResolve = _resolve;
|
||||
|
@ -71,8 +71,8 @@ const mockCommandClient = {
|
|||
}
|
||||
});
|
||||
},
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
actorID: "threadActorID",
|
||||
};
|
||||
|
||||
|
@ -104,7 +104,7 @@ function createPauseInfo(
|
|||
describe("pause", () => {
|
||||
describe("stepping", () => {
|
||||
it("should set and clear the command", async () => {
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(mockThreadFront);
|
||||
const mockPauseInfo = createPauseInfo();
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo1")));
|
||||
|
@ -129,7 +129,7 @@ describe("pause", () => {
|
|||
});
|
||||
|
||||
it("should step when paused", async () => {
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(mockThreadFront);
|
||||
const mockPauseInfo = createPauseInfo();
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo1")));
|
||||
|
@ -140,7 +140,7 @@ describe("pause", () => {
|
|||
});
|
||||
|
||||
it("should step over when paused", async () => {
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(mockThreadFront);
|
||||
const { dispatch, getState } = store;
|
||||
const mockPauseInfo = createPauseInfo();
|
||||
|
||||
|
@ -155,7 +155,7 @@ describe("pause", () => {
|
|||
|
||||
it("should step over when paused before an await", async () => {
|
||||
features.asyncStepping = true;
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(mockThreadFront);
|
||||
const { dispatch, getState } = store;
|
||||
const mockPauseInfo = createPauseInfo({
|
||||
sourceId: "await",
|
||||
|
@ -175,8 +175,8 @@ describe("pause", () => {
|
|||
|
||||
it("should step over when paused after an await", async () => {
|
||||
const store = createStore({
|
||||
...mockCommandClient,
|
||||
getSourceActorBreakpointPositions: async () => ({ [2]: [1] }),
|
||||
...mockThreadFront,
|
||||
getBreakpointPositions: async () => ({ [2]: [1] }),
|
||||
});
|
||||
const { dispatch, getState } = store;
|
||||
const mockPauseInfo = createPauseInfo({
|
||||
|
@ -202,7 +202,7 @@ describe("pause", () => {
|
|||
column: 0,
|
||||
};
|
||||
|
||||
const store = createStore(mockCommandClient, {});
|
||||
const store = createStore(mockThreadFront, {});
|
||||
const { dispatch, getState } = store;
|
||||
const mockPauseInfo = createPauseInfo(generatedLocation, {
|
||||
scope: {
|
||||
|
@ -279,7 +279,7 @@ describe("pause", () => {
|
|||
getGeneratedLocation: async location => location,
|
||||
};
|
||||
|
||||
const store = createStore(mockCommandClient, {}, sourceMapsMock);
|
||||
const store = createStore(mockThreadFront, {}, sourceMapsMock);
|
||||
const { dispatch, getState } = store;
|
||||
const mockPauseInfo = createPauseInfo(generatedLocation);
|
||||
|
||||
|
@ -340,7 +340,7 @@ describe("pause", () => {
|
|||
getGeneratedRangesForOriginal: async () => [],
|
||||
};
|
||||
|
||||
const store = createStore(mockCommandClient, {}, sourceMapsMock);
|
||||
const store = createStore(mockThreadFront, {}, sourceMapsMock);
|
||||
const { dispatch, getState } = store;
|
||||
const mockPauseInfo = createPauseInfo(generatedLocation);
|
||||
|
||||
|
@ -383,7 +383,7 @@ describe("pause", () => {
|
|||
|
||||
describe("resumed", () => {
|
||||
it("should not evaluate expression while stepping", async () => {
|
||||
const client = { ...mockCommandClient, evaluateExpressions: jest.fn() };
|
||||
const client = { ...mockThreadFront, evaluateExpressions: jest.fn() };
|
||||
const { dispatch, getState } = createStore(client);
|
||||
const mockPauseInfo = createPauseInfo();
|
||||
|
||||
|
@ -392,12 +392,12 @@ describe("pause", () => {
|
|||
|
||||
const cx = selectors.getThreadContext(getState());
|
||||
dispatch(actions.stepIn(cx));
|
||||
await dispatch(actions.resumed(mockCommandClient.actorID));
|
||||
await dispatch(actions.resumed(mockThreadFront.actorID));
|
||||
expect(client.evaluateExpressions.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("resuming - will re-evaluate watch expressions", async () => {
|
||||
const client = { ...mockCommandClient, evaluateExpressions: jest.fn() };
|
||||
const client = { ...mockThreadFront, evaluateExpressions: jest.fn() };
|
||||
const store = createStore(client);
|
||||
const { dispatch, getState, cx } = store;
|
||||
const mockPauseInfo = createPauseInfo();
|
||||
|
@ -410,7 +410,7 @@ describe("pause", () => {
|
|||
client.evaluateExpressions.mockReturnValue(Promise.resolve(["YAY"]));
|
||||
await dispatch(actions.paused(mockPauseInfo));
|
||||
|
||||
await dispatch(actions.resumed(mockCommandClient.actorID));
|
||||
await dispatch(actions.resumed(mockThreadFront.actorID));
|
||||
const expression = selectors.getExpression(getState(), "foo");
|
||||
expect(expression && expression.value).toEqual("YAY");
|
||||
});
|
||||
|
|
|
@ -5,18 +5,7 @@
|
|||
// @flow
|
||||
|
||||
import type { ThunkArgs } from "./types";
|
||||
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";
|
||||
import type { SourceActor } from "../reducers/source-actors";
|
||||
|
||||
export function insertSourceActor(item: SourceActor) {
|
||||
return insertSourceActors([item]);
|
||||
|
@ -41,48 +30,3 @@ 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,7 +10,6 @@ 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 = [];
|
||||
|
@ -31,6 +30,11 @@ 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) {
|
||||
|
@ -38,19 +42,10 @@ export function setBreakableLines(cx: Context, sourceId: string) {
|
|||
}
|
||||
|
||||
dispatch({
|
||||
type: "SET_ORIGINAL_BREAKABLE_LINES",
|
||||
type: "SET_BREAKABLE_LINES",
|
||||
cx,
|
||||
sourceId,
|
||||
breakableLines,
|
||||
});
|
||||
} else {
|
||||
const actors = getSourceActorsForSource(getState(), sourceId);
|
||||
|
||||
await Promise.all(
|
||||
actors.map(actor =>
|
||||
dispatch(loadSourceActorBreakableLines({ id: actor.id }))
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import { addBreakpoint } from "../breakpoints";
|
|||
|
||||
import { prettyPrintSource } from "./prettyPrint";
|
||||
import { setBreakableLines } from "./breakableLines";
|
||||
import { isFulfilled, fulfilled } from "../../utils/async-value";
|
||||
import { isFulfilled } from "../../utils/async-value";
|
||||
|
||||
import { isOriginal, isPretty } from "../../utils/source";
|
||||
import {
|
||||
|
@ -124,6 +124,8 @@ async function loadSourceTextPromise(
|
|||
await dispatch(addBreakpoint(cx, location, options, disabled));
|
||||
}
|
||||
}
|
||||
|
||||
return newSource;
|
||||
}
|
||||
|
||||
export function loadSourceById(cx: Context, sourceId: string) {
|
||||
|
@ -137,22 +139,14 @@ export const loadSourceText: MemoizedAction<
|
|||
{ cx: Context, source: Source },
|
||||
?Source
|
||||
> = memoizeableAction("loadSourceText", {
|
||||
getValue: ({ source }, { getState }) => {
|
||||
source = source ? getSource(getState(), source.id) : null;
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { content } = getSourceWithContent(getState(), source.id);
|
||||
if (!content || content.state === "pending") {
|
||||
return content;
|
||||
}
|
||||
|
||||
// This currently swallows source-load-failure since we return fulfilled
|
||||
// here when content.state === "rejected". In an ideal world we should
|
||||
// propagate that error upward.
|
||||
return fulfilled(source);
|
||||
exitEarly: ({ source }) => !source,
|
||||
hasValue: ({ source }, { getState }) => {
|
||||
return !!(
|
||||
getSource(getState(), source.id) &&
|
||||
getSourceWithContent(getState(), source.id).content
|
||||
);
|
||||
},
|
||||
getValue: ({ source }, { getState }) => getSource(getState(), source.id),
|
||||
createKey: ({ source }, { getState }) => {
|
||||
const epoch = getSourcesEpoch(getState());
|
||||
return `${epoch}:${source.id}`;
|
||||
|
|
|
@ -66,7 +66,7 @@ export function createPrettySource(cx: Context, sourceId: string) {
|
|||
const url = getPrettySourceURL(source.url);
|
||||
const id = generatedToOriginalId(sourceId, url);
|
||||
|
||||
const prettySource = {
|
||||
const prettySource: Source = {
|
||||
id,
|
||||
url,
|
||||
relativeUrl: url,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import { getSymbols } from "../../selectors";
|
||||
import { hasSymbols, getSymbols } from "../../selectors";
|
||||
|
||||
import { PROMISE } from "../utils/middleware/promise";
|
||||
import { updateTab } from "../tabs";
|
||||
|
@ -14,7 +14,6 @@ import {
|
|||
memoizeableAction,
|
||||
type MemoizedAction,
|
||||
} from "../../utils/memoizableAction";
|
||||
import { fulfilled } from "../../utils/async-value";
|
||||
|
||||
import type { Source, Context } from "../../types";
|
||||
import type { Symbols } from "../../reducers/types";
|
||||
|
@ -35,6 +34,8 @@ async function doSetSymbols(cx, source, { dispatch, getState, parser }) {
|
|||
if (symbols && symbols.framework) {
|
||||
dispatch(updateTab(source, symbols.framework));
|
||||
}
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
||||
type Args = { cx: Context, source: Source };
|
||||
|
@ -42,18 +43,9 @@ type Args = { cx: Context, source: Source };
|
|||
export const setSymbols: MemoizedAction<Args, ?Symbols> = memoizeableAction(
|
||||
"setSymbols",
|
||||
{
|
||||
getValue: ({ source }, { getState }) => {
|
||||
if (source.isWasm) {
|
||||
return fulfilled(null);
|
||||
}
|
||||
|
||||
const symbols = getSymbols(getState(), source);
|
||||
if (!symbols || symbols.loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fulfilled(symbols);
|
||||
},
|
||||
exitEarly: ({ source }) => source.isWasm,
|
||||
hasValue: ({ source }, { getState }) => hasSymbols(getState(), source),
|
||||
getValue: ({ source }, { getState }) => getSymbols(getState(), source),
|
||||
createKey: ({ source }) => source.id,
|
||||
action: ({ cx, source }, thunkArgs) => doSetSymbols(cx, source, thunkArgs),
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ describe("blackbox", () => {
|
|||
it("should blackbox a source", async () => {
|
||||
const store = createStore({
|
||||
blackBox: async () => true,
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakableLines: async () => [],
|
||||
});
|
||||
const { dispatch, getState, cx } = store;
|
||||
|
||||
|
|
|
@ -14,14 +14,14 @@ import {
|
|||
} from "../../../utils/test-head";
|
||||
import {
|
||||
createSource,
|
||||
mockCommandClient,
|
||||
} from "../../tests/helpers/mockCommandClient";
|
||||
sourceThreadFront,
|
||||
} from "../../tests/helpers/threadFront.js";
|
||||
import { getBreakpointsList } from "../../../selectors";
|
||||
import { isFulfilled, isRejected } from "../../../utils/async-value";
|
||||
|
||||
describe("loadSourceText", () => {
|
||||
it("should load source text", async () => {
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(sourceThreadFront);
|
||||
const { dispatch, getState, cx } = store;
|
||||
|
||||
const foo1Source = await dispatch(
|
||||
|
@ -59,10 +59,10 @@ describe("loadSourceText", () => {
|
|||
|
||||
const store = createStore(
|
||||
{
|
||||
...mockCommandClient,
|
||||
...sourceThreadFront,
|
||||
sourceContents: async () => fooGenContent,
|
||||
getSourceActorBreakpointPositions: async () => ({ "1": [0] }),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({ "1": [0] }),
|
||||
getBreakableLines: async () => [],
|
||||
},
|
||||
{},
|
||||
{
|
||||
|
@ -156,8 +156,8 @@ describe("loadSourceText", () => {
|
|||
count++;
|
||||
resolve = r;
|
||||
}),
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
});
|
||||
const id = "foo";
|
||||
|
||||
|
@ -194,8 +194,8 @@ describe("loadSourceText", () => {
|
|||
count++;
|
||||
resolve = r;
|
||||
}),
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
});
|
||||
const id = "foo";
|
||||
|
||||
|
@ -223,7 +223,7 @@ describe("loadSourceText", () => {
|
|||
});
|
||||
|
||||
it("should cache subsequent source text loads", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
const source = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foo1"))
|
||||
|
@ -238,7 +238,7 @@ describe("loadSourceText", () => {
|
|||
});
|
||||
|
||||
it("should indicate a loading source", async () => {
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(sourceThreadFront);
|
||||
const { dispatch, cx } = store;
|
||||
|
||||
const source = await dispatch(
|
||||
|
@ -255,7 +255,7 @@ describe("loadSourceText", () => {
|
|||
});
|
||||
|
||||
it("should indicate an errored source text", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
const source = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("bad-id"))
|
||||
|
|
|
@ -20,11 +20,12 @@ const {
|
|||
} = selectors;
|
||||
import sourceQueue from "../../../utils/source-queue";
|
||||
|
||||
import { mockCommandClient } from "../../tests/helpers/mockCommandClient";
|
||||
// eslint-disable-next-line max-len
|
||||
import { sourceThreadFront as threadFront } from "../../tests/helpers/threadFront.js";
|
||||
|
||||
describe("sources - new sources", () => {
|
||||
it("should add sources to state", async () => {
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(threadFront);
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("jquery.js")));
|
||||
|
||||
|
@ -36,7 +37,7 @@ describe("sources - new sources", () => {
|
|||
});
|
||||
|
||||
it("should not add multiple identical sources", async () => {
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(threadFront);
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js")));
|
||||
|
@ -45,7 +46,7 @@ describe("sources - new sources", () => {
|
|||
});
|
||||
|
||||
it("should automatically select a pending source", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
const baseSourceURL = makeSourceURL("base.js");
|
||||
await dispatch(actions.selectSourceURL(cx, baseSourceURL));
|
||||
|
||||
|
@ -60,7 +61,7 @@ describe("sources - new sources", () => {
|
|||
|
||||
it("should add original sources", async () => {
|
||||
const { dispatch, getState } = createStore(
|
||||
mockCommandClient,
|
||||
threadFront,
|
||||
{},
|
||||
{
|
||||
getOriginalURLs: async () => ["magic.js"],
|
||||
|
@ -81,7 +82,7 @@ describe("sources - new sources", () => {
|
|||
it("should not attempt to fetch original sources if it's missing a source map url", async () => {
|
||||
const getOriginalURLs = jest.fn();
|
||||
const { dispatch } = createStore(
|
||||
mockCommandClient,
|
||||
threadFront,
|
||||
{},
|
||||
{
|
||||
getOriginalURLs,
|
||||
|
@ -96,7 +97,7 @@ describe("sources - new sources", () => {
|
|||
// eslint-disable-next-line
|
||||
it("should process new sources immediately, without waiting for source maps to be fetched first", async () => {
|
||||
const { dispatch, getState } = createStore(
|
||||
mockCommandClient,
|
||||
threadFront,
|
||||
{},
|
||||
{
|
||||
getOriginalURLs: async () => new Promise(_ => {}),
|
||||
|
@ -116,7 +117,7 @@ describe("sources - new sources", () => {
|
|||
// eslint-disable-next-line
|
||||
it("shouldn't let one slow loading source map delay all the other source maps", async () => {
|
||||
const dbg = createStore(
|
||||
mockCommandClient,
|
||||
threadFront,
|
||||
{},
|
||||
{
|
||||
getOriginalURLs: async source => {
|
||||
|
@ -152,7 +153,7 @@ describe("sources - new sources", () => {
|
|||
it(`should find two sources when same source with
|
||||
querystring`, async () => {
|
||||
const { getSourcesUrlsInSources } = selectors;
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(threadFront);
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js?v=1")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js?v=2")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("diff.js?v=1")));
|
||||
|
|
|
@ -11,12 +11,12 @@ import {
|
|||
makeSource,
|
||||
} from "../../../utils/test-head";
|
||||
import { createPrettySource } from "../prettyPrint";
|
||||
import { mockCommandClient } from "../../tests/helpers/mockCommandClient";
|
||||
import { sourceThreadFront } from "../../tests/helpers/threadFront.js";
|
||||
import { isFulfilled } from "../../../utils/async-value";
|
||||
|
||||
describe("sources - pretty print", () => {
|
||||
it("returns a pretty source for a minified file", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
const url = "base.js";
|
||||
const source = await dispatch(actions.newGeneratedSource(makeSource(url)));
|
||||
|
@ -42,7 +42,7 @@ describe("sources - pretty print", () => {
|
|||
});
|
||||
|
||||
it("should create a source when first toggling pretty print", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
const source = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foobar.js"))
|
||||
|
@ -54,7 +54,7 @@ describe("sources - pretty print", () => {
|
|||
});
|
||||
|
||||
it("should not make a second source when toggling pretty print", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
const source = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foobar.js"))
|
||||
|
|
|
@ -13,11 +13,11 @@ import {
|
|||
const { getSourcesUrlsInSources } = selectors;
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
import { mockCommandClient } from "../../tests/helpers/mockCommandClient";
|
||||
import { sourceThreadFront as threadFront } from "../../tests/helpers/threadFront.js";
|
||||
|
||||
describe("sources - sources with querystrings", () => {
|
||||
it("should find two sources when same source with querystring", async () => {
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(threadFront);
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js?v=1")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("base.js?v=2")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("diff.js?v=1")));
|
||||
|
|
|
@ -23,7 +23,7 @@ const {
|
|||
getSelectedLocation,
|
||||
} = selectors;
|
||||
|
||||
import { mockCommandClient } from "../../tests/helpers/mockCommandClient";
|
||||
import { sourceThreadFront } from "../../tests/helpers/threadFront.js";
|
||||
|
||||
process.on("unhandledRejection", (reason, p) => {});
|
||||
|
||||
|
@ -35,7 +35,7 @@ describe("sources", () => {
|
|||
it("should select a source", async () => {
|
||||
// Note that we pass an empty client in because the action checks
|
||||
// if it exists.
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(sourceThreadFront);
|
||||
const { dispatch, getState } = store;
|
||||
|
||||
const frame = makeFrame({ id: "1", sourceId: "foo1" });
|
||||
|
@ -69,7 +69,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("should select next tab on tab closed if no previous tab", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
const fooSource = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foo.js"))
|
||||
|
@ -98,7 +98,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("should open a tab for the source", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
dispatch(actions.selectLocation(cx, initialLocation("foo.js")));
|
||||
|
||||
|
@ -108,7 +108,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("should select previous tab on tab closed", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
|
||||
|
||||
|
@ -127,7 +127,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("should keep the selected source when other tab closed", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
|
||||
|
@ -154,7 +154,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("should not select new sources that lack a URL", async () => {
|
||||
const { dispatch, getState } = createStore(mockCommandClient);
|
||||
const { dispatch, getState } = createStore(sourceThreadFront);
|
||||
|
||||
await dispatch(
|
||||
actions.newGeneratedSource({
|
||||
|
@ -169,7 +169,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("sets and clears selected location correctly", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
const source = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("testSource"))
|
||||
);
|
||||
|
@ -188,7 +188,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("sets and clears pending selected location correctly", () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
const url = "testURL";
|
||||
const options = { location: { line: "testLine" } };
|
||||
|
||||
|
@ -207,7 +207,7 @@ describe("sources", () => {
|
|||
});
|
||||
|
||||
it("should keep the generated the viewing context", async () => {
|
||||
const store = createStore(mockCommandClient);
|
||||
const store = createStore(sourceThreadFront);
|
||||
const { dispatch, getState, cx } = store;
|
||||
const baseSource = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("base.js"))
|
||||
|
@ -224,7 +224,7 @@ describe("sources", () => {
|
|||
|
||||
it("should keep the original the viewing context", async () => {
|
||||
const { dispatch, getState, cx } = createStore(
|
||||
mockCommandClient,
|
||||
sourceThreadFront,
|
||||
{},
|
||||
{
|
||||
getOriginalLocation: async location => ({ ...location, line: 12 }),
|
||||
|
@ -258,7 +258,7 @@ describe("sources", () => {
|
|||
|
||||
it("should change the original the viewing context", async () => {
|
||||
const { dispatch, getState, cx } = createStore(
|
||||
mockCommandClient,
|
||||
sourceThreadFront,
|
||||
{},
|
||||
{
|
||||
getOriginalLocation: async location => ({ ...location, line: 12 }),
|
||||
|
@ -290,7 +290,7 @@ describe("sources", () => {
|
|||
|
||||
describe("selectSourceURL", () => {
|
||||
it("should automatically select a pending source", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(sourceThreadFront);
|
||||
const baseSourceURL = makeSourceURL("base.js");
|
||||
await dispatch(actions.selectSourceURL(cx, baseSourceURL));
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import readFixture from "./helpers/readFixture";
|
||||
const { getSymbols, isSymbolsLoading, getFramework } = selectors;
|
||||
|
||||
const mockCommandClient = {
|
||||
const threadFront = {
|
||||
sourceContents: async ({ source }) => ({
|
||||
source: sourceTexts[source],
|
||||
contentType: "text/javascript",
|
||||
|
@ -26,8 +26,8 @@ const mockCommandClient = {
|
|||
evaluate: async expression => ({ result: evaluationResult[expression] }),
|
||||
evaluateExpressions: async expressions =>
|
||||
expressions.map(expression => ({ result: evaluationResult[expression] })),
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: 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(mockCommandClient);
|
||||
const store = createStore(threadFront);
|
||||
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(mockCommandClient);
|
||||
const { getState, dispatch } = createStore(threadFront);
|
||||
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(mockCommandClient);
|
||||
const { getState } = createStore(threadFront);
|
||||
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(mockCommandClient, {}, sourceMaps);
|
||||
const store = createStore(threadFront, {}, 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(mockCommandClient);
|
||||
const store = createStore(threadFront);
|
||||
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" }),
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
autocomplete: () => {
|
||||
return new Promise(resolve => {
|
||||
resolve({
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import type { SourceActor } from "../../../types";
|
||||
import type {
|
||||
SourceActor,
|
||||
SourceActorLocation,
|
||||
BreakpointOptions,
|
||||
} from "../../../types";
|
||||
|
||||
export function createSource(name: string, code?: string) {
|
||||
name = name.replace(/\..*$/, "");
|
||||
|
@ -32,7 +36,33 @@ const sources = [
|
|||
"jquery.js",
|
||||
];
|
||||
|
||||
export const mockCommandClient = {
|
||||
export const simpleMockThreadFront = {
|
||||
getBreakpointByLocation: (jest.fn(): any),
|
||||
setBreakpoint: (location: SourceActorLocation, _condition: string) =>
|
||||
Promise.resolve({ id: "hi", actualLocation: location }),
|
||||
|
||||
removeBreakpoint: (_id: string) => Promise.resolve(),
|
||||
|
||||
setBreakpointOptions: (
|
||||
_id: string,
|
||||
_location: SourceActorLocation,
|
||||
_options: BreakpointOptions,
|
||||
_noSliding: boolean
|
||||
) => Promise.resolve({ sourceId: "a", line: 5 }),
|
||||
sourceContents: ({
|
||||
source,
|
||||
}: SourceActor): Promise<{| source: any, contentType: ?string |}> =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (sources.includes(source)) {
|
||||
resolve(createSource(source));
|
||||
}
|
||||
|
||||
reject(`unknown source: ${source}`);
|
||||
}),
|
||||
};
|
||||
|
||||
// sources and tabs
|
||||
export const sourceThreadFront = {
|
||||
sourceContents: function({
|
||||
source,
|
||||
}: SourceActor): Promise<{| source: any, contentType: ?string |}> {
|
||||
|
@ -45,10 +75,9 @@ export const mockCommandClient = {
|
|||
});
|
||||
},
|
||||
setBreakpoint: async () => {},
|
||||
removeBreakpoint: (_id: string) => Promise.resolve(),
|
||||
threadFront: async () => {},
|
||||
getFrameScopes: async () => {},
|
||||
evaluateExpressions: async () => {},
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
};
|
|
@ -27,8 +27,8 @@ const threadFront = {
|
|||
source: "function foo1() {\n const foo = 5; return foo;\n}",
|
||||
contentType: "text/javascript",
|
||||
}),
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
detachWorkers: () => {},
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
mockPendingBreakpoint,
|
||||
} from "./helpers/breakpoints.js";
|
||||
|
||||
import { mockCommandClient } from "./helpers/mockCommandClient";
|
||||
import { simpleMockThreadFront } from "./helpers/threadFront.js";
|
||||
|
||||
import { asyncStore } from "../../utils/prefs";
|
||||
|
||||
|
@ -47,10 +47,10 @@ import sourceMaps from "devtools-source-map";
|
|||
import { makePendingLocationId } from "../../utils/breakpoint";
|
||||
function mockClient(bpPos = {}) {
|
||||
return {
|
||||
...mockCommandClient,
|
||||
...simpleMockThreadFront,
|
||||
|
||||
getSourceActorBreakpointPositions: async () => bpPos,
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => bpPos,
|
||||
getBreakableLines: async () => [],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ function mockThreadFront(overrides) {
|
|||
source: "",
|
||||
contentType: "text/javascript",
|
||||
}),
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
evaluateExpressions: async () => [],
|
||||
loadObjectProperties: async () => ({}),
|
||||
...overrides,
|
||||
|
|
|
@ -39,8 +39,8 @@ const sources = {
|
|||
|
||||
const threadFront = {
|
||||
sourceContents: async ({ source }) => sources[source],
|
||||
getSourceActorBreakpointPositions: async () => ({}),
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakpointPositions: async () => ({}),
|
||||
getBreakableLines: async () => [],
|
||||
};
|
||||
|
||||
describe("project text search", () => {
|
||||
|
|
|
@ -43,7 +43,7 @@ describe("setProjectDirectoryRoot", () => {
|
|||
|
||||
it("should filter sources", async () => {
|
||||
const store = createStore({
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakableLines: 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({
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakableLines: 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({
|
||||
getSourceActorBreakableLines: async () => [],
|
||||
getBreakableLines: async () => [],
|
||||
});
|
||||
dispatch(actions.setProjectDirectoryRoot(cx, "webpack://"));
|
||||
dispatch(actions.setProjectDirectoryRoot(cx, "webpack:///app"));
|
||||
|
|
|
@ -12,11 +12,11 @@ import {
|
|||
} from "../../utils/test-head";
|
||||
const { getSelectedSource, getSourceTabs } = selectors;
|
||||
|
||||
import { mockCommandClient } from "./helpers/mockCommandClient";
|
||||
import { sourceThreadFront as threadFront } from "./helpers/threadFront.js";
|
||||
|
||||
describe("closing tabs", () => {
|
||||
it("closing a tab", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
const fooSource = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foo.js"))
|
||||
|
@ -29,7 +29,7 @@ describe("closing tabs", () => {
|
|||
});
|
||||
|
||||
it("closing the inactive tab", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
const fooSource = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foo.js"))
|
||||
|
@ -45,7 +45,7 @@ describe("closing tabs", () => {
|
|||
});
|
||||
|
||||
it("closing the only tab", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
const fooSource = await dispatch(
|
||||
actions.newGeneratedSource(makeSource("foo.js"))
|
||||
|
@ -58,7 +58,7 @@ describe("closing tabs", () => {
|
|||
});
|
||||
|
||||
it("closing the active tab", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
const barSource = await dispatch(
|
||||
|
@ -74,7 +74,7 @@ describe("closing tabs", () => {
|
|||
});
|
||||
|
||||
it("closing many inactive tabs", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
|
||||
|
@ -97,7 +97,7 @@ describe("closing tabs", () => {
|
|||
});
|
||||
|
||||
it("closing many tabs including the active tab", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
|
||||
|
@ -119,7 +119,7 @@ describe("closing tabs", () => {
|
|||
});
|
||||
|
||||
it("closing all the tabs", async () => {
|
||||
const { dispatch, getState, cx } = createStore(mockCommandClient);
|
||||
const { dispatch, getState, cx } = createStore(threadFront);
|
||||
|
||||
await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
|
||||
await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import type { SourceId, Source, SourceLocation, Context } from "../../types";
|
||||
import type { PromiseAction } from "../utils/middleware/promise";
|
||||
import type { SourceBase } from "../../reducers/sources";
|
||||
|
||||
export type LoadSourceAction = PromiseAction<
|
||||
{|
|
||||
|
@ -25,12 +24,12 @@ export type SourceAction =
|
|||
| {|
|
||||
+type: "ADD_SOURCE",
|
||||
+cx: Context,
|
||||
+source: SourceBase,
|
||||
+source: Source,
|
||||
|}
|
||||
| {|
|
||||
+type: "ADD_SOURCES",
|
||||
+cx: Context,
|
||||
+sources: Array<SourceBase>,
|
||||
+sources: Array<Source>,
|
||||
|}
|
||||
| {|
|
||||
+type: "CLEAR_SOURCE_MAP_URL",
|
||||
|
@ -77,7 +76,7 @@ export type SourceAction =
|
|||
+tabs: any,
|
||||
|}
|
||||
| {|
|
||||
type: "SET_ORIGINAL_BREAKABLE_LINES",
|
||||
type: "SET_BREAKABLE_LINES",
|
||||
+cx: Context,
|
||||
breakableLines: number[],
|
||||
sourceId: string,
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
// @flow
|
||||
|
||||
import { type PromiseAction } from "../utils/middleware/promise";
|
||||
import type {
|
||||
SourceActorId,
|
||||
SourceActor,
|
||||
} from "../../reducers/source-actors.js";
|
||||
import type { SourceActor } from "../../reducers/source-actors.js";
|
||||
|
||||
export type SourceActorsInsertAction = {|
|
||||
type: "INSERT_SOURCE_ACTORS",
|
||||
|
@ -19,25 +15,6 @@ 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
|
||||
| SourceActorBreakpointColumnsAction
|
||||
| SourceActorBreakableLinesAction;
|
||||
| SourceActorsRemoveAction;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { fromPairs, toPairs } from "lodash";
|
||||
import { executeSoon } from "../../../utils/DevToolsUtils";
|
||||
|
||||
import type { ThunkArgs } from "../../types";
|
||||
|
||||
type BasePromiseAction = {|
|
||||
|
@ -29,25 +30,7 @@ export type ErrorPromiseAction = {|
|
|||
+error: 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> =
|
||||
export type PromiseAction<Action, Value = any> =
|
||||
// | {| ...Action, "@@dispatch/promise": Promise<Object> |}
|
||||
| {|
|
||||
...BasePromiseAction,
|
||||
|
|
|
@ -438,19 +438,33 @@ function getMainThread() {
|
|||
return currentThreadFront.actor;
|
||||
}
|
||||
|
||||
async function getSourceActorBreakpointPositions(
|
||||
{ thread, actor }: SourceActor,
|
||||
range: Range
|
||||
): Promise<{ [number]: number[] }> {
|
||||
async function getBreakpointPositions(
|
||||
actors: Array<SourceActor>,
|
||||
range: ?Range
|
||||
): Promise<{ [string]: number[] }> {
|
||||
const sourcePositions = {};
|
||||
|
||||
for (const { thread, actor } of actors) {
|
||||
const sourceThreadFront = lookupThreadFront(thread);
|
||||
const sourceFront = sourceThreadFront.source({ actor });
|
||||
return sourceFront.getBreakpointPositionsCompressed(range);
|
||||
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])];
|
||||
}
|
||||
|
||||
sourcePositions[line] = columns;
|
||||
}
|
||||
}
|
||||
return sourcePositions;
|
||||
}
|
||||
|
||||
async function getSourceActorBreakableLines({
|
||||
thread,
|
||||
actor,
|
||||
}: SourceActor): Promise<Array<number>> {
|
||||
async function getBreakableLines(actors: Array<SourceActor>) {
|
||||
let lines = [];
|
||||
for (const { thread, actor } of actors) {
|
||||
const sourceThreadFront = lookupThreadFront(thread);
|
||||
const sourceFront = sourceThreadFront.source({ actor });
|
||||
let actorLines = [];
|
||||
|
@ -469,7 +483,10 @@ async function getSourceActorBreakableLines({
|
|||
}
|
||||
}
|
||||
|
||||
return actorLines;
|
||||
lines = [...new Set([...lines, ...actorLines])];
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
const clientCommands = {
|
||||
|
@ -489,8 +506,8 @@ const clientCommands = {
|
|||
breakOnNext,
|
||||
sourceContents,
|
||||
getSourceForActor,
|
||||
getSourceActorBreakpointPositions,
|
||||
getSourceActorBreakableLines,
|
||||
getBreakpointPositions,
|
||||
getBreakableLines,
|
||||
hasBreakpoint,
|
||||
setBreakpoint,
|
||||
setXHRBreakpoint,
|
||||
|
|
|
@ -30,7 +30,7 @@ type Props = {
|
|||
editor: SourceEditor,
|
||||
hasPrettySource: boolean,
|
||||
isPaused: boolean,
|
||||
selectedSource: SourceWithContent,
|
||||
selectedSourceWithContent: SourceWithContent,
|
||||
};
|
||||
|
||||
class EditorMenu extends Component<Props> {
|
||||
|
@ -47,7 +47,7 @@ class EditorMenu extends Component<Props> {
|
|||
const {
|
||||
cx,
|
||||
editor,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
editorActions,
|
||||
hasPrettySource,
|
||||
isPaused,
|
||||
|
@ -56,7 +56,7 @@ class EditorMenu extends Component<Props> {
|
|||
|
||||
const location = getSourceLocationFromMouseEvent(
|
||||
editor,
|
||||
selectedSource,
|
||||
selectedSourceWithContent.source,
|
||||
// Use a coercion, as contextMenu is optional
|
||||
(event: any)
|
||||
);
|
||||
|
@ -66,7 +66,7 @@ class EditorMenu extends Component<Props> {
|
|||
editorMenuItems({
|
||||
cx,
|
||||
editorActions,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
hasPrettySource,
|
||||
location,
|
||||
isPaused,
|
||||
|
@ -84,7 +84,10 @@ class EditorMenu extends Component<Props> {
|
|||
const mapStateToProps = (state, props) => ({
|
||||
cx: getThreadContext(state),
|
||||
isPaused: getIsPaused(state, getCurrentThread(state)),
|
||||
hasPrettySource: !!getPrettySource(state, props.selectedSource.id),
|
||||
hasPrettySource: !!getPrettySource(
|
||||
state,
|
||||
props.selectedSourceWithContent.source.id
|
||||
),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -38,7 +38,7 @@ type CursorPosition = {
|
|||
|
||||
type Props = {
|
||||
cx: Context,
|
||||
selectedSource: ?SourceWithContent,
|
||||
selectedSourceWithContent: ?SourceWithContent,
|
||||
mappedSource: Source,
|
||||
endPanelCollapsed: boolean,
|
||||
horizontal: boolean,
|
||||
|
@ -84,13 +84,16 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
prettyPrintButton() {
|
||||
const { cx, selectedSource, togglePrettyPrint } = this.props;
|
||||
const { cx, selectedSourceWithContent, togglePrettyPrint } = this.props;
|
||||
|
||||
if (!selectedSource) {
|
||||
if (!selectedSourceWithContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedSource.content && selectedSource.isPrettyPrinted) {
|
||||
if (
|
||||
!selectedSourceWithContent.content &&
|
||||
selectedSourceWithContent.source.isPrettyPrinted
|
||||
) {
|
||||
return (
|
||||
<div className="loader" key="pretty-loader">
|
||||
<AccessibleImage className="loader" />
|
||||
|
@ -99,12 +102,13 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
const sourceContent =
|
||||
selectedSource.content && isFulfilled(selectedSource.content)
|
||||
? selectedSource.content.value
|
||||
selectedSourceWithContent.content &&
|
||||
isFulfilled(selectedSourceWithContent.content)
|
||||
? selectedSourceWithContent.content.value
|
||||
: null;
|
||||
if (
|
||||
!shouldShowPrettyPrint(
|
||||
selectedSource,
|
||||
selectedSourceWithContent.source,
|
||||
sourceContent || { type: "text", value: "", contentType: undefined }
|
||||
)
|
||||
) {
|
||||
|
@ -112,15 +116,17 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
const tooltip = L10N.getStr("sourceTabs.prettyPrint");
|
||||
const sourceLoaded = !!selectedSource.content;
|
||||
const sourceLoaded = !!selectedSourceWithContent.content;
|
||||
|
||||
const type = "prettyPrint";
|
||||
return (
|
||||
<button
|
||||
onClick={() => togglePrettyPrint(cx, selectedSource.id)}
|
||||
onClick={() =>
|
||||
togglePrettyPrint(cx, selectedSourceWithContent.source.id)
|
||||
}
|
||||
className={classnames("action", type, {
|
||||
active: sourceLoaded,
|
||||
pretty: isPretty(selectedSource),
|
||||
pretty: isPretty(selectedSourceWithContent.source),
|
||||
})}
|
||||
key={type}
|
||||
title={tooltip}
|
||||
|
@ -132,18 +138,19 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
blackBoxButton() {
|
||||
const { cx, selectedSource, toggleBlackBox } = this.props;
|
||||
const sourceLoaded = selectedSource && selectedSource.content;
|
||||
const { cx, selectedSourceWithContent, toggleBlackBox } = this.props;
|
||||
const sourceLoaded =
|
||||
selectedSourceWithContent && selectedSourceWithContent.content;
|
||||
|
||||
if (!selectedSource) {
|
||||
if (!selectedSourceWithContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldBlackbox(selectedSource)) {
|
||||
if (!shouldBlackbox(selectedSourceWithContent.source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const blackboxed = selectedSource.isBlackBoxed;
|
||||
const blackboxed = selectedSourceWithContent.source.isBlackBoxed;
|
||||
|
||||
const tooltip = blackboxed
|
||||
? L10N.getStr("sourceFooter.unblackbox")
|
||||
|
@ -153,7 +160,7 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<button
|
||||
onClick={() => toggleBlackBox(cx, selectedSource)}
|
||||
onClick={() => toggleBlackBox(cx, selectedSourceWithContent.source)}
|
||||
className={classnames("action", type, {
|
||||
active: sourceLoaded,
|
||||
blackboxed: blackboxed,
|
||||
|
@ -196,10 +203,14 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
cx,
|
||||
mappedSource,
|
||||
jumpToMappedLocation,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
} = this.props;
|
||||
|
||||
if (!mappedSource || !selectedSource || !isOriginal(selectedSource)) {
|
||||
if (
|
||||
!mappedSource ||
|
||||
!selectedSourceWithContent ||
|
||||
!isOriginal(selectedSourceWithContent.source)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -210,7 +221,7 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
);
|
||||
const title = L10N.getFormatStr("sourceFooter.mappedSource", filename);
|
||||
const mappedSourceLocation = {
|
||||
sourceId: selectedSource.id,
|
||||
sourceId: selectedSourceWithContent.source.id,
|
||||
line: 1,
|
||||
column: 1,
|
||||
};
|
||||
|
@ -231,7 +242,7 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
};
|
||||
|
||||
renderCursorPosition() {
|
||||
if (!this.props.selectedSource) {
|
||||
if (!this.props.selectedSourceWithContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -269,15 +280,18 @@ class SourceFooter extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = getSelectedSourceWithContent(state);
|
||||
const selectedSourceWithContent = getSelectedSourceWithContent(state);
|
||||
|
||||
return {
|
||||
cx: getContext(state),
|
||||
selectedSource,
|
||||
mappedSource: getGeneratedSource(state, selectedSource),
|
||||
selectedSourceWithContent,
|
||||
mappedSource: getGeneratedSource(
|
||||
state,
|
||||
selectedSourceWithContent && selectedSourceWithContent.source
|
||||
),
|
||||
prettySource: getPrettySource(
|
||||
state,
|
||||
selectedSource ? selectedSource.id : null
|
||||
selectedSourceWithContent ? selectedSourceWithContent.source.id : null
|
||||
),
|
||||
endPanelCollapsed: getPaneCollapse(state, "end"),
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ type Props = {
|
|||
pauseCommand: Command,
|
||||
selectedFrame: Frame,
|
||||
selectedLocation: SourceLocation,
|
||||
selectedSource: ?SourceWithContent,
|
||||
selectedSourceWithContent: ?SourceWithContent,
|
||||
};
|
||||
|
||||
function isDebugLine(selectedFrame: Frame, selectedLocation: SourceLocation) {
|
||||
|
@ -42,11 +42,14 @@ function isDebugLine(selectedFrame: Frame, selectedLocation: SourceLocation) {
|
|||
);
|
||||
}
|
||||
|
||||
function isDocumentReady(selectedSource: ?SourceWithContent, selectedLocation) {
|
||||
function isDocumentReady(
|
||||
selectedSourceWithContent: ?SourceWithContent,
|
||||
selectedLocation
|
||||
) {
|
||||
return (
|
||||
selectedLocation &&
|
||||
selectedSource &&
|
||||
selectedSource.content &&
|
||||
selectedSourceWithContent &&
|
||||
selectedSourceWithContent.content &&
|
||||
hasDocument(selectedLocation.sourceId)
|
||||
);
|
||||
}
|
||||
|
@ -56,8 +59,11 @@ export class HighlightLine extends Component<Props> {
|
|||
previousEditorLine: ?number = null;
|
||||
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
const { selectedLocation, selectedSource } = nextProps;
|
||||
return this.shouldSetHighlightLine(selectedLocation, selectedSource);
|
||||
const { selectedLocation, selectedSourceWithContent } = nextProps;
|
||||
return this.shouldSetHighlightLine(
|
||||
selectedLocation,
|
||||
selectedSourceWithContent
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
|
@ -70,12 +76,12 @@ export class HighlightLine extends Component<Props> {
|
|||
|
||||
shouldSetHighlightLine(
|
||||
selectedLocation: SourceLocation,
|
||||
selectedSource: ?SourceWithContent
|
||||
selectedSourceWithContent: ?SourceWithContent
|
||||
) {
|
||||
const { sourceId, line } = selectedLocation;
|
||||
const editorLine = toEditorLine(sourceId, line);
|
||||
|
||||
if (!isDocumentReady(selectedSource, selectedLocation)) {
|
||||
if (!isDocumentReady(selectedSourceWithContent, selectedLocation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -91,7 +97,7 @@ export class HighlightLine extends Component<Props> {
|
|||
pauseCommand,
|
||||
selectedLocation,
|
||||
selectedFrame,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
} = this.props;
|
||||
if (pauseCommand) {
|
||||
this.isStepping = true;
|
||||
|
@ -101,20 +107,26 @@ export class HighlightLine extends Component<Props> {
|
|||
if (prevProps) {
|
||||
this.clearHighlightLine(
|
||||
prevProps.selectedLocation,
|
||||
prevProps.selectedSource
|
||||
prevProps.selectedSourceWithContent
|
||||
);
|
||||
}
|
||||
this.setHighlightLine(selectedLocation, selectedFrame, selectedSource);
|
||||
this.setHighlightLine(
|
||||
selectedLocation,
|
||||
selectedFrame,
|
||||
selectedSourceWithContent
|
||||
);
|
||||
endOperation();
|
||||
}
|
||||
|
||||
setHighlightLine(
|
||||
selectedLocation: SourceLocation,
|
||||
selectedFrame: Frame,
|
||||
selectedSource: ?SourceWithContent
|
||||
selectedSourceWithContent: ?SourceWithContent
|
||||
) {
|
||||
const { sourceId, line } = selectedLocation;
|
||||
if (!this.shouldSetHighlightLine(selectedLocation, selectedSource)) {
|
||||
if (
|
||||
!this.shouldSetHighlightLine(selectedLocation, selectedSourceWithContent)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -154,9 +166,9 @@ export class HighlightLine extends Component<Props> {
|
|||
|
||||
clearHighlightLine(
|
||||
selectedLocation: SourceLocation,
|
||||
selectedSource: ?SourceWithContent
|
||||
selectedSourceWithContent: ?SourceWithContent
|
||||
) {
|
||||
if (!isDocumentReady(selectedSource, selectedLocation)) {
|
||||
if (!isDocumentReady(selectedSourceWithContent, selectedLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -175,5 +187,5 @@ export default connect(state => ({
|
|||
pauseCommand: getPauseCommand(state, getCurrentThread(state)),
|
||||
selectedFrame: getVisibleSelectedFrame(state),
|
||||
selectedLocation: getSelectedLocation(state),
|
||||
selectedSource: getSelectedSourceWithContent(state),
|
||||
selectedSourceWithContent: getSelectedSourceWithContent(state),
|
||||
}))(HighlightLine);
|
||||
|
|
|
@ -95,7 +95,7 @@ const cssVars = {
|
|||
export type Props = {
|
||||
cx: ThreadContext,
|
||||
selectedLocation: ?SourceLocation,
|
||||
selectedSource: ?SourceWithContent,
|
||||
selectedSourceWithContent: ?SourceWithContent,
|
||||
searchOn: boolean,
|
||||
startPanelSize: number,
|
||||
endPanelSize: number,
|
||||
|
@ -139,7 +139,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
componentWillReceiveProps(nextProps: Props) {
|
||||
let editor = this.state.editor;
|
||||
|
||||
if (!this.state.editor && nextProps.selectedSource) {
|
||||
if (!this.state.editor && nextProps.selectedSourceWithContent) {
|
||||
editor = this.setupEditor();
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,10 @@ class Editor extends PureComponent<Props, State> {
|
|||
this.scrollToLocation(nextProps, editor);
|
||||
endOperation();
|
||||
|
||||
if (this.props.selectedSource != nextProps.selectedSource) {
|
||||
if (
|
||||
this.props.selectedSourceWithContent !=
|
||||
nextProps.selectedSourceWithContent
|
||||
) {
|
||||
this.props.updateViewport();
|
||||
resizeBreakpointGutter(editor.codeMirror);
|
||||
resizeToggleButton(editor.codeMirror);
|
||||
|
@ -227,11 +230,11 @@ class Editor extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
onClosePress = (key, e: KeyboardEvent) => {
|
||||
const { cx, selectedSource } = this.props;
|
||||
if (selectedSource) {
|
||||
const { cx, selectedSourceWithContent } = this.props;
|
||||
if (selectedSourceWithContent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.closeTab(cx, selectedSource);
|
||||
this.props.closeTab(cx, selectedSourceWithContent.source);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -251,13 +254,13 @@ class Editor extends PureComponent<Props, State> {
|
|||
|
||||
getCurrentLine() {
|
||||
const { codeMirror } = this.state.editor;
|
||||
const { selectedSource } = this.props;
|
||||
if (!selectedSource) {
|
||||
const { selectedSourceWithContent } = this.props;
|
||||
if (!selectedSourceWithContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const line = getCursorLine(codeMirror);
|
||||
return toSourceLine(selectedSource.id, line);
|
||||
return toSourceLine(selectedSourceWithContent.source.id, line);
|
||||
}
|
||||
|
||||
onToggleBreakpoint = (key, e: KeyboardEvent) => {
|
||||
|
@ -328,7 +331,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
|
||||
const {
|
||||
cx,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
breakpointActions,
|
||||
editorActions,
|
||||
isPaused,
|
||||
|
@ -336,7 +339,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
closeConditionalPanel,
|
||||
} = this.props;
|
||||
const { editor } = this.state;
|
||||
if (!selectedSource || !editor) {
|
||||
if (!selectedSourceWithContent || !editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -346,7 +349,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
const target: Element = (event.target: any);
|
||||
const { id: sourceId } = selectedSource;
|
||||
const { id: sourceId } = selectedSourceWithContent.source;
|
||||
const line = lineAtHeight(editor, sourceId, event);
|
||||
|
||||
if (typeof line != "number") {
|
||||
|
@ -382,7 +385,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
) => {
|
||||
const {
|
||||
cx,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
conditionalPanelLocation,
|
||||
closeConditionalPanel,
|
||||
addBreakpointAtLine,
|
||||
|
@ -391,13 +394,20 @@ class Editor extends PureComponent<Props, State> {
|
|||
} = this.props;
|
||||
|
||||
// ignore right clicks in the gutter
|
||||
if ((ev.ctrlKey && ev.button === 0) || ev.button === 2 || !selectedSource) {
|
||||
if (
|
||||
(ev.ctrlKey && ev.button === 0) ||
|
||||
ev.button === 2 ||
|
||||
!selectedSourceWithContent
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if user clicks gutter to set breakpoint on blackboxed source, un-blackbox the source.
|
||||
if (selectedSource && selectedSource.isBlackBoxed) {
|
||||
toggleBlackBox(cx, selectedSource);
|
||||
if (
|
||||
selectedSourceWithContent &&
|
||||
selectedSourceWithContent.source.isBlackBoxed
|
||||
) {
|
||||
toggleBlackBox(cx, selectedSourceWithContent.source);
|
||||
}
|
||||
|
||||
if (conditionalPanelLocation) {
|
||||
|
@ -408,7 +418,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
const sourceLine = toSourceLine(selectedSource.id, line);
|
||||
const sourceLine = toSourceLine(selectedSourceWithContent.source.id, line);
|
||||
if (typeof sourceLine !== "number") {
|
||||
return;
|
||||
}
|
||||
|
@ -425,12 +435,12 @@ class Editor extends PureComponent<Props, State> {
|
|||
};
|
||||
|
||||
onClick(e: MouseEvent) {
|
||||
const { cx, selectedSource, jumpToMappedLocation } = this.props;
|
||||
const { cx, selectedSourceWithContent, jumpToMappedLocation } = this.props;
|
||||
|
||||
if (selectedSource && e.metaKey && e.altKey) {
|
||||
if (selectedSourceWithContent && e.metaKey && e.altKey) {
|
||||
const sourceLocation = getSourceLocationFromMouseEvent(
|
||||
this.state.editor,
|
||||
selectedSource,
|
||||
selectedSourceWithContent.source,
|
||||
e
|
||||
);
|
||||
jumpToMappedLocation(cx, sourceLocation);
|
||||
|
@ -442,42 +452,42 @@ class Editor extends PureComponent<Props, State> {
|
|||
conditionalPanelLocation,
|
||||
closeConditionalPanel,
|
||||
openConditionalPanel,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
} = this.props;
|
||||
|
||||
if (conditionalPanelLocation) {
|
||||
return closeConditionalPanel();
|
||||
}
|
||||
|
||||
if (!selectedSource) {
|
||||
if (!selectedSourceWithContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
return openConditionalPanel(
|
||||
{
|
||||
line: line,
|
||||
sourceId: selectedSource.id,
|
||||
sourceUrl: selectedSource.url,
|
||||
sourceId: selectedSourceWithContent.source.id,
|
||||
sourceUrl: selectedSourceWithContent.source.url,
|
||||
},
|
||||
log
|
||||
);
|
||||
};
|
||||
|
||||
shouldScrollToLocation(nextProps, editor) {
|
||||
const { selectedLocation, selectedSource } = this.props;
|
||||
const { selectedLocation, selectedSourceWithContent } = this.props;
|
||||
if (
|
||||
!editor ||
|
||||
!nextProps.selectedSource ||
|
||||
!nextProps.selectedSourceWithContent ||
|
||||
!nextProps.selectedLocation ||
|
||||
!nextProps.selectedLocation.line ||
|
||||
!nextProps.selectedSource.content
|
||||
!nextProps.selectedSourceWithContent.content
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isFirstLoad =
|
||||
(!selectedSource || !selectedSource.content) &&
|
||||
nextProps.selectedSource.content;
|
||||
(!selectedSourceWithContent || !selectedSourceWithContent.content) &&
|
||||
nextProps.selectedSourceWithContent.content;
|
||||
const locationChanged = selectedLocation !== nextProps.selectedLocation;
|
||||
const symbolsChanged = nextProps.symbols != this.props.symbols;
|
||||
|
||||
|
@ -485,13 +495,16 @@ class Editor extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
scrollToLocation(nextProps, editor) {
|
||||
const { selectedLocation, selectedSource } = nextProps;
|
||||
const { selectedLocation, selectedSourceWithContent } = nextProps;
|
||||
|
||||
if (selectedLocation && this.shouldScrollToLocation(nextProps, editor)) {
|
||||
let { line, column } = toEditorPosition(selectedLocation);
|
||||
|
||||
if (selectedSource && hasDocument(selectedSource.id)) {
|
||||
const doc = getDocument(selectedSource.id);
|
||||
if (
|
||||
selectedSourceWithContent &&
|
||||
hasDocument(selectedSourceWithContent.source.id)
|
||||
) {
|
||||
const doc = getDocument(selectedSourceWithContent.source.id);
|
||||
const lineText: ?string = doc.getLine(line);
|
||||
column = Math.max(column, getIndentation(lineText));
|
||||
}
|
||||
|
@ -514,23 +527,23 @@ class Editor extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
setText(props, editor) {
|
||||
const { selectedSource, symbols } = props;
|
||||
const { selectedSourceWithContent, symbols } = props;
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we previously had a selected source
|
||||
if (!selectedSource) {
|
||||
if (!selectedSourceWithContent) {
|
||||
return this.clearEditor();
|
||||
}
|
||||
|
||||
if (!selectedSource.content) {
|
||||
if (!selectedSourceWithContent.content) {
|
||||
return showLoading(editor);
|
||||
}
|
||||
|
||||
if (selectedSource.content.state === "rejected") {
|
||||
let { value } = selectedSource.content;
|
||||
if (selectedSourceWithContent.content.state === "rejected") {
|
||||
let { value } = selectedSourceWithContent.content;
|
||||
if (typeof value !== "string") {
|
||||
value = "Unexpected source error";
|
||||
}
|
||||
|
@ -540,8 +553,8 @@ class Editor extends PureComponent<Props, State> {
|
|||
|
||||
return showSourceText(
|
||||
editor,
|
||||
selectedSource,
|
||||
selectedSource.content.value,
|
||||
selectedSourceWithContent.source,
|
||||
selectedSourceWithContent.content.value,
|
||||
symbols
|
||||
);
|
||||
}
|
||||
|
@ -581,13 +594,17 @@ class Editor extends PureComponent<Props, State> {
|
|||
renderItems() {
|
||||
const {
|
||||
cx,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
conditionalPanelLocation,
|
||||
isPaused,
|
||||
} = this.props;
|
||||
const { editor, contextMenu } = this.state;
|
||||
|
||||
if (!selectedSource || !editor || !getDocument(selectedSource.id)) {
|
||||
if (
|
||||
!selectedSourceWithContent ||
|
||||
!editor ||
|
||||
!getDocument(selectedSourceWithContent.source.id)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -604,7 +621,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
editor={editor}
|
||||
contextMenu={contextMenu}
|
||||
clearContextMenu={this.clearContextMenu}
|
||||
selectedSource={selectedSource}
|
||||
selectedSourceWithContent={selectedSourceWithContent}
|
||||
/>
|
||||
}
|
||||
{conditionalPanelLocation ? <ConditionalPanel editor={editor} /> : null}
|
||||
|
@ -612,7 +629,10 @@ class Editor extends PureComponent<Props, State> {
|
|||
<ColumnBreakpoints editor={editor} />
|
||||
) : null}
|
||||
{isPaused && features.inlinePreview ? (
|
||||
<InlinePreviews editor={editor} selectedSource={selectedSource} />
|
||||
<InlinePreviews
|
||||
editor={editor}
|
||||
selectedSource={selectedSourceWithContent.source}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
@ -621,7 +641,7 @@ class Editor extends PureComponent<Props, State> {
|
|||
renderSearchBar() {
|
||||
const { editor } = this.state;
|
||||
|
||||
if (!this.props.selectedSource) {
|
||||
if (!this.props.selectedSourceWithContent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -629,11 +649,13 @@ class Editor extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { selectedSource, skipPausing } = this.props;
|
||||
const { selectedSourceWithContent, skipPausing } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={classnames("editor-wrapper", {
|
||||
blackboxed: selectedSource && selectedSource.isBlackBoxed,
|
||||
blackboxed:
|
||||
selectedSourceWithContent &&
|
||||
selectedSourceWithContent.source.isBlackBoxed,
|
||||
"skip-pausing": skipPausing,
|
||||
})}
|
||||
ref={c => (this.$editorWrapper = c)}
|
||||
|
@ -654,15 +676,18 @@ Editor.contextTypes = {
|
|||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = getSelectedSourceWithContent(state);
|
||||
const selectedSourceWithContent = getSelectedSourceWithContent(state);
|
||||
|
||||
return {
|
||||
cx: getThreadContext(state),
|
||||
selectedLocation: getSelectedLocation(state),
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
searchOn: getActiveSearch(state) === "file",
|
||||
conditionalPanelLocation: getConditionalPanelLocation(state),
|
||||
symbols: getSymbols(state, selectedSource),
|
||||
symbols: getSymbols(
|
||||
state,
|
||||
selectedSourceWithContent ? selectedSourceWithContent.source : null
|
||||
),
|
||||
isPaused: getIsPaused(state, getCurrentThread(state)),
|
||||
skipPausing: getSkipPausing(state),
|
||||
};
|
||||
|
|
|
@ -168,7 +168,7 @@ const downloadFileItem = (
|
|||
export function editorMenuItems({
|
||||
cx,
|
||||
editorActions,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
location,
|
||||
selectionText,
|
||||
hasPrettySource,
|
||||
|
@ -177,7 +177,7 @@ export function editorMenuItems({
|
|||
}: {
|
||||
cx: ThreadContext,
|
||||
editorActions: EditorItemActions,
|
||||
selectedSource: SourceWithContent,
|
||||
selectedSourceWithContent: SourceWithContent,
|
||||
location: SourceLocation,
|
||||
selectionText: string,
|
||||
hasPrettySource: boolean,
|
||||
|
@ -185,11 +185,7 @@ export function editorMenuItems({
|
|||
isPaused: boolean,
|
||||
}) {
|
||||
const items = [];
|
||||
|
||||
const content =
|
||||
selectedSource.content && isFulfilled(selectedSource.content)
|
||||
? selectedSource.content.value
|
||||
: null;
|
||||
const { source: selectedSource, content } = selectedSourceWithContent;
|
||||
|
||||
items.push(
|
||||
jumpToMappedLocationItem(
|
||||
|
@ -201,15 +197,17 @@ export function editorMenuItems({
|
|||
),
|
||||
continueToHereItem(cx, location, isPaused, editorActions),
|
||||
{ type: "separator" },
|
||||
...(content ? [copyToClipboardItem(content, editorActions)] : []),
|
||||
...(content && isFulfilled(content)
|
||||
? [copyToClipboardItem(content.value, editorActions)]
|
||||
: []),
|
||||
...(!selectedSource.isWasm
|
||||
? [
|
||||
copySourceItem(selectedSource, selectionText, editorActions),
|
||||
copySourceUri2Item(selectedSource, editorActions),
|
||||
]
|
||||
: []),
|
||||
...(content
|
||||
? [downloadFileItem(selectedSource, content, editorActions)]
|
||||
...(content && isFulfilled(content)
|
||||
? [downloadFileItem(selectedSource, content.value, editorActions)]
|
||||
: []),
|
||||
{ type: "separator" },
|
||||
showSourceMenuItem(cx, selectedSource, editorActions),
|
||||
|
|
|
@ -34,7 +34,7 @@ function generateDefaults(editor, overrides) {
|
|||
},
|
||||
frame: null,
|
||||
source: ({
|
||||
...createSourceObject("foo"),
|
||||
source: createSourceObject("foo"),
|
||||
content: null,
|
||||
}: SourceWithContent),
|
||||
...overrides,
|
||||
|
@ -57,7 +57,7 @@ function render(overrides = {}) {
|
|||
const props = generateDefaults(editor, overrides);
|
||||
|
||||
const doc = createMockDocument(clear);
|
||||
setDocument(props.source.id, doc);
|
||||
setDocument(props.source.source.id, doc);
|
||||
|
||||
// $FlowIgnore
|
||||
const component = shallow(<DebugLine.WrappedComponent {...props} />, {
|
||||
|
@ -71,7 +71,7 @@ describe("DebugLine Component", () => {
|
|||
it("should show a new debug line", async () => {
|
||||
const { component, props, doc } = render({
|
||||
source: {
|
||||
...createSourceObject("foo"),
|
||||
source: createSourceObject("foo"),
|
||||
content: asyncValue.fulfilled({
|
||||
type: "text",
|
||||
value: "",
|
||||
|
@ -94,7 +94,7 @@ describe("DebugLine Component", () => {
|
|||
it("should replace the first debug line", async () => {
|
||||
const { props, component, clear, doc } = render({
|
||||
source: {
|
||||
...createSourceObject("foo"),
|
||||
source: createSourceObject("foo"),
|
||||
content: asyncValue.fulfilled({
|
||||
type: "text",
|
||||
value: "",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import Editor from "../index";
|
||||
import type { Source, SourceWithContent, SourceBase } from "../../../types";
|
||||
import type { Source, SourceWithContent } from "../../../types";
|
||||
import { getDocument } from "../../../utils/editor/source-documents";
|
||||
import * as asyncValue from "../../../utils/async-value";
|
||||
|
||||
|
@ -74,7 +74,7 @@ function createMockSourceWithContent(
|
|||
...otherOverrides
|
||||
} = overrides;
|
||||
|
||||
const source: SourceBase = ({
|
||||
const source: Source = ({
|
||||
id: "foo",
|
||||
url: "foo",
|
||||
...otherOverrides,
|
||||
|
@ -95,7 +95,7 @@ function createMockSourceWithContent(
|
|||
}
|
||||
|
||||
return {
|
||||
...source,
|
||||
source,
|
||||
content,
|
||||
};
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ describe("Editor", () => {
|
|||
const { component, mockEditor } = render();
|
||||
await component.setState({ editor: mockEditor });
|
||||
component.setProps({
|
||||
selectedSource: {
|
||||
selectedSourceWithContent: {
|
||||
source: { loadedState: "loading" },
|
||||
content: null,
|
||||
},
|
||||
|
@ -148,7 +148,7 @@ describe("Editor", () => {
|
|||
await component.setState({ editor: mockEditor });
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
}),
|
||||
selectedLocation: { sourceId: "foo", line: 3, column: 1 },
|
||||
|
@ -166,7 +166,7 @@ describe("Editor", () => {
|
|||
await component.setState({ editor: mockEditor });
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
text: undefined,
|
||||
error: "error text",
|
||||
|
@ -185,7 +185,7 @@ describe("Editor", () => {
|
|||
await component.setState({ editor: mockEditor });
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
isWasm: true,
|
||||
text: undefined,
|
||||
|
@ -207,7 +207,7 @@ describe("Editor", () => {
|
|||
await component.setState({ editor: mockEditor });
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
}),
|
||||
selectedLocation: { sourceId: "foo", line: 3, column: 1 },
|
||||
|
@ -216,7 +216,7 @@ describe("Editor", () => {
|
|||
// navigate to a new source that is still loading
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
id: "bar",
|
||||
loadedState: "loading",
|
||||
}),
|
||||
|
@ -237,17 +237,17 @@ describe("Editor", () => {
|
|||
|
||||
await component.setState({ editor: mockEditor });
|
||||
|
||||
const selectedSource = createMockSourceWithContent({
|
||||
const selectedSourceWithContent = createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
contentType: "javascript",
|
||||
});
|
||||
|
||||
await component.setProps({ ...props, selectedSource });
|
||||
await component.setProps({ ...props, selectedSourceWithContent });
|
||||
|
||||
const symbols = { hasJsx: true };
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
symbols,
|
||||
});
|
||||
|
||||
|
@ -262,29 +262,31 @@ describe("Editor", () => {
|
|||
|
||||
await component.setState({ editor: mockEditor });
|
||||
|
||||
const selectedSource = createMockSourceWithContent({
|
||||
const selectedSourceWithContent = createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
contentType: "javascript",
|
||||
});
|
||||
|
||||
await component.setProps({ ...props, selectedSource });
|
||||
await component.setProps({ ...props, selectedSourceWithContent });
|
||||
|
||||
// symbols are parsed
|
||||
const symbols = { hasJsx: true };
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
symbols,
|
||||
});
|
||||
|
||||
// selectedLocation changes e.g. pausing/stepping
|
||||
mockEditor.codeMirror.doc = getDocument(selectedSource.id);
|
||||
mockEditor.codeMirror.doc = getDocument(
|
||||
selectedSourceWithContent.source.id
|
||||
);
|
||||
mockEditor.codeMirror.getOption = () => ({ name: "jsx" });
|
||||
const selectedLocation = { sourceId: "foo", line: 4, column: 1 };
|
||||
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource,
|
||||
selectedSourceWithContent,
|
||||
symbols,
|
||||
selectedLocation,
|
||||
});
|
||||
|
@ -303,7 +305,7 @@ describe("Editor", () => {
|
|||
await component.setState({ editor: mockEditor });
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
loadedState: "loading",
|
||||
}),
|
||||
selectedLocation: { sourceId: "foo", line: 1, column: 1 },
|
||||
|
@ -312,7 +314,7 @@ describe("Editor", () => {
|
|||
// navigate to a new source that is still loading
|
||||
await component.setProps({
|
||||
...props,
|
||||
selectedSource: createMockSourceWithContent({
|
||||
selectedSourceWithContent: createMockSourceWithContent({
|
||||
loadedState: "loaded",
|
||||
}),
|
||||
selectedLocation: { sourceId: "foo", line: 1, column: 1 },
|
||||
|
|
|
@ -29,8 +29,8 @@ function generateDefaults(overrides) {
|
|||
},
|
||||
},
|
||||
endPanelCollapsed: false,
|
||||
selectedSource: {
|
||||
...createSourceObject("foo"),
|
||||
selectedSourceWithContent: {
|
||||
source: createSourceObject("foo"),
|
||||
content: null,
|
||||
},
|
||||
...overrides,
|
||||
|
@ -42,7 +42,7 @@ function render(overrides = {}, position = { line: 0, column: 0 }) {
|
|||
const props = generateDefaults(overrides);
|
||||
|
||||
const doc = createMockDocument(clear, position);
|
||||
setDocument(props.selectedSource.id, doc);
|
||||
setDocument(props.selectedSourceWithContent.source.id, doc);
|
||||
|
||||
// $FlowIgnore
|
||||
const component = shallow(<SourceFooter.WrappedComponent {...props} />, {
|
||||
|
|
|
@ -263,12 +263,14 @@ export class Outline extends Component<Props, State> {
|
|||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = getSelectedSourceWithContent(state);
|
||||
const symbols = selectedSource ? getSymbols(state, selectedSource) : null;
|
||||
const symbols = selectedSource
|
||||
? getSymbols(state, selectedSource.source)
|
||||
: null;
|
||||
|
||||
return {
|
||||
cx: getContext(state),
|
||||
symbols,
|
||||
selectedSource: (selectedSource: ?Source),
|
||||
selectedSource: selectedSource && selectedSource.source,
|
||||
selectedLocation: getSelectedLocation(state),
|
||||
getFunctionText: line => {
|
||||
if (selectedSource) {
|
||||
|
|
|
@ -206,8 +206,8 @@ describe("Frames", () => {
|
|||
];
|
||||
|
||||
const sources: SourceResourceState = insertResources(createInitial(), [
|
||||
{ ...source1, content: null },
|
||||
{ ...source2, content: null },
|
||||
source1,
|
||||
source2,
|
||||
]);
|
||||
|
||||
const processedFrames = formatCallStackFrames(frames, sources, source1);
|
||||
|
|
|
@ -6,35 +6,20 @@
|
|||
|
||||
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,
|
||||
|
@ -62,12 +47,6 @@ 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 };
|
||||
|
@ -81,14 +60,7 @@ export default function update(
|
|||
switch (action.type) {
|
||||
case "INSERT_SOURCE_ACTORS": {
|
||||
const { items } = action;
|
||||
state = insertResources(
|
||||
state,
|
||||
items.map(item => ({
|
||||
...item,
|
||||
breakpointPositions: new Map(),
|
||||
breakableLines: null,
|
||||
}))
|
||||
);
|
||||
state = insertResources(state, items);
|
||||
break;
|
||||
}
|
||||
case "REMOVE_SOURCE_ACTORS": {
|
||||
|
@ -101,58 +73,13 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
export function resourceAsSourceActor(r: SourceActorResource): SourceActor {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Because we are using an opaque type for our source actor IDs, these
|
||||
|
@ -174,7 +101,7 @@ export function getSourceActor(
|
|||
state: SourceActorOuterState,
|
||||
id: SourceActorId
|
||||
): SourceActor {
|
||||
return getMappedResource(state.sourceActors, id, resourceAsSourceActor);
|
||||
return getResource(state.sourceActors, id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,40 +166,3 @@ 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;
|
||||
}, [])
|
||||
),
|
||||
});
|
||||
|
|
|
@ -24,9 +24,7 @@ import {
|
|||
updateResources,
|
||||
hasResource,
|
||||
getResource,
|
||||
getMappedResource,
|
||||
getResourceIds,
|
||||
memoizeResourceShallow,
|
||||
makeReduceQuery,
|
||||
makeReduceAllQuery,
|
||||
makeMapWithArgs,
|
||||
|
@ -47,7 +45,6 @@ import {
|
|||
getSourceActor,
|
||||
getSourceActors,
|
||||
getThreadsBySource,
|
||||
getBreakableLinesForSourceActors,
|
||||
type SourceActorId,
|
||||
type SourceActorOuterState,
|
||||
} from "./source-actors";
|
||||
|
@ -69,6 +66,9 @@ import type { DebuggeeState } from "./debuggee";
|
|||
import { uniq } from "lodash";
|
||||
|
||||
export type SourcesMap = { [SourceId]: Source };
|
||||
type SourcesContentMap = {
|
||||
[SourceId]: AsyncValue<SourceContent> | null,
|
||||
};
|
||||
export type SourcesMapByThread = { [ThreadId]: SourcesMap };
|
||||
|
||||
export type BreakpointPositionsMap = { [SourceId]: BreakpointPositions };
|
||||
|
@ -77,23 +77,8 @@ type SourceActorMap = { [SourceId]: Array<SourceActorId> };
|
|||
type UrlsMap = { [string]: SourceId[] };
|
||||
type PlainUrlsMap = { [string]: string[] };
|
||||
|
||||
export type SourceBase = {|
|
||||
+id: SourceId,
|
||||
+url: string,
|
||||
+sourceMapURL?: string,
|
||||
+isBlackBoxed: boolean,
|
||||
+isPrettyPrinted: boolean,
|
||||
+relativeUrl: string,
|
||||
+introductionUrl: ?string,
|
||||
+introductionType: ?string,
|
||||
+extensionName: ?string,
|
||||
+isExtension: boolean,
|
||||
+isWasm: boolean,
|
||||
|};
|
||||
|
||||
type SourceResource = Resource<{
|
||||
...SourceBase,
|
||||
content: AsyncValue<SourceContent> | null,
|
||||
...Source,
|
||||
}>;
|
||||
export type SourceResourceState = ResourceState<SourceResource>;
|
||||
|
||||
|
@ -103,6 +88,8 @@ export type SourcesState = {
|
|||
// All known sources.
|
||||
sources: SourceResourceState,
|
||||
|
||||
content: SourcesContentMap,
|
||||
|
||||
breakpointPositions: BreakpointPositionsMap,
|
||||
breakableLines: { [SourceId]: Array<number> },
|
||||
|
||||
|
@ -217,7 +204,7 @@ function update(
|
|||
case "SET_PROJECT_DIRECTORY_ROOT":
|
||||
return updateProjectDirectoryRoot(state, action.url);
|
||||
|
||||
case "SET_ORIGINAL_BREAKABLE_LINES": {
|
||||
case "SET_BREAKABLE_LINES": {
|
||||
const { breakableLines, sourceId } = action;
|
||||
return {
|
||||
...state,
|
||||
|
@ -253,45 +240,36 @@ function update(
|
|||
return state;
|
||||
}
|
||||
|
||||
const resourceAsSourceBase = memoizeResourceShallow(
|
||||
({ content, ...source }: SourceResource): SourceBase => source
|
||||
);
|
||||
|
||||
const resourceAsSourceWithContent = memoizeResourceShallow(
|
||||
({ content, ...source }: SourceResource): SourceWithContent => ({
|
||||
...source,
|
||||
content: asyncValue.asSettled(content),
|
||||
})
|
||||
);
|
||||
function resourceAsSource(r: SourceResource): Source {
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add sources to the sources store
|
||||
* - Add the source to the sources store
|
||||
* - Add the source URL to the urls map
|
||||
*/
|
||||
function addSources(state: SourcesState, sources: SourceBase[]): SourcesState {
|
||||
function addSources(state: SourcesState, sources: Source[]): SourcesState {
|
||||
state = {
|
||||
...state,
|
||||
content: { ...state.content },
|
||||
urls: { ...state.urls },
|
||||
plainUrls: { ...state.plainUrls },
|
||||
};
|
||||
|
||||
state.sources = insertResources(
|
||||
state.sources,
|
||||
sources.map(source => ({
|
||||
...source,
|
||||
content: null,
|
||||
}))
|
||||
);
|
||||
state.sources = insertResources(state.sources, sources);
|
||||
|
||||
for (const source of sources) {
|
||||
// 1. Update the source url map
|
||||
// 1. Add the source to the sources map
|
||||
state.content[source.id] = null;
|
||||
|
||||
// 2. Update the source url map
|
||||
const existing = state.urls[source.url] || [];
|
||||
if (!existing.includes(source.id)) {
|
||||
state.urls[source.url] = [...existing, source.id];
|
||||
}
|
||||
|
||||
// 2. Update the plain url map
|
||||
// 3. Update the plain url map
|
||||
if (source.url) {
|
||||
const plainUrl = getPlainUrl(source.url);
|
||||
const existingPlainUrls = state.plainUrls[plainUrl] || [];
|
||||
|
@ -375,7 +353,7 @@ function updateProjectDirectoryRoot(state: SourcesState, root: string) {
|
|||
|
||||
function updateRootRelativeValues(
|
||||
state: SourcesState,
|
||||
sources?: $ReadOnlyArray<Source>
|
||||
sources?: Array<Source>
|
||||
) {
|
||||
const ids = sources
|
||||
? sources.map(source => source.id)
|
||||
|
@ -411,7 +389,7 @@ function updateLoadedState(
|
|||
|
||||
// If there was a navigation between the time the action was started and
|
||||
// completed, we don't want to update the store.
|
||||
if (action.epoch !== state.epoch || !hasResource(state.sources, sourceId)) {
|
||||
if (action.epoch !== state.epoch || !(sourceId in state.content)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -435,12 +413,10 @@ function updateLoadedState(
|
|||
|
||||
return {
|
||||
...state,
|
||||
sources: updateResources(state.sources, [
|
||||
{
|
||||
id: sourceId,
|
||||
content,
|
||||
content: {
|
||||
...state.content,
|
||||
[sourceId]: content,
|
||||
},
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -538,9 +514,7 @@ export function getSourceInSources(
|
|||
sources: SourceResourceState,
|
||||
id: string
|
||||
): ?Source {
|
||||
return hasResource(sources, id)
|
||||
? getMappedResource(sources, id, resourceAsSourceBase)
|
||||
: null;
|
||||
return hasResource(sources, id) ? getResource(sources, id) : null;
|
||||
}
|
||||
|
||||
export function getSource(state: OuterState, id: SourceId): ?Source {
|
||||
|
@ -574,9 +548,7 @@ export function getSourcesByURLInSources(
|
|||
if (!url || !urls[url]) {
|
||||
return [];
|
||||
}
|
||||
return urls[url].map(id =>
|
||||
getMappedResource(sources, id, resourceAsSourceBase)
|
||||
);
|
||||
return urls[url].map(id => getResource(sources, id));
|
||||
}
|
||||
|
||||
export function getSourcesByURL(state: OuterState, url: string): Source[] {
|
||||
|
@ -695,7 +667,7 @@ export function getHasSiblingOfSameName(state: OuterState, source: ?Source) {
|
|||
const querySourceList: ReduceAllQuery<
|
||||
SourceResource,
|
||||
Array<Source>
|
||||
> = makeReduceAllQuery(resourceAsSourceBase, sources => sources.slice());
|
||||
> = makeReduceAllQuery(resourceAsSource, sources => sources.slice());
|
||||
|
||||
export function getSources(state: OuterState): SourceResourceState {
|
||||
return state.sources.sources;
|
||||
|
@ -762,15 +734,17 @@ type GSSWC = Selector<?SourceWithContent>;
|
|||
export const getSelectedSourceWithContent: GSSWC = createSelector(
|
||||
getSelectedLocation,
|
||||
getSources,
|
||||
state => state.sources.content,
|
||||
(
|
||||
selectedLocation: ?SourceLocation,
|
||||
sources: SourceResourceState
|
||||
sources: SourceResourceState,
|
||||
content: SourcesContentMap
|
||||
): SourceWithContent | null => {
|
||||
const source =
|
||||
selectedLocation &&
|
||||
getSourceInSources(sources, selectedLocation.sourceId);
|
||||
return source
|
||||
? getMappedResource(sources, source.id, resourceAsSourceWithContent)
|
||||
? getSourceWithContentInner(sources, content, source.id)
|
||||
: null;
|
||||
}
|
||||
);
|
||||
|
@ -778,18 +752,49 @@ export function getSourceWithContent(
|
|||
state: OuterState,
|
||||
id: SourceId
|
||||
): SourceWithContent {
|
||||
return getMappedResource(
|
||||
return getSourceWithContentInner(
|
||||
state.sources.sources,
|
||||
id,
|
||||
resourceAsSourceWithContent
|
||||
state.sources.content,
|
||||
id
|
||||
);
|
||||
}
|
||||
export function getSourceContent(
|
||||
state: OuterState,
|
||||
id: SourceId
|
||||
): SettledValue<SourceContent> | null {
|
||||
const { content } = getResource(state.sources.sources, id);
|
||||
return asyncValue.asSettled(content);
|
||||
// Assert the resource exists.
|
||||
getResource(state.sources.sources, id);
|
||||
const content = state.sources.content[id];
|
||||
|
||||
if (!content || content.state === "pending") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
const contentLookup: WeakMap<Source, SourceWithContent> = new WeakMap();
|
||||
function getSourceWithContentInner(
|
||||
sources: SourceResourceState,
|
||||
content: SourcesContentMap,
|
||||
id: SourceId
|
||||
): SourceWithContent {
|
||||
const source = getResource(sources, id);
|
||||
let contentValue = content[source.id];
|
||||
|
||||
let result = contentLookup.get(source);
|
||||
if (!result || result.content !== contentValue) {
|
||||
if (contentValue && contentValue.state === "pending") {
|
||||
contentValue = null;
|
||||
}
|
||||
result = {
|
||||
source,
|
||||
content: contentValue,
|
||||
};
|
||||
contentLookup.set(source, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getSelectedSourceId(state: OuterState) {
|
||||
|
@ -965,40 +970,24 @@ export function getBreakpointPositionsForLocation(
|
|||
return findPosition(positions, location);
|
||||
}
|
||||
|
||||
export function getBreakableLines(
|
||||
state: OuterState & SourceActorOuterState,
|
||||
sourceId: string
|
||||
): ?Array<number> {
|
||||
export function getBreakableLines(state: OuterState, sourceId: string) {
|
||||
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 && getBreakableLines(state, sourceId);
|
||||
return sourceId && state.sources.breakableLines[sourceId];
|
||||
},
|
||||
breakableLines => new Set(breakableLines || [])
|
||||
);
|
||||
|
||||
export function isSourceLoadingOrLoaded(state: OuterState, sourceId: string) {
|
||||
const { content } = getResource(state.sources.sources, sourceId);
|
||||
const content = state.sources.content[sourceId];
|
||||
return content !== null;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ declare var expect: (value: any) => any;
|
|||
import update, { initialSourcesState, getDisplayedSources } from "../sources";
|
||||
import { initialDebuggeeState } from "../debuggee";
|
||||
import updateSourceActors from "../source-actors";
|
||||
import type { SourceActor } from "../../types";
|
||||
import type { Source, SourceActor } from "../../types";
|
||||
import { prefs } from "../../utils/prefs";
|
||||
import { makeMockSource, mockcx } from "../../utils/test-mockup";
|
||||
import { getResourceIds } from "../../utils/resource";
|
||||
|
@ -82,7 +82,7 @@ describe("sources selectors", () => {
|
|||
sources: update(state, {
|
||||
type: "ADD_SOURCES",
|
||||
cx: mockcx,
|
||||
sources: mockedSources,
|
||||
sources: ((mockedSources: any): Source[]),
|
||||
}),
|
||||
sourceActors: undefined,
|
||||
};
|
||||
|
@ -106,7 +106,7 @@ describe("sources selectors", () => {
|
|||
sources: update(state, {
|
||||
type: "ADD_SOURCES",
|
||||
cx: mockcx,
|
||||
sources: mockedSources,
|
||||
sources: ((mockedSources: any): Source[]),
|
||||
}),
|
||||
sourceActors: undefined,
|
||||
};
|
||||
|
|
|
@ -148,12 +148,17 @@ export function getColumnBreakpoints(
|
|||
positions: BreakpointPosition[],
|
||||
breakpoints: ?(Breakpoint[]),
|
||||
viewport: ?Range,
|
||||
selectedSource: ?SourceWithContent
|
||||
selectedSourceWithContent: ?SourceWithContent
|
||||
) {
|
||||
if (!positions || !selectedSource) {
|
||||
if (!positions || !selectedSourceWithContent) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const {
|
||||
source: selectedSource,
|
||||
content: selectedContent,
|
||||
} = selectedSourceWithContent;
|
||||
|
||||
// We only want to show a column breakpoint if several conditions are matched
|
||||
// - it is the first breakpoint to appear at an the original location
|
||||
// - the position is in the current viewport
|
||||
|
@ -162,7 +167,7 @@ export function getColumnBreakpoints(
|
|||
const breakpointMap = groupBreakpoints(breakpoints, selectedSource);
|
||||
positions = filterByLineCount(positions, selectedSource);
|
||||
positions = filterVisible(positions, selectedSource, viewport);
|
||||
positions = filterInLine(positions, selectedSource, selectedSource.content);
|
||||
positions = filterInLine(positions, selectedSource, selectedContent);
|
||||
positions = filterByBreakpoints(positions, selectedSource, breakpointMap);
|
||||
|
||||
return formatPositions(positions, selectedSource, breakpointMap);
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
import type { SettledValue, FulfilledValue } from "./utils/async-value";
|
||||
import type { SourcePayload } from "./client/firefox/types";
|
||||
import type { SourceActorId, SourceActor } from "./reducers/source-actors";
|
||||
import type { SourceBase } from "./reducers/sources";
|
||||
|
||||
export type { SourceActorId, SourceActor, SourceBase };
|
||||
export type { SourceActorId, SourceActor };
|
||||
|
||||
export type SearchModifiers = {
|
||||
caseSensitive: boolean,
|
||||
|
@ -382,14 +381,14 @@ export type WasmSourceContent = {|
|
|||
|};
|
||||
export type SourceContent = TextSourceContent | WasmSourceContent;
|
||||
|
||||
export type SourceWithContent = $ReadOnly<{
|
||||
...SourceBase,
|
||||
export type SourceWithContent = {|
|
||||
source: Source,
|
||||
+content: SettledValue<SourceContent> | null,
|
||||
}>;
|
||||
export type SourceWithContentAndType<+Content: SourceContent> = $ReadOnly<{
|
||||
...SourceBase,
|
||||
|};
|
||||
export type SourceWithContentAndType<+Content: SourceContent> = {|
|
||||
source: Source,
|
||||
+content: FulfilledValue<Content>,
|
||||
}>;
|
||||
|};
|
||||
|
||||
/**
|
||||
* Source
|
||||
|
@ -398,7 +397,7 @@ export type SourceWithContentAndType<+Content: SourceContent> = $ReadOnly<{
|
|||
* @static
|
||||
*/
|
||||
|
||||
export type Source = {
|
||||
export type Source = {|
|
||||
+id: SourceId,
|
||||
+url: string,
|
||||
+sourceMapURL?: string,
|
||||
|
@ -410,7 +409,7 @@ export type Source = {
|
|||
+extensionName: ?string,
|
||||
+isExtension: boolean,
|
||||
+isWasm: boolean,
|
||||
};
|
||||
|};
|
||||
|
||||
/**
|
||||
* Script
|
||||
|
|
|
@ -28,12 +28,6 @@ export function rejected(value: mixed): RejectedValue {
|
|||
return { state: "rejected", value };
|
||||
}
|
||||
|
||||
export function asSettled<T>(
|
||||
value: AsyncValue<T> | null
|
||||
): SettledValue<T> | null {
|
||||
return value && value.state !== "pending" ? value : null;
|
||||
}
|
||||
|
||||
export function isPending(value: AsyncValue<mixed>): boolean %checks {
|
||||
return value.state === "pending";
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ import { getSymbols } from "../../../workers/parser/getSymbols";
|
|||
import cases from "jest-in-case";
|
||||
|
||||
async function setup({ file, location, functionName, original }) {
|
||||
const source = original ? populateOriginalSource(file) : populateSource(file);
|
||||
const { source } = original
|
||||
? populateOriginalSource(file)
|
||||
: populateSource(file);
|
||||
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import { makeMockSource, makeMockSourceAndContent } from "../../test-mockup";
|
|||
|
||||
describe("shouldShowPrettyPrint", () => {
|
||||
it("shows pretty print for a source", () => {
|
||||
const { content, ...source } = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"http://example.com/index.js",
|
||||
"test-id-123",
|
||||
"text/javascript",
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { Symbols } from "../reducers/ast";
|
|||
|
||||
export function findFunctionText(
|
||||
line: number,
|
||||
source: SourceWithContent,
|
||||
{ source, content }: SourceWithContent,
|
||||
symbols: ?Symbols
|
||||
): ?string {
|
||||
const func = findClosestFunction(symbols, {
|
||||
|
@ -23,9 +23,9 @@ export function findFunctionText(
|
|||
if (
|
||||
source.isWasm ||
|
||||
!func ||
|
||||
!source.content ||
|
||||
!isFulfilled(source.content) ||
|
||||
source.content.value.type !== "text"
|
||||
!content ||
|
||||
!isFulfilled(content) ||
|
||||
content.value.type !== "text"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ export function findFunctionText(
|
|||
const {
|
||||
location: { start, end },
|
||||
} = func;
|
||||
const lines = source.content.value.value.split("\n");
|
||||
const lines = content.value.value.split("\n");
|
||||
const firstLine = lines[start.line - 1].slice(start.column);
|
||||
const lastLine = lines[end.line - 1].slice(0, end.column);
|
||||
const middle = lines.slice(start.line, end.line - 1);
|
||||
|
|
|
@ -13,20 +13,16 @@ const INDENT_COUNT_THRESHOLD = 5;
|
|||
const CHARACTER_LIMIT = 250;
|
||||
const _minifiedCache = new Map();
|
||||
|
||||
export function isMinified(source: SourceWithContent) {
|
||||
export function isMinified({ source, content }: SourceWithContent) {
|
||||
if (_minifiedCache.has(source.id)) {
|
||||
return _minifiedCache.get(source.id);
|
||||
}
|
||||
|
||||
if (
|
||||
!source.content ||
|
||||
!isFulfilled(source.content) ||
|
||||
source.content.value.type !== "text"
|
||||
) {
|
||||
if (!content || !isFulfilled(content) || content.value.type !== "text") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let text = source.content.value.value;
|
||||
let text = content.value.value;
|
||||
|
||||
let lineEndIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
|
|
|
@ -5,22 +5,25 @@
|
|||
// @flow
|
||||
|
||||
import type { ThunkArgs } from "../actions/types";
|
||||
import { asSettled, type AsyncValue } from "./async-value";
|
||||
|
||||
export type MemoizedAction<
|
||||
Args,
|
||||
Result
|
||||
> = Args => ThunkArgs => Promise<Result | null>;
|
||||
> = Args => ThunkArgs => Promise<?Result>;
|
||||
type MemoizableActionParams<Args, Result> = {
|
||||
getValue: (args: Args, thunkArgs: ThunkArgs) => AsyncValue<Result> | null,
|
||||
exitEarly?: (args: Args, thunkArgs: ThunkArgs) => boolean,
|
||||
hasValue: (args: Args, thunkArgs: ThunkArgs) => boolean,
|
||||
getValue: (args: Args, thunkArgs: ThunkArgs) => Result,
|
||||
createKey: (args: Args, thunkArgs: ThunkArgs) => string,
|
||||
action: (args: Args, thunkArgs: ThunkArgs) => Promise<mixed>,
|
||||
action: (args: Args, thunkArgs: ThunkArgs) => Promise<Result>,
|
||||
};
|
||||
|
||||
/*
|
||||
* memoizableActon is a utility for actions that should only be performed
|
||||
* once per key. It is useful for loading sources, parsing symbols ...
|
||||
*
|
||||
* @exitEarly - if true, do not attempt to perform the action
|
||||
* @hasValue - checks to see if the result is in the redux store
|
||||
* @getValue - gets the result from the redux store
|
||||
* @createKey - creates a key for the requests map
|
||||
* @action - kicks off the async work for the action
|
||||
|
@ -41,12 +44,24 @@ type MemoizableActionParams<Args, Result> = {
|
|||
*/
|
||||
export function memoizeableAction<Args, Result>(
|
||||
name: string,
|
||||
{ getValue, createKey, action }: MemoizableActionParams<Args, Result>
|
||||
{
|
||||
hasValue,
|
||||
getValue,
|
||||
createKey,
|
||||
action,
|
||||
exitEarly,
|
||||
}: MemoizableActionParams<Args, Result>
|
||||
): MemoizedAction<Args, Result> {
|
||||
const requests = new Map();
|
||||
return args => async thunkArgs => {
|
||||
let result = asSettled(getValue(args, thunkArgs));
|
||||
if (!result) {
|
||||
return args => async (thunkArgs: ThunkArgs) => {
|
||||
if (exitEarly && exitEarly(args, thunkArgs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasValue(args, thunkArgs)) {
|
||||
return getValue(args, thunkArgs);
|
||||
}
|
||||
|
||||
const key = createKey(args, thunkArgs);
|
||||
if (!requests.has(key)) {
|
||||
requests.set(
|
||||
|
@ -64,24 +79,6 @@ export function memoizeableAction<Args, Result>(
|
|||
}
|
||||
|
||||
await requests.get(key);
|
||||
|
||||
result = asSettled(getValue(args, thunkArgs));
|
||||
|
||||
if (!result) {
|
||||
// Returning null here is not ideal. This means that the action
|
||||
// resolved but 'getValue' didn't return a loaded value, for instance
|
||||
// if the data the action was meant to store was deleted. In a perfect
|
||||
// world we'd throw a ContextError here or handle cancellation somehow.
|
||||
// Throwing will also allow us to change the return type on the action
|
||||
// to always return a promise for the getValue AsyncValue type, but
|
||||
// for now we have to add an additional '| null' for this case.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.state === "rejected") {
|
||||
throw result.value;
|
||||
}
|
||||
return result.value;
|
||||
return getValue(args, thunkArgs);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,12 +27,8 @@ import type {
|
|||
Why,
|
||||
} from "../types";
|
||||
import * as asyncValue from "./async-value";
|
||||
import type { SourceBase } from "../reducers/sources";
|
||||
|
||||
function makeMockSource(
|
||||
url: string = "url",
|
||||
id: SourceId = "source"
|
||||
): SourceBase {
|
||||
function makeMockSource(url: string = "url", id: SourceId = "source"): Source {
|
||||
return {
|
||||
id,
|
||||
url,
|
||||
|
@ -56,7 +52,7 @@ function makeMockSourceWithContent(
|
|||
const source = makeMockSource(url, id);
|
||||
|
||||
return {
|
||||
...source,
|
||||
source,
|
||||
content: text
|
||||
? asyncValue.fulfilled({
|
||||
type: "text",
|
||||
|
@ -72,11 +68,11 @@ function makeMockSourceAndContent(
|
|||
id?: SourceId,
|
||||
contentType?: string = "text/javascript",
|
||||
text: string = ""
|
||||
): { ...SourceBase, content: TextSourceContent } {
|
||||
): { source: Source, content: TextSourceContent } {
|
||||
const source = makeMockSource(url, id);
|
||||
|
||||
return {
|
||||
...source,
|
||||
source,
|
||||
content: {
|
||||
type: "text",
|
||||
value: text,
|
||||
|
@ -85,7 +81,7 @@ function makeMockSourceAndContent(
|
|||
};
|
||||
}
|
||||
|
||||
function makeMockWasmSource(): SourceBase {
|
||||
function makeMockWasmSource(): Source {
|
||||
return {
|
||||
id: "wasm-source-id",
|
||||
url: "url",
|
||||
|
@ -106,7 +102,7 @@ function makeMockWasmSourceWithContent(text: {|
|
|||
const source = makeMockWasmSource();
|
||||
|
||||
return {
|
||||
...source,
|
||||
source,
|
||||
content: asyncValue.fulfilled({
|
||||
type: "wasm",
|
||||
value: text,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { getSymbols } from "../../workers/parser/getSymbols";
|
|||
import { populateSource } from "../../workers/parser/tests/helpers";
|
||||
|
||||
describe("find the best expression for the token", () => {
|
||||
const source = populateSource("computed-props");
|
||||
const { source } = populateSource("computed-props");
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
it("should find the identifier", () => {
|
||||
|
|
|
@ -13,14 +13,14 @@ describe("function", () => {
|
|||
describe("findFunctionText", () => {
|
||||
it("finds function", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const symbols = getSymbols(source.source.id);
|
||||
const text = findFunctionText(14, source, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("finds function signature", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const symbols = getSymbols(source.source.id);
|
||||
|
||||
const text = findFunctionText(13, source, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
|
@ -28,7 +28,7 @@ describe("function", () => {
|
|||
|
||||
it("misses function closing brace", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const symbols = getSymbols(source.source.id);
|
||||
|
||||
const text = findFunctionText(15, source, symbols);
|
||||
|
||||
|
@ -38,7 +38,7 @@ describe("function", () => {
|
|||
|
||||
it("finds property function", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const symbols = getSymbols(source.source.id);
|
||||
|
||||
const text = findFunctionText(29, source, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
|
@ -46,7 +46,7 @@ describe("function", () => {
|
|||
|
||||
it("finds class function", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const symbols = getSymbols(source.source.id);
|
||||
|
||||
const text = findFunctionText(33, source, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
|
@ -54,7 +54,7 @@ describe("function", () => {
|
|||
|
||||
it("cant find function", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const symbols = getSymbols(source.source.id);
|
||||
|
||||
const text = findFunctionText(20, source, symbols);
|
||||
expect(text).toEqual(null);
|
||||
|
|
|
@ -237,43 +237,47 @@ describe("sources", () => {
|
|||
describe("isJavaScript", () => {
|
||||
it("is not JavaScript", () => {
|
||||
{
|
||||
const source = makeMockSourceAndContent("foo.html", undefined, "");
|
||||
expect(isJavaScript(source, source.content)).toBe(false);
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"foo.html",
|
||||
undefined,
|
||||
""
|
||||
);
|
||||
expect(isJavaScript(source, content)).toBe(false);
|
||||
}
|
||||
{
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/html"
|
||||
);
|
||||
expect(isJavaScript(source, source.content)).toBe(false);
|
||||
expect(isJavaScript(source, content)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("is JavaScript", () => {
|
||||
{
|
||||
const source = makeMockSourceAndContent("foo.js");
|
||||
expect(isJavaScript(source, source.content)).toBe(true);
|
||||
const { source, content } = makeMockSourceAndContent("foo.js");
|
||||
expect(isJavaScript(source, content)).toBe(true);
|
||||
}
|
||||
{
|
||||
const source = makeMockSourceAndContent("bar.jsm");
|
||||
expect(isJavaScript(source, source.content)).toBe(true);
|
||||
const { source, content } = makeMockSourceAndContent("bar.jsm");
|
||||
expect(isJavaScript(source, content)).toBe(true);
|
||||
}
|
||||
{
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/javascript"
|
||||
);
|
||||
expect(isJavaScript(source, source.content)).toBe(true);
|
||||
expect(isJavaScript(source, content)).toBe(true);
|
||||
}
|
||||
{
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"application/javascript"
|
||||
);
|
||||
expect(isJavaScript(source, source.content)).toBe(true);
|
||||
expect(isJavaScript(source, content)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -296,124 +300,121 @@ describe("sources", () => {
|
|||
|
||||
describe("getMode", () => {
|
||||
it("//@flow", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/javascript",
|
||||
"// @flow"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({
|
||||
expect(getMode(source, content)).toEqual({
|
||||
name: "javascript",
|
||||
typescript: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("/* @flow */", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/javascript",
|
||||
" /* @flow */"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({
|
||||
expect(getMode(source, content)).toEqual({
|
||||
name: "javascript",
|
||||
typescript: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("mixed html", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"",
|
||||
" <html"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "htmlmixed" });
|
||||
expect(getMode(source, content)).toEqual({ name: "htmlmixed" });
|
||||
});
|
||||
|
||||
it("elm", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/x-elm",
|
||||
'main = text "Hello, World!"'
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "elm" });
|
||||
expect(getMode(source, content)).toEqual({ name: "elm" });
|
||||
});
|
||||
|
||||
it("returns jsx if contentType jsx is given", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/jsx",
|
||||
"<h1></h1>"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "jsx" });
|
||||
expect(getMode(source, content)).toEqual({ name: "jsx" });
|
||||
});
|
||||
|
||||
it("returns jsx if sourceMetaData says it's a react component", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"",
|
||||
"<h1></h1>"
|
||||
);
|
||||
expect(
|
||||
getMode(source, source.content, {
|
||||
...defaultSymbolDeclarations,
|
||||
hasJsx: true,
|
||||
})
|
||||
getMode(source, content, { ...defaultSymbolDeclarations, hasJsx: true })
|
||||
).toEqual({ name: "jsx" });
|
||||
});
|
||||
|
||||
it("returns jsx if the fileExtension is .jsx", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"myComponent.jsx",
|
||||
undefined,
|
||||
"",
|
||||
"<h1></h1>"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "jsx" });
|
||||
expect(getMode(source, content)).toEqual({ name: "jsx" });
|
||||
});
|
||||
|
||||
it("returns text/x-haxe if the file extension is .hx", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"myComponent.hx",
|
||||
undefined,
|
||||
"",
|
||||
"function foo(){}"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "text/x-haxe" });
|
||||
expect(getMode(source, content)).toEqual({ name: "text/x-haxe" });
|
||||
});
|
||||
|
||||
it("typescript", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/typescript",
|
||||
"function foo(){}"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({
|
||||
expect(getMode(source, content)).toEqual({
|
||||
name: "javascript",
|
||||
typescript: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("typescript-jsx", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/typescript-jsx",
|
||||
"<h1></h1>"
|
||||
);
|
||||
expect(getMode(source, source.content).base).toEqual({
|
||||
expect(getMode(source, content).base).toEqual({
|
||||
name: "javascript",
|
||||
typescript: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("cross-platform clojure(script) with reader conditionals", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"my-clojurescript-source-with-reader-conditionals.cljc",
|
||||
undefined,
|
||||
"text/x-clojure",
|
||||
|
@ -421,54 +422,54 @@ describe("sources", () => {
|
|||
" #?(:clj (java.lang.Integer/parseInt s) " +
|
||||
" :cljs (js/parseInt s)))"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "clojure" });
|
||||
expect(getMode(source, content)).toEqual({ name: "clojure" });
|
||||
});
|
||||
|
||||
it("clojurescript", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"my-clojurescript-source.cljs",
|
||||
undefined,
|
||||
"text/x-clojurescript",
|
||||
"(+ 1 2 3)"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "clojure" });
|
||||
expect(getMode(source, content)).toEqual({ name: "clojure" });
|
||||
});
|
||||
|
||||
it("coffeescript", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
undefined,
|
||||
"text/coffeescript",
|
||||
"x = (a) -> 3"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "coffeescript" });
|
||||
expect(getMode(source, content)).toEqual({ name: "coffeescript" });
|
||||
});
|
||||
|
||||
it("wasm", () => {
|
||||
const source = makeMockWasmSourceWithContent({
|
||||
const { source, content } = makeMockWasmSourceWithContent({
|
||||
binary: "\x00asm\x01\x00\x00\x00",
|
||||
});
|
||||
expect(getMode(source, source.content.value)).toEqual({ name: "text" });
|
||||
expect(getMode(source, content.value)).toEqual({ name: "text" });
|
||||
});
|
||||
|
||||
it("marko", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"http://localhost.com:7999/increment/sometestfile.marko",
|
||||
undefined,
|
||||
"does not matter",
|
||||
"function foo(){}"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "javascript" });
|
||||
expect(getMode(source, content)).toEqual({ name: "javascript" });
|
||||
});
|
||||
|
||||
it("es6", () => {
|
||||
const source = makeMockSourceAndContent(
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
"http://localhost.com:7999/increment/sometestfile.es6",
|
||||
undefined,
|
||||
"does not matter",
|
||||
"function foo(){}"
|
||||
);
|
||||
expect(getMode(source, source.content)).toEqual({ name: "javascript" });
|
||||
expect(getMode(source, content)).toEqual({ name: "javascript" });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ describe("wasm", () => {
|
|||
expect(isWasm(sourceId)).toEqual(false);
|
||||
});
|
||||
it("should give us the true when wasm text was registered", () => {
|
||||
const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
renderWasmText(source.id, source.content.value);
|
||||
const { source, content } = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
renderWasmText(source.id, content.value);
|
||||
expect(isWasm(source.id)).toEqual(true);
|
||||
// clear shall remove
|
||||
clearWasmStates();
|
||||
|
@ -48,8 +48,8 @@ describe("wasm", () => {
|
|||
|
||||
describe("renderWasmText", () => {
|
||||
it("render simple wasm", () => {
|
||||
const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
const lines = renderWasmText(source.id, source.content.value);
|
||||
const { source, content } = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
const lines = renderWasmText(source.id, content.value);
|
||||
expect(lines.join("\n")).toEqual(SIMPLE_WASM_TEXT);
|
||||
clearWasmStates();
|
||||
});
|
||||
|
@ -60,8 +60,8 @@ describe("wasm", () => {
|
|||
expect(SIMPLE_WASM.binary[SIMPLE_WASM_NOP_OFFSET]).toEqual("\x01");
|
||||
|
||||
it("get simple wasm nop offset", () => {
|
||||
const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
renderWasmText(source.id, source.content.value);
|
||||
const { source, content } = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
renderWasmText(source.id, content.value);
|
||||
const offset = lineToWasmOffset(source.id, SIMPLE_WASM_NOP_TEXT_LINE);
|
||||
expect(offset).toEqual(SIMPLE_WASM_NOP_OFFSET);
|
||||
clearWasmStates();
|
||||
|
@ -70,8 +70,8 @@ describe("wasm", () => {
|
|||
|
||||
describe("wasmOffsetToLine", () => {
|
||||
it("get simple wasm nop line", () => {
|
||||
const source = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
renderWasmText(source.id, source.content.value);
|
||||
const { source, content } = makeMockWasmSourceWithContent(SIMPLE_WASM);
|
||||
renderWasmText(source.id, content.value);
|
||||
const line = wasmOffsetToLine(source.id, SIMPLE_WASM_NOP_OFFSET);
|
||||
expect(line).toEqual(SIMPLE_WASM_NOP_TEXT_LINE);
|
||||
clearWasmStates();
|
||||
|
|
|
@ -20,7 +20,7 @@ function formatLines(actual) {
|
|||
|
||||
describe("Parser.findOutOfScopeLocations", () => {
|
||||
it("should exclude non-enclosing function blocks", () => {
|
||||
const source = populateSource("outOfScope");
|
||||
const { source } = populateSource("outOfScope");
|
||||
const actual = findOutOfScopeLocations(source.id, {
|
||||
line: 5,
|
||||
column: 5,
|
||||
|
@ -30,7 +30,7 @@ describe("Parser.findOutOfScopeLocations", () => {
|
|||
});
|
||||
|
||||
it("should roll up function blocks", () => {
|
||||
const source = populateSource("outOfScope");
|
||||
const { source } = populateSource("outOfScope");
|
||||
const actual = findOutOfScopeLocations(source.id, {
|
||||
line: 24,
|
||||
column: 0,
|
||||
|
@ -40,7 +40,7 @@ describe("Parser.findOutOfScopeLocations", () => {
|
|||
});
|
||||
|
||||
it("should exclude function for locations on declaration", () => {
|
||||
const source = populateSource("outOfScope");
|
||||
const { source } = populateSource("outOfScope");
|
||||
const actual = findOutOfScopeLocations(source.id, {
|
||||
line: 3,
|
||||
column: 12,
|
||||
|
@ -50,7 +50,7 @@ describe("Parser.findOutOfScopeLocations", () => {
|
|||
});
|
||||
|
||||
it("should treat comments as out of scope", () => {
|
||||
const source = populateSource("outOfScopeComment");
|
||||
const { source } = populateSource("outOfScopeComment");
|
||||
const actual = findOutOfScopeLocations(source.id, {
|
||||
line: 3,
|
||||
column: 2,
|
||||
|
@ -62,7 +62,7 @@ describe("Parser.findOutOfScopeLocations", () => {
|
|||
});
|
||||
|
||||
it("should not exclude in-scope inner locations", () => {
|
||||
const source = populateSource("outOfScope");
|
||||
const { source } = populateSource("outOfScope");
|
||||
const actual = findOutOfScopeLocations(source.id, {
|
||||
line: 61,
|
||||
column: 0,
|
||||
|
|
|
@ -11,7 +11,7 @@ import cases from "jest-in-case";
|
|||
cases(
|
||||
"Parser.getFramework",
|
||||
({ name, file, value }) => {
|
||||
const source = populateOriginalSource("frameworks/plainJavascript");
|
||||
const { source } = populateOriginalSource("frameworks/plainJavascript");
|
||||
const symbols = getSymbols(source.id);
|
||||
expect(symbols.framework).toBeUndefined();
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ import cases from "jest-in-case";
|
|||
cases(
|
||||
"Parser.getScopes",
|
||||
({ name, file, type, locations }) => {
|
||||
const source = populateOriginalSource(file, type);
|
||||
const { source } = populateOriginalSource(file, type);
|
||||
|
||||
locations.forEach(([line, column]) => {
|
||||
const scopes = getScopes({
|
||||
|
|
|
@ -12,7 +12,7 @@ import cases from "jest-in-case";
|
|||
cases(
|
||||
"Parser.getSymbols",
|
||||
({ name, file, original, type }) => {
|
||||
const source = original
|
||||
const { source } = original
|
||||
? populateOriginalSource(file, type)
|
||||
: populateSource(file, type);
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import path from "path";
|
|||
import type {
|
||||
Source,
|
||||
TextSourceContent,
|
||||
SourceBase,
|
||||
SourceWithContent,
|
||||
} from "../../../../types";
|
||||
import { makeMockSourceAndContent } from "../../../../utils/test-mockup";
|
||||
|
@ -48,20 +47,20 @@ function getSourceContent(
|
|||
}
|
||||
|
||||
export function getSource(name: string, type?: string): Source {
|
||||
return getSourceWithContent(name, type);
|
||||
return getSourceWithContent(name, type).source;
|
||||
}
|
||||
|
||||
export function getSourceWithContent(
|
||||
name: string,
|
||||
type?: string
|
||||
): { ...SourceBase, content: TextSourceContent } {
|
||||
): { source: Source, content: TextSourceContent } {
|
||||
const { value: text, contentType } = getSourceContent(name, type);
|
||||
|
||||
return makeMockSourceAndContent(undefined, name, contentType, text);
|
||||
}
|
||||
|
||||
export function populateSource(name: string, type?: string): SourceWithContent {
|
||||
const { content, ...source } = getSourceWithContent(name, type);
|
||||
const { source, content } = getSourceWithContent(name, type);
|
||||
setSource({
|
||||
id: source.id,
|
||||
text: content.value,
|
||||
|
@ -69,19 +68,19 @@ export function populateSource(name: string, type?: string): SourceWithContent {
|
|||
isWasm: false,
|
||||
});
|
||||
return {
|
||||
...source,
|
||||
source,
|
||||
content: asyncValue.fulfilled(content),
|
||||
};
|
||||
}
|
||||
|
||||
export function getOriginalSource(name: string, type?: string): Source {
|
||||
return getOriginalSourceWithContent(name, type);
|
||||
return getOriginalSourceWithContent(name, type).source;
|
||||
}
|
||||
|
||||
export function getOriginalSourceWithContent(
|
||||
name: string,
|
||||
type?: string
|
||||
): { ...SourceBase, content: TextSourceContent } {
|
||||
): { source: Source, content: TextSourceContent } {
|
||||
const { value: text, contentType } = getSourceContent(name, type);
|
||||
|
||||
return makeMockSourceAndContent(
|
||||
|
@ -96,7 +95,7 @@ export function populateOriginalSource(
|
|||
name: string,
|
||||
type?: string
|
||||
): SourceWithContent {
|
||||
const { content, ...source } = getOriginalSourceWithContent(name, type);
|
||||
const { source, content } = getOriginalSourceWithContent(name, type);
|
||||
setSource({
|
||||
id: source.id,
|
||||
text: content.value,
|
||||
|
@ -104,7 +103,7 @@ export function populateOriginalSource(
|
|||
isWasm: false,
|
||||
});
|
||||
return {
|
||||
...source,
|
||||
source,
|
||||
content: asyncValue.fulfilled(content),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { populateSource } from "./helpers";
|
|||
describe("getNextStep", () => {
|
||||
describe("await", () => {
|
||||
it("first await call", () => {
|
||||
const source = populateSource("async");
|
||||
const { source } = populateSource("async");
|
||||
const pausePosition = { line: 8, column: 2, sourceId: source.id };
|
||||
expect(getNextStep(source.id, pausePosition)).toEqual({
|
||||
...pausePosition,
|
||||
|
@ -19,7 +19,7 @@ describe("getNextStep", () => {
|
|||
});
|
||||
|
||||
it("first await call expression", () => {
|
||||
const source = populateSource("async");
|
||||
const { source } = populateSource("async");
|
||||
const pausePosition = { line: 8, column: 9, sourceId: source.id };
|
||||
expect(getNextStep(source.id, pausePosition)).toEqual({
|
||||
...pausePosition,
|
||||
|
@ -29,13 +29,13 @@ describe("getNextStep", () => {
|
|||
});
|
||||
|
||||
it("second await call", () => {
|
||||
const source = populateSource("async");
|
||||
const { source } = populateSource("async");
|
||||
const pausePosition = { line: 9, column: 2, sourceId: source.id };
|
||||
expect(getNextStep(source.id, pausePosition)).toEqual(null);
|
||||
});
|
||||
|
||||
it("second call expression", () => {
|
||||
const source = populateSource("async");
|
||||
const { source } = populateSource("async");
|
||||
const pausePosition = { line: 9, column: 9, sourceId: source.id };
|
||||
expect(getNextStep(source.id, pausePosition)).toEqual(null);
|
||||
});
|
||||
|
@ -43,7 +43,7 @@ describe("getNextStep", () => {
|
|||
|
||||
describe("yield", () => {
|
||||
it("first yield call", () => {
|
||||
const source = populateSource("generators");
|
||||
const { source } = populateSource("generators");
|
||||
const pausePosition = { line: 2, column: 2, sourceId: source.id };
|
||||
expect(getNextStep(source.id, pausePosition)).toEqual({
|
||||
...pausePosition,
|
||||
|
@ -52,7 +52,7 @@ describe("getNextStep", () => {
|
|||
});
|
||||
|
||||
it("second yield call", () => {
|
||||
const source = populateSource("generators");
|
||||
const { source } = populateSource("generators");
|
||||
const pausePosition = { line: 3, column: 2, sourceId: source.id };
|
||||
expect(getNextStep(source.id, pausePosition)).toEqual(null);
|
||||
});
|
||||
|
|
|
@ -23,11 +23,16 @@ const astKeys = [
|
|||
cases(
|
||||
"ast.getAst",
|
||||
({ name }) => {
|
||||
const source = makeMockSourceAndContent(undefined, "foo", name, "2");
|
||||
const { source, content } = makeMockSourceAndContent(
|
||||
undefined,
|
||||
"foo",
|
||||
name,
|
||||
"2"
|
||||
);
|
||||
setSource({
|
||||
id: source.id,
|
||||
text: source.content.value || "",
|
||||
contentType: source.content.contentType,
|
||||
text: content.value || "",
|
||||
contentType: content.contentType,
|
||||
isWasm: false,
|
||||
});
|
||||
const ast = getAst("foo");
|
||||
|
|
|
@ -241,8 +241,8 @@ function waitForSelectedSource(dbg, url) {
|
|||
return waitForState(
|
||||
dbg,
|
||||
state => {
|
||||
const source = getSelectedSourceWithContent() || {};
|
||||
if (!source.content) {
|
||||
const { source, content } = getSelectedSourceWithContent() || {};
|
||||
if (!content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -318,8 +318,9 @@ function assertPausedLocation(dbg) {
|
|||
function assertDebugLine(dbg, line) {
|
||||
// Check the debug line
|
||||
const lineInfo = getCM(dbg).lineInfo(line - 1);
|
||||
const source = dbg.selectors.getSelectedSourceWithContent() || {};
|
||||
if (source && !source.content) {
|
||||
const { source, content } =
|
||||
dbg.selectors.getSelectedSourceWithContent() || {};
|
||||
if (source && !content) {
|
||||
const url = source.url;
|
||||
ok(
|
||||
false,
|
||||
|
@ -514,9 +515,14 @@ function isSelectedFrameSelected(dbg, state) {
|
|||
// Make sure the source text is completely loaded for the
|
||||
// source we are paused in.
|
||||
const sourceId = frame.location.sourceId;
|
||||
const source = dbg.selectors.getSelectedSourceWithContent() || {};
|
||||
const { source, content } =
|
||||
dbg.selectors.getSelectedSourceWithContent() || {};
|
||||
|
||||
if (!source || !source.content) {
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче