Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

[preview] jump to definition should respect the current context #6811

Merged
merged 1 commit into from
Aug 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/panel/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ DebuggerPanel.prototype = {
},

selectSource(url, line) {
this._actions.selectSourceURL(url, { location: { line } });
this._actions.selectSourceURL(url, { line });
},

getSource(sourceURL) {
Expand Down
13 changes: 2 additions & 11 deletions src/actions/pause/paused.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import { isGeneratedId } from "devtools-source-map";

import {
getHiddenBreakpointLocation,
isEvaluatingExpression,
getSelectedFrame,
getVisibleSelectedFrame,
getSources
} from "../../selectors";

Expand Down Expand Up @@ -73,15 +69,10 @@ export function paused(pauseInfo: Pause) {
}

await dispatch(mapFrames());
const selectedFrame = getSelectedFrame(getState());

const selectedFrame = getSelectedFrame(getState());
if (selectedFrame) {
const visibleFrame = getVisibleSelectedFrame(getState());
const location =
visibleFrame && isGeneratedId(visibleFrame.location.sourceId)
? selectedFrame.generatedLocation
: selectedFrame.location;
await dispatch(selectLocation(location));
await dispatch(selectLocation(selectedFrame.location));
}

dispatch(togglePaneCollapse("end", false));
Expand Down
3 changes: 2 additions & 1 deletion src/actions/pause/tests/pause.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ describe("pause", () => {
getOriginalSourceText: async () => ({
source: "\n\nfunction fooOriginal() {\n return -5;\n}",
contentType: "text/javascript"
})
}),
getGeneratedLocation: async location => location
};

const store = createStore(mockThreadClient, {}, sourceMapsMock);
Expand Down
1 change: 0 additions & 1 deletion src/actions/sources/loadSourceText.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export function loadSourceText(source: Source) {
}

const newSource = getSourceFromId(getState(), source.id);

