Bug 1558303 - Stop using immutable.

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jason Laster 2019-06-11 16:15:17 +00:00
Родитель bbc8c51513
Коммит 8578b45a14
20 изменённых файлов: 193 добавлений и 220 удалений

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

@ -61,7 +61,6 @@
"devtools-splitter": "^0.0.8",
"devtools-utils": "0.0.14",
"fuzzaldrin-plus": "^0.6.0",
"immutable": "^3.8.2",
"lodash": "^4.17.4",
"lodash-move": "^1.1.1",
"lodash.kebabcase": "^4.1.1",
@ -72,7 +71,6 @@
"react": "16.4.1",
"react-aria-components": "^0.0.4",
"react-dom": "16.4.1",
"react-immutable-proptypes": "^2.1.0",
"react-redux": "^5.0.7",
"react-transition-group": "^2.2.1",
"reselect": "^4.0.0",

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

@ -121,7 +121,7 @@ export function deleteExpression(expression: Expression) {
*/
export function evaluateExpressions(cx: ThreadContext) {
return async function({ dispatch, getState, client }: ThunkArgs) {
const expressions = getExpressions(getState()).toJS();
const expressions = getExpressions(getState());
const inputs = expressions.map(({ input }) => input);
const frameId = getSelectedFrameId(getState(), cx.thread);
const results = await client.evaluateExpressions(inputs, {

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

@ -126,7 +126,6 @@ export function searchContents(
return;
}
const _modifiers = modifiers.toJS();
let text;
if (selectedContent.type === "wasm") {
text = renderWasmText(selectedSource.id, selectedContent).join("\n");
@ -134,9 +133,9 @@ export function searchContents(
text = selectedContent.value;
}
const matches = await getMatches(query, text, _modifiers);
const matches = await getMatches(query, text, modifiers);
const res = find(ctx, query, true, _modifiers, focusFirstResult);
const res = find(ctx, query, true, modifiers, focusFirstResult);
if (!res) {
return;
}
@ -168,9 +167,7 @@ export function searchContentsForHighlight(
}
const ctx = { ed: editor, cm: editor.codeMirror };
const _modifiers = modifiers.toJS();
searchSourceForHighlight(ctx, false, query, true, _modifiers, line, ch);
searchSourceForHighlight(ctx, false, query, true, modifiers, line, ch);
};
}
@ -192,7 +189,7 @@ export function traverseResults(cx: Context, rev: boolean, editor: Editor) {
if (modifiers) {
const matchedLocations = matches || [];
const findArgs = [ctx, query, true, modifiers.toJS()];
const findArgs = [ctx, query, true, modifiers];
const results = rev ? findPrev(...findArgs) : findNext(...findArgs);
if (!results) {

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

@ -403,7 +403,7 @@ describe("pause", () => {
await dispatch(actions.resumed(resumedPacket()));
const expression = selectors.getExpression(getState(), "foo");
expect(expression.value).toEqual("YAY");
expect(expression && expression.value).toEqual("YAY");
});
});
});

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

@ -55,7 +55,7 @@ describe("expressions", () => {
const { dispatch, getState, cx } = createStore(mockThreadClient);
await dispatch(actions.addExpression(cx, "foo"));
expect(selectors.getExpressions(getState()).size).toBe(1);
expect(selectors.getExpressions(getState())).toHaveLength(1);
});
it("should not add empty expressions", () => {
@ -63,14 +63,14 @@ describe("expressions", () => {
dispatch(actions.addExpression(cx, (undefined: any)));
dispatch(actions.addExpression(cx, ""));
expect(selectors.getExpressions(getState()).size).toBe(0);
expect(selectors.getExpressions(getState())).toHaveLength(0);
});
it("should not add invalid expressions", async () => {
const { dispatch, getState, cx } = createStore(mockThreadClient);
await dispatch(actions.addExpression(cx, "foo#"));
const state = getState();
expect(selectors.getExpressions(state).size).toBe(0);
expect(selectors.getExpressions(state)).toHaveLength(0);
expect(selectors.getExpressionError(state)).toBe(true);
});
@ -79,9 +79,14 @@ describe("expressions", () => {
await dispatch(actions.addExpression(cx, "foo"));
const expression = selectors.getExpression(getState(), "foo");
await dispatch(actions.updateExpression(cx, "bar", expression));
if (!expression) {
throw new Error("expression must exist");
}
expect(selectors.getExpression(getState(), "bar").input).toBe("bar");
await dispatch(actions.updateExpression(cx, "bar", expression));
const bar = selectors.getExpression(getState(), "bar");
expect(bar && bar.input).toBe("bar");
});
it("should not update an expression w/ invalid code", async () => {
@ -89,6 +94,9 @@ describe("expressions", () => {
await dispatch(actions.addExpression(cx, "foo"));
const expression = selectors.getExpression(getState(), "foo");
if (!expression) {
throw new Error("expression must exist");
}
await dispatch(actions.updateExpression(cx, "#bar", expression));
expect(selectors.getExpression(getState(), "bar")).toBeUndefined();
});
@ -98,24 +106,35 @@ describe("expressions", () => {
await dispatch(actions.addExpression(cx, "foo"));
await dispatch(actions.addExpression(cx, "bar"));
expect(selectors.getExpressions(getState()).size).toBe(2);
expect(selectors.getExpressions(getState())).toHaveLength(2);
const expression = selectors.getExpression(getState(), "foo");
if (!expression) {
throw new Error("expression must exist");
}
const bar = selectors.getExpression(getState(), "bar");
dispatch(actions.deleteExpression(expression));
expect(selectors.getExpressions(getState()).size).toBe(1);
expect(selectors.getExpression(getState(), "bar").input).toBe("bar");
expect(selectors.getExpressions(getState())).toHaveLength(1);
expect(bar && bar.input).toBe("bar");
});
it("should evaluate expressions global scope", async () => {
const { dispatch, getState, cx } = createStore(mockThreadClient);
await dispatch(actions.addExpression(cx, "foo"));
await dispatch(actions.addExpression(cx, "bar"));
expect(selectors.getExpression(getState(), "foo").value).toBe("bla");
expect(selectors.getExpression(getState(), "bar").value).toBe("bla");
let foo = selectors.getExpression(getState(), "foo");
let bar = selectors.getExpression(getState(), "bar");
expect(foo && foo.value).toBe("bla");
expect(bar && bar.value).toBe("bla");
await dispatch(actions.evaluateExpressions(cx));
expect(selectors.getExpression(getState(), "foo").value).toBe("bla");
expect(selectors.getExpression(getState(), "bar").value).toBe("bla");
foo = selectors.getExpression(getState(), "foo");
bar = selectors.getExpression(getState(), "bar");
expect(foo && foo.value).toBe("bla");
expect(bar && bar.value).toBe("bla");
});
it("should evaluate expressions in specific scope", async () => {
@ -127,13 +146,16 @@ describe("expressions", () => {
await dispatch(actions.addExpression(cx, "foo"));
await dispatch(actions.addExpression(cx, "bar"));
expect(selectors.getExpression(getState(), "foo").value).toBe("boo");
expect(selectors.getExpression(getState(), "bar").value).toBe("boo");
let foo = selectors.getExpression(getState(), "foo");
let bar = selectors.getExpression(getState(), "bar");
expect(foo && foo.value).toBe("boo");
expect(bar && bar.value).toBe("boo");
await dispatch(actions.evaluateExpressions(cx));
expect(selectors.getExpression(getState(), "foo").value).toBe("boo");
expect(selectors.getExpression(getState(), "bar").value).toBe("boo");
foo = selectors.getExpression(getState(), "foo");
bar = selectors.getExpression(getState(), "bar");
expect(foo && foo.value).toBe("boo");
expect(bar && bar.value).toBe("boo");
});
it("should get the autocomplete matches for the input", async () => {

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

@ -45,10 +45,10 @@ describe("file text search", () => {
it("should toggle a file search modifier", () => {
const { dispatch, getState, cx } = createStore();
let fileSearchModState = getFileSearchModifiers(getState());
expect(fileSearchModState.get("caseSensitive")).toBe(false);
expect(fileSearchModState.caseSensitive).toBe(false);
dispatch(actions.toggleFileSearchModifier(cx, "caseSensitive"));
fileSearchModState = getFileSearchModifiers(getState());
expect(fileSearchModState.get("caseSensitive")).toBe(true);
expect(fileSearchModState.caseSensitive).toBe(true);
});
it("should toggle a file search query cleaning", () => {

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

@ -4,12 +4,7 @@
// @flow
import type {
Source,
PartialRange,
SourceLocation,
Context,
} from "../../types";
import type { Source, Range, SourceLocation, Context } from "../../types";
import type {
ActiveSearchType,
@ -81,5 +76,5 @@ export type UIAction =
|}
| {|
+type: "SET_VIEWPORT",
+viewport: PartialRange,
+viewport: Range,
|};

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

@ -9,6 +9,7 @@ import {
getPaneCollapse,
getQuickOpenEnabled,
getSource,
getSourceContent,
startsWithThreadActor,
getFileSearchQuery,
getProjectDirectoryRoot,
@ -17,8 +18,10 @@ import { selectSource } from "../actions/sources/select";
import type { ThunkArgs, panelPositionType } from "./types";
import { getEditor, getLocationsInViewport } from "../utils/editor";
import { searchContents } from "./file-search";
import { copyToTheClipboard } from "../utils/clipboard";
import { isFulfilled } from "../utils/async-value";
import type { SourceLocation, Context } from "../types";
import type { SourceLocation, Context, Source } from "../types";
import type {
ActiveSearchType,
OrientationType,
@ -219,3 +222,12 @@ export function updateViewport() {
export function setOrientation(orientation: OrientationType) {
return { type: "SET_ORIENTATION", orientation };
}
export function copyToClipboard(source: Source) {
return ({ dispatch, getState }: ThunkArgs) => {
const content = getSourceContent(getState(), source.id);
if (content && isFulfilled(content) && content.value.type === "text") {
copyToTheClipboard(content.value.value);
}
};
}

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

@ -251,7 +251,7 @@ class SearchBar extends Component<Props, State> {
function SearchModBtn({ modVal, className, svgName, tooltip }) {
const preppedClass = classnames(className, {
active: modifiers && modifiers.get(modVal),
active: modifiers && modifiers[modVal],
});
return (
<button

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

@ -11,8 +11,8 @@ import { showMenu, buildMenu } from "devtools-contextmenu";
import SourceIcon from "../shared/SourceIcon";
import { CloseButton } from "../shared/Button";
import { copyToTheClipboard } from "../../utils/clipboard";
import type { List } from "immutable";
import type { Source, Context } from "../../types";
import actions from "../../actions";
@ -26,7 +26,6 @@ import {
isPretty,
shouldBlackbox,
} from "../../utils/source";
import { copyToTheClipboard } from "../../utils/clipboard";
import { getTabMenuItems } from "../../utils/tabs";
import {
@ -40,11 +39,9 @@ import type { ActiveSearchType } from "../../selectors";
import classnames from "classnames";
type SourcesList = List<Source>;
type Props = {
cx: Context,
tabSources: SourcesList,
tabSources: Source[],
selectedSource: Source,
source: Source,
activeSearch: ActiveSearchType,
@ -52,6 +49,7 @@ type Props = {
selectSource: typeof actions.selectSource,
closeTab: typeof actions.closeTab,
closeTabs: typeof actions.closeTabs,
copyToClipboard: typeof actions.copyToClipboard,
togglePrettyPrint: typeof actions.togglePrettyPrint,
showSource: typeof actions.showSource,
toggleBlackBox: typeof actions.toggleBlackBox,
@ -68,6 +66,7 @@ class Tab extends PureComponent<Props> {
cx,
closeTab,
closeTabs,
copyToClipboard,
tabSources,
showSource,
toggleBlackBox,
@ -124,7 +123,7 @@ class Tab extends PureComponent<Props> {
item: {
...tabMenuItems.copyToClipboard,
disabled: selectedSource.id !== tab,
click: () => copyToTheClipboard(sourceTab.text),
click: () => copyToClipboard(sourceTab),
},
},
{
@ -250,6 +249,7 @@ export default connect(
mapStateToProps,
{
selectSource: actions.selectSource,
copyToClipboard: actions.copyToClipboard,
closeTab: actions.closeTab,
closeTabs: actions.closeTabs,
togglePrettyPrint: actions.togglePrettyPrint,

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

@ -79,7 +79,7 @@ class Expressions extends Component<Props, State> {
componentDidMount() {
const { cx, expressions, evaluateExpressions, showInput } = this.props;
if (expressions.size > 0) {
if (expressions.length > 0) {
evaluateExpressions(cx);
}
@ -377,7 +377,7 @@ class Expressions extends Component<Props, State> {
return (
<ul className="pane expressions-list">
{expressions.map(this.renderExpression)}
{(showInput || !expressions.size) && this.renderNewExpressionInput()}
{(showInput || !expressions.length) && this.renderNewExpressionInput()}
</ul>
);
}

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

@ -10,6 +10,7 @@ import Expressions from "../Expressions";
function generateDefaults(overrides) {
return {
evaluateExpressions: async () => {},
expressions: [
{
input: "expression1",

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

@ -88,33 +88,6 @@ exports[`Expressions should always have unique keys 1`] = `
</div>
</div>
</li>
<li
className="expression-input-container"
>
<form
className="expression-input-form"
onSubmit={[Function]}
>
<input
className="input-expression"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="Add watch expression"
type="text"
value=""
/>
<input
style={
Object {
"display": "none",
}
}
type="submit"
/>
</form>
</li>
</ul>
`;
@ -206,32 +179,5 @@ exports[`Expressions should render 1`] = `
</div>
</div>
</li>
<li
className="expression-input-container"
>
<form
className="expression-input-form"
onSubmit={[Function]}
>
<input
className="input-expression"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="Add watch expression"
type="text"
value=""
/>
<input
style={
Object {
"display": "none",
}
}
type="submit"
/>
</form>
</li>
</ul>
`;

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

@ -9,8 +9,6 @@
* @module reducers/expressions
*/
import makeRecord from "../utils/makeRecord";
import { List, Map } from "immutable";
import { omit, zip } from "lodash";
import { createSelector } from "reselect";
@ -19,30 +17,30 @@ import { prefs } from "../utils/prefs";
import type { Expression } from "../types";
import type { Selector, State } from "../reducers/types";
import type { Action } from "../actions/types";
import type { Record } from "../utils/makeRecord";
type AutocompleteMatches = { [string]: string[] };
export type ExpressionState = {
expressions: List<Expression>,
expressions: Expression[],
expressionError: boolean,
autocompleteMatches: Map<string, List<string>>,
autocompleteMatches: AutocompleteMatches,
currentAutocompleteInput: string | null,
};
export const createExpressionState: () => Record<ExpressionState> = makeRecord({
expressions: List(restoreExpressions()),
export const createExpressionState = () => ({
expressions: restoreExpressions(),
expressionError: false,
autocompleteMatches: Map({}),
autocompleteMatches: {},
currentAutocompleteInput: null,
});
function update(
state: Record<ExpressionState> = createExpressionState(),
state: ExpressionState = createExpressionState(),
action: Action
): Record<ExpressionState> {
): ExpressionState {
switch (action.type) {
case "ADD_EXPRESSION":
if (action.expressionError) {
return state.set("expressionError", !!action.expressionError);
return { ...state, expressionError: !!action.expressionError };
}
return appendExpressionToList(state, {
input: action.input,
@ -52,11 +50,13 @@ function update(
case "UPDATE_EXPRESSION":
const key = action.expression.input;
return updateExpressionInList(state, key, {
const newState = updateExpressionInList(state, key, {
input: action.input,
value: null,
updating: true,
}).set("expressionError", !!action.expressionError);
});
return { ...newState, expressionError: !!action.expressionError };
case "EVALUATE_EXPRESSION":
return updateExpressionInList(state, action.input, {
@ -69,8 +69,8 @@ function update(
const { inputs, results } = action;
return zip(inputs, results).reduce(
(newState, [input, result]) =>
updateExpressionInList(newState, input, {
(_state, [input, result]) =>
updateExpressionInList(_state, input, {
input: input,
value: result,
updating: false,
@ -82,19 +82,26 @@ function update(
return deleteExpression(state, action.input);
case "CLEAR_EXPRESSION_ERROR":
return state.set("expressionError", false);
return { ...state, expressionError: false };
case "AUTOCOMPLETE":
const { matchProp, matches } = action.result;
return state
.updateIn(["autocompleteMatches", matchProp], list => matches)
.set("currentAutocompleteInput", matchProp);
return {
...state,
currentAutocompleteInput: matchProp,
autocompleteMatches: {
...state.autocompleteMatches,
[matchProp]: matches,
},
};
case "CLEAR_AUTOCOMPLETE":
return state
.updateIn(["autocompleteMatches", ""], list => [])
.set("currentAutocompleteInput", "");
return {
...state,
autocompleteMatches: {},
currentAutocompleteInput: "",
};
}
return state;
@ -103,58 +110,54 @@ function update(
function restoreExpressions() {
const exprs = prefs.expressions;
if (exprs.length == 0) {
return;
return [];
}
return exprs;
}
function storeExpressions({ expressions }) {
prefs.expressions = expressions
.map(expression => omit(expression, "value"))
.toJS();
prefs.expressions = expressions.map(expression => omit(expression, "value"));
}
function appendExpressionToList(state: Record<ExpressionState>, value: any) {
const newState = state.update("expressions", () => {
return state.expressions.push(value);
});
function appendExpressionToList(state: ExpressionState, value: any) {
const newState = { ...state, expressions: [...state.expressions, value] };
storeExpressions(newState);
return newState;
}
function updateExpressionInList(
state: Record<ExpressionState>,
state: ExpressionState,
key: string,
value: any
) {
const newState = state.update("expressions", () => {
const list = state.expressions;
const index = list.findIndex(e => e.input == key);
return list.update(index, () => value);
});
const list = [...state.expressions];
const index = list.findIndex(e => e.input == key);
list[index] = value;
const newState = { ...state, expressions: list };
storeExpressions(newState);
return newState;
}
function deleteExpression(state: Record<ExpressionState>, input: string) {
const index = state.expressions.findIndex(e => e.input == input);
const newState = state.deleteIn(["expressions", index]);
function deleteExpression(state: ExpressionState, input: string) {
const list = [...state.expressions];
const index = list.findIndex(e => e.input == input);
list.splice(index, 1);
const newState = { ...state, expressions: list };
storeExpressions(newState);
return newState;
}
const getExpressionsWrapper = state => state.expressions;
export const getExpressions: Selector<List<Expression>> = createSelector(
export const getExpressions: Selector<Array<Expression>> = createSelector(
getExpressionsWrapper,
expressions => expressions.expressions
);
export const getAutocompleteMatches: Selector<
Map<string, List<string>>
> = createSelector(
export const getAutocompleteMatches: Selector<AutocompleteMatches> = createSelector(
getExpressionsWrapper,
expressions => expressions.autocompleteMatches
);
@ -164,8 +167,10 @@ export function getExpression(state: State, input: string) {
}
export function getAutocompleteMatchset(state: State) {
const input = state.expressions.get("currentAutocompleteInput");
return getAutocompleteMatches(state).get(input);
const input = state.expressions.currentAutocompleteInput;
if (input) {
return getAutocompleteMatches(state)[input];
}
}
export const getExpressionError: Selector<boolean> = createSelector(

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

@ -9,17 +9,15 @@
* @module reducers/fileSearch
*/
import makeRecord from "../utils/makeRecord";
import { prefs } from "../utils/prefs";
import type { Action } from "../actions/types";
import type { Record } from "../utils/makeRecord";
export type Modifiers = Record<{
export type Modifiers = {
caseSensitive: boolean,
wholeWord: boolean,
regexMatch: boolean,
}>;
};
export type MatchedLocations = {
line: number,
@ -46,27 +44,27 @@ const emptySearchResults = Object.freeze({
count: 0,
});
export const createFileSearchState: () => Record<FileSearchState> = makeRecord({
export const createFileSearchState = () => ({
query: "",
searchResults: emptySearchResults,
modifiers: makeRecord({
modifiers: {
caseSensitive: prefs.fileSearchCaseSensitive,
wholeWord: prefs.fileSearchWholeWord,
regexMatch: prefs.fileSearchRegexMatch,
})(),
},
});
function update(
state: Record<FileSearchState> = createFileSearchState(),
state: FileSearchState = createFileSearchState(),
action: Action
): Record<FileSearchState> {
): FileSearchState {
switch (action.type) {
case "UPDATE_FILE_SEARCH_QUERY": {
return state.set("query", action.query);
return { ...state, query: action.query };
}
case "UPDATE_SEARCH_RESULTS": {
return state.set("searchResults", action.results);
return { ...state, searchResults: action.results };
}
case "TOGGLE_FILE_SEARCH_MODIFIER": {
@ -84,11 +82,14 @@ function update(
prefs.fileSearchRegexMatch = actionVal;
}
return state.setIn(["modifiers", action.modifier], actionVal);
return {
...state,
modifiers: { ...state.modifiers, [action.modifier]: actionVal },
};
}
case "NAVIGATE": {
return state.set("query", "").set("searchResults", emptySearchResults);
return { ...state, query: "", searchResults: emptySearchResults };
}
default: {
@ -99,7 +100,7 @@ function update(
// NOTE: we'd like to have the app state fully typed
// https://github.com/firefox-devtools/debugger/blob/master/src/reducers/sources.js#L179-L185
type OuterState = { fileSearch: Record<FileSearchState> };
type OuterState = { fileSearch: FileSearchState };
export function getFileSearchQuery(state: OuterState): string {
return state.fileSearch.query;

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

@ -9,10 +9,8 @@
* @module reducers/quick-open
*/
import makeRecord from "../utils/makeRecord";
import { parseQuickOpenQuery } from "../utils/quick-open";
import type { Action } from "../actions/types";
import type { Record } from "../utils/makeRecord";
export type QuickOpenType = "sources" | "functions" | "goto" | "gotoSource";
@ -22,50 +20,52 @@ export type QuickOpenState = {
searchType: QuickOpenType,
};
export const createQuickOpenState: () => Record<QuickOpenState> = makeRecord({
export const createQuickOpenState = (): QuickOpenState => ({
enabled: false,
query: "",
searchType: "sources",
});
export default function update(
state: Record<QuickOpenState> = createQuickOpenState(),
state: QuickOpenState = createQuickOpenState(),
action: Action
): Record<QuickOpenState> {
): QuickOpenState {
switch (action.type) {
case "OPEN_QUICK_OPEN":
if (action.query != null) {
return state.merge({
return {
...state,
enabled: true,
query: action.query,
searchType: parseQuickOpenQuery(action.query),
});
};
}
return state.set("enabled", true);
return { ...state, enabled: true };
case "CLOSE_QUICK_OPEN":
return createQuickOpenState();
case "SET_QUICK_OPEN_QUERY":
return state.merge({
return {
...state,
query: action.query,
searchType: parseQuickOpenQuery(action.query),
});
};
default:
return state;
}
}
type OuterState = {
quickOpen: Record<QuickOpenState>,
quickOpen: QuickOpenState,
};
export function getQuickOpenEnabled(state: OuterState): boolean {
return state.quickOpen.get("enabled");
return state.quickOpen.enabled;
}
export function getQuickOpenQuery(state: OuterState) {
return state.quickOpen.get("query");
return state.quickOpen.query;
}
export function getQuickOpenType(state: OuterState): QuickOpenType {
return state.quickOpen.get("searchType");
return state.quickOpen.searchType;
}

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

@ -32,7 +32,7 @@ export type State = {
expressions: Record<ExpressionState>,
eventListenerBreakpoints: EventListenersState,
debuggee: DebuggeeState,
fileSearch: Record<FileSearchState>,
fileSearch: FileSearchState,
pause: PauseState,
preview: PreviewState,
pendingBreakpoints: PendingBreakpointsState,
@ -40,8 +40,8 @@ export type State = {
sources: SourcesState,
sourceActors: SourceActorsState,
tabs: TabList,
ui: Record<UIState>,
quickOpen: Record<QuickOpenState>,
ui: UIState,
quickOpen: QuickOpenState,
};
export type Selector<T> = State => T;

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

@ -9,13 +9,11 @@
* @module reducers/ui
*/
import makeRecord from "../utils/makeRecord";
import { prefs } from "../utils/prefs";
import type { Source, PartialRange, SourceLocation } from "../types";
import type { Source, Range, SourceLocation } from "../types";
import type { Action, panelPositionType } from "../actions/types";
import type { Record } from "../utils/makeRecord";
export type ActiveSearchType = "project" | "file";
@ -23,8 +21,6 @@ export type OrientationType = "horizontal" | "vertical";
export type SelectedPrimaryPaneTabType = "sources" | "outline";
type Viewport = PartialRange;
export type UIState = {
selectedPrimaryPaneTab: SelectedPrimaryPaneTabType,
activeSearch: ?ActiveSearchType,
@ -33,7 +29,7 @@ export type UIState = {
endPanelCollapsed: boolean,
frameworkGroupingOn: boolean,
orientation: OrientationType,
viewport: ?Viewport,
viewport: ?Range,
highlightedLineRange?: {
start?: number,
end?: number,
@ -43,7 +39,7 @@ export type UIState = {
isLogPoint: boolean,
};
export const createUIState: () => Record<UIState> = makeRecord({
export const createUIState = (): UIState => ({
selectedPrimaryPaneTab: "sources",
activeSearch: null,
shownSource: null,
@ -57,36 +53,33 @@ export const createUIState: () => Record<UIState> = makeRecord({
viewport: null,
});
function update(
state: Record<UIState> = createUIState(),
action: Action
): Record<UIState> {
function update(state: UIState = createUIState(), action: Action): UIState {
switch (action.type) {
case "TOGGLE_ACTIVE_SEARCH": {
return state.set("activeSearch", action.value);
return { ...state, activeSearch: action.value };
}
case "TOGGLE_FRAMEWORK_GROUPING": {
prefs.frameworkGroupingOn = action.value;
return state.set("frameworkGroupingOn", action.value);
return { ...state, frameworkGroupingOn: action.value };
}
case "SET_ORIENTATION": {
return state.set("orientation", action.orientation);
return { ...state, orientation: action.orientation };
}
case "SHOW_SOURCE": {
return state.set("shownSource", action.source);
return { ...state, shownSource: action.source };
}
case "TOGGLE_PANE": {
if (action.position == "start") {
prefs.startPanelCollapsed = action.paneCollapsed;
return state.set("startPanelCollapsed", action.paneCollapsed);
return { ...state, startPanelCollapsed: action.paneCollapsed };
}
prefs.endPanelCollapsed = action.paneCollapsed;
return state.set("endPanelCollapsed", action.paneCollapsed);
return { ...state, endPanelCollapsed: action.paneCollapsed };
}
case "HIGHLIGHT_LINES":
@ -97,36 +90,38 @@ function update(
lineRange = { start, end, sourceId };
}
return state.set("highlightedLineRange", lineRange);
return { ...state, highlightedLineRange: lineRange };
case "CLOSE_QUICK_OPEN":
case "CLEAR_HIGHLIGHT_LINES":
return state.set("highlightedLineRange", {});
return { ...state, highlightedLineRange: {} };
case "OPEN_CONDITIONAL_PANEL":
return state
.set("conditionalPanelLocation", action.location)
.set("isLogPoint", action.log);
return {
...state,
conditionalPanelLocation: action.location,
isLogPoint: action.log,
};
case "CLOSE_CONDITIONAL_PANEL":
return state.set("conditionalPanelLocation", null);
return { ...state, conditionalPanelLocation: null };
case "SET_PRIMARY_PANE_TAB":
return state.set("selectedPrimaryPaneTab", action.tabName);
return { ...state, selectedPrimaryPaneTab: action.tabName };
case "CLOSE_PROJECT_SEARCH": {
if (state.get("activeSearch") === "project") {
return state.set("activeSearch", null);
if (state.activeSearch === "project") {
return { ...state, activeSearch: null };
}
return state;
}
case "SET_VIEWPORT": {
return state.set("viewport", action.viewport);
return { ...state, viewport: action.viewport };
}
case "NAVIGATE": {
return state.set("activeSearch", null).set("highlightedLineRange", {});
return { ...state, activeSearch: null, highlightedLineRange: {} };
}
default: {
@ -137,24 +132,24 @@ function update(
// NOTE: we'd like to have the app state fully typed
// https://github.com/firefox-devtools/debugger/blob/master/src/reducers/sources.js#L179-L185
type OuterState = { ui: Record<UIState> };
type OuterState = { ui: UIState };
export function getSelectedPrimaryPaneTab(
state: OuterState
): SelectedPrimaryPaneTabType {
return state.ui.get("selectedPrimaryPaneTab");
return state.ui.selectedPrimaryPaneTab;
}
export function getActiveSearch(state: OuterState): ActiveSearchType {
return state.ui.get("activeSearch");
export function getActiveSearch(state: OuterState): ?ActiveSearchType {
return state.ui.activeSearch;
}
export function getFrameworkGroupingState(state: OuterState): boolean {
return state.ui.get("frameworkGroupingOn");
return state.ui.frameworkGroupingOn;
}
export function getShownSource(state: OuterState): Source {
return state.ui.get("shownSource");
export function getShownSource(state: OuterState): ?Source {
return state.ui.shownSource;
}
export function getPaneCollapse(
@ -162,32 +157,32 @@ export function getPaneCollapse(
position: panelPositionType
): boolean {
if (position == "start") {
return state.ui.get("startPanelCollapsed");
return state.ui.startPanelCollapsed;
}
return state.ui.get("endPanelCollapsed");
return state.ui.endPanelCollapsed;
}
export function getHighlightedLineRange(state: OuterState) {
return state.ui.get("highlightedLineRange");
return state.ui.highlightedLineRange;
}
export function getConditionalPanelLocation(
state: OuterState
): null | SourceLocation {
return state.ui.get("conditionalPanelLocation");
return state.ui.conditionalPanelLocation;
}
export function getLogPointStatus(state: OuterState): boolean {
return state.ui.get("isLogPoint");
return state.ui.isLogPoint;
}
export function getOrientation(state: OuterState): OrientationType {
return state.ui.get("orientation");
return state.ui.orientation;
}
export function getViewport(state: OuterState) {
return state.ui.get("viewport");
return state.ui.viewport;
}
export default update;

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

@ -147,7 +147,7 @@ function convertToList(
export function getColumnBreakpoints(
positions: ?BreakpointPositions,
breakpoints: ?(Breakpoint[]),
viewport: Range,
viewport: ?Range,
selectedSourceWithContent: ?SourceWithContent
) {
if (!positions || !selectedSourceWithContent) {

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

@ -97,6 +97,7 @@ beforeEach(async () => {
clearHistory();
clearDocuments();
prefs.projectDirectoryRoot = "";
prefs.expressions = [];
// Ensures window.dbg is there to track telemetry
setupHelper({ selectors: {} });