if (isOriginalId(newSource.id) && !newSource.isWasm) {
const generatedSource = getGeneratedSource(getState(), source);
await dispatch(loadSourceText(generatedSource));
Expand Down
9 changes: 6 additions & 3 deletions src/actions/sources/prettyPrint.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { prettyPrint } from "../../workers/pretty-print";
import { setSource } from "../../workers/parser";
import { getPrettySourceURL, isLoaded } from "../../utils/source";
import { loadSourceText } from "./loadSourceText";
import { selectLocation } from "../sources";
import { mapFrames } from "../pause";
import { selectSpecificLocation } from "../sources";

import {
getSource,
Expand Down Expand Up @@ -106,7 +106,7 @@ export function togglePrettyPrint(sourceId: string) {
if (prettySource) {
const _sourceId = prettySource.id;
return dispatch(
selectLocation({ ...options.location, sourceId: _sourceId })
selectSpecificLocation({ ...options.location, sourceId: _sourceId })
);
}

Expand All @@ -118,7 +118,10 @@ export function togglePrettyPrint(sourceId: string) {
await dispatch(setSymbols(newPrettySource.id));

dispatch(
selectLocation({ ...options.location, sourceId: newPrettySource.id })
selectSpecificLocation({
...options.location,
sourceId: newPrettySource.id
})
);

return newPrettySource;
Expand Down
75 changes: 38 additions & 37 deletions src/actions/sources/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import { isOriginalId } from "devtools-source-map";

import { getSourceFromId } from "../../reducers/sources";
import { setOutOfScopeLocations, setSymbols } from "../ast";
import { closeActiveSearch, updateActiveFileSearch } from "../ui";

Expand All @@ -21,7 +22,7 @@ import { loadSourceText } from "./loadSourceText";
import { prefs } from "../../utils/prefs";
import { shouldPrettyPrint, isMinified } from "../../utils/source";
import { createLocation } from "../../utils/location";
import { getGeneratedLocation } from "../../utils/source-maps";
import { getMappedLocation } from "../../utils/source-maps";

import {
getSource,
Expand All @@ -32,14 +33,9 @@ import {
getSelectedSource
} from "../../selectors";

import type { Location, Source } from "../../types";
import type { Location, Position, Source } from "../../types";
import type { ThunkArgs } from "../types";

declare type SelectSourceOptions = {
tabIndex?: number,
location?: { line: number, column?: ?number }
};

export const setSelectedLocation = (source: Source, location: Location) => ({
type: "SET_SELECTED_LOCATION",
source,
Expand All @@ -59,25 +55,25 @@ export const clearSelectedLocation = () => ({
/**
* Deterministically select a source that has a given URL. This will
* work regardless of the connection status or if the source exists
* yet. This exists mostly for external things to interact with the
* yet.
*
* This exists mostly for external things to interact with the
* debugger.
*
* @memberof actions/sources
* @static
*/
export function selectSourceURL(
url: string,
options: SelectSourceOptions = {}
) {
return async ({ dispatch, getState }: ThunkArgs) => {

export function selectSourceURL(url: string, options: Position = { line: 1 }) {
return async ({ dispatch, getState, sourceMaps }: ThunkArgs) => {
const source = getSourceByURL(getState(), url);
if (source) {
const sourceId = source.id;
const location = createLocation({ ...options.location, sourceId });
await dispatch(selectLocation(location));
} else {
dispatch(setPendingSelectedLocation(url, options));
if (!source) {
return dispatch(setPendingSelectedLocation(url, options));
}

const sourceId = source.id;
const location = createLocation({ ...options, sourceId });
return dispatch(selectLocation(location));
};
}

Expand All @@ -98,9 +94,9 @@ export function selectSource(sourceId: string) {
*/
export function selectLocation(
location: Location,
{ checkPrettyPrint = true }: Object = {}
{ keepContext = true }: Object = {}
) {
return async ({ dispatch, getState, client }: ThunkArgs) => {
return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
const currentSource = getSelectedSource(getState());

if (!client) {
Expand All @@ -109,7 +105,7 @@ export function selectLocation(
return;
}

const source = getSource(getState(), location.sourceId);
let source = getSource(getState(), location.sourceId);
if (!source) {
// If there is no source we deselect the current selected source
return dispatch(clearSelectedLocation());
Expand All @@ -120,6 +116,18 @@ export function selectLocation(
dispatch(closeActiveSearch());
}

// Preserve the current source map context (original / generated)
// when navigting to a new location.
const selectedSource = getSelectedSource(getState());
if (
keepContext &&
selectedSource &&
isOriginalId(selectedSource.id) != isOriginalId(location.sourceId)
) {
location = await getMappedLocation(getState(), sourceMaps, location);
source = getSourceFromId(getState(), location.sourceId);
}

dispatch(addTab(source.url, 0));
dispatch(setSelectedLocation(source, location));

Expand All @@ -132,7 +140,7 @@ export function selectLocation(
}

if (
checkPrettyPrint &&
keepContext &&
prefs.autoPrettyPrint &&
!getPrettySource(getState(), loadedSource.id) &&
shouldPrettyPrint(loadedSource) &&
Expand All @@ -158,7 +166,7 @@ export function selectLocation(
* @static
*/
export function selectSpecificLocation(location: Location) {
return selectLocation(location, { checkPrettyPrint: false });
return selectLocation(location, { keepContext: false });
}

/**
Expand All @@ -182,20 +190,13 @@ export function jumpToMappedLocation(location: Location) {
return;
}

const source = getSource(getState(), location.sourceId);
let pairedLocation;
if (isOriginalId(location.sourceId)) {
pairedLocation = await getGeneratedLocation(
getState(),
source,
location,
sourceMaps
);
} else {
pairedLocation = await sourceMaps.getOriginalLocation(location, source);
}
const pairedLocation = await getMappedLocation(
getState(),
sourceMaps,
location
);

return dispatch(selectLocation({ ...pairedLocation }));
return dispatch(selectSpecificLocation({ ...pairedLocation }));
};
}

Expand Down
83 changes: 72 additions & 11 deletions src/actions/sources/tests/select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

import { getSymbols } from "../../../reducers/ast";
import {
actions,
selectors,
createStore,
makeFrame,
makeSource,
waitForState
waitForState,
makeOriginalSource
} from "../../../utils/test-head";
const {
getSource,
Expand Down Expand Up @@ -58,16 +60,6 @@ describe("sources", () => {
expect(locations).toHaveLength(1);
});

it("should automatically select a pending source", async () => {
const { dispatch, getState } = createStore(sourceThreadClient);
const baseSource = makeSource("base.js");
await dispatch(actions.selectSourceURL(baseSource.url));

expect(getSelectedSource(getState())).toBe(undefined);
await dispatch(actions.newSource(baseSource));
expect(getSelectedSource(getState()).url).toBe(baseSource.url);
});

it("should open a tab for the source", async () => {
const { dispatch, getState } = createStore(sourceThreadClient);
await dispatch(actions.newSource(makeSource("foo.js")));
Expand Down Expand Up @@ -150,4 +142,73 @@ describe("sources", () => {
const clearResult = getState().sources.pendingSelectedLocation;
expect(clearResult).toEqual({ url: "" });
});

it("should keep the generated the viewing context", async () => {
const store = createStore(sourceThreadClient);
const { dispatch, getState } = store;
const baseSource = makeSource("base.js");
await dispatch(actions.newSource(baseSource));

await dispatch(
actions.selectLocation({ sourceId: baseSource.id, line: 1 })
);

expect(getSelectedSource(getState()).id).toBe(baseSource.id);
await waitForState(store, state => getSymbols(state, baseSource));
});

it("should keep the original the viewing context", async () => {
const { dispatch, getState } = createStore(
sourceThreadClient,
{},
{
getOriginalLocation: async location => ({ ...location, line: 12 }),
getGeneratedLocation: async location => ({ ...location, line: 12 }),
getOriginalSourceText: async () => ({ source: "" })
}
);

const baseSource = makeSource("base.js");
await dispatch(actions.newSource(baseSource));

const originalBaseSource = makeOriginalSource("base.js");
await dispatch(actions.newSource(originalBaseSource));

await dispatch(actions.selectSource(originalBaseSource.id));

const fooSource = makeSource("foo.js");
await dispatch(actions.newSource(fooSource));
await dispatch(actions.selectLocation({ sourceId: fooSource.id, line: 1 }));

expect(getSelectedLocation(getState()).line).toBe(12);
});

it("should change the original the viewing context", async () => {
const { dispatch, getState } = createStore(
sourceThreadClient,
{},
{ getOriginalLocation: async location => ({ ...location, line: 12 }) }
);

const baseSource = makeOriginalSource("base.js");
await dispatch(actions.newSource(baseSource));
await dispatch(actions.selectSource(baseSource.id));

await dispatch(
actions.selectSpecificLocation({ sourceId: baseSource.id, line: 1 })
);
expect(getSelectedLocation(getState()).line).toBe(1);
});

describe("selectSourceURL", () => {
it("should automatically select a pending source", async () => {
const { dispatch, getState } = createStore(sourceThreadClient);
const baseSource = makeSource("base.js");
await dispatch(actions.selectSourceURL(baseSource.url));

expect(getSelectedSource(getState())).toBe(undefined);
await dispatch(actions.newSource(baseSource));
expect(getSelectedSource(getState()).url).toBe(baseSource.url);
});
});
});
2 changes: 0 additions & 2 deletions src/components/PrimaryPanes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ type Props = {
horizontal: boolean,
sourceSearchOn: boolean,
setPrimaryPaneTab: string => void,
selectLocation: Object => void,
setActiveSearch: string => void,
closeActiveSearch: () => void
};
Expand Down Expand Up @@ -126,7 +125,6 @@ export default connect(
mapStateToProps,
{
setPrimaryPaneTab: actions.setPrimaryPaneTab,
selectLocation: actions.selectLocation,
setActiveSearch: actions.setActiveSearch,
closeActiveSearch: actions.closeActiveSearch
}
Expand Down
1 change: 0 additions & 1 deletion src/components/PrimaryPanes/tests/SourcesTree.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,6 @@ function generateDefaults(overrides) {
};
return {
autoExpandAll: true,
selectLocation: jest.fn(),
selectSource: jest.fn(),
setExpandedState: jest.fn(),
sources: defaultSources,
Expand Down
Loading