Skip to content

Commit

Permalink
Improve JSX syntax highlighting (firefox-devtools#4539)
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/actions/sources/loadSourceText.js
#	src/actions/tests/ast.spec.js
#	src/components/Editor/index.js
  • Loading branch information
nyrosmith authored and jasonLaster committed Nov 16, 2017
1 parent d3d88b6 commit a1877f4
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 25 deletions.
27 changes: 26 additions & 1 deletion src/actions/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { PROMISE } from "../utils/redux/middleware/promise";
import {
getSymbols,
getEmptyLines,
getOutOfScopeLocations
getOutOfScopeLocations,
isReactComponent
} from "../workers/parser";

import { findBestMatchExpression } from "../utils/ast";
Expand All @@ -29,6 +30,29 @@ const extraProps = {
react: { displayName: "this._reactInternalInstance.getName()" }
};

export function setSourceMetaData(sourceId: SourceId) {
return async ({ dispatch, getState }: ThunkArgs) => {
const sourceRecord = getSource(getState(), sourceId);
if (!sourceRecord) {
return;
}

const source = sourceRecord.toJS();
if (!source.text || source.isWasm) {
return;
}

const isReactComp = await isReactComponent(source);
dispatch({
type: "SET_SOURCE_METADATA",
sourceId: source.id,
sourceMetaData: {
isReactComponent: isReactComp
}
});
};
}

export function setSymbols(sourceId: SourceId) {
return async ({ dispatch, getState }: ThunkArgs) => {
const sourceRecord = getSource(getState(), sourceId);
Expand All @@ -50,6 +74,7 @@ export function setSymbols(sourceId: SourceId) {
});

dispatch(setEmptyLines(source.id));
dispatch(setSourceMetaData(source.id));
};
}

Expand Down
30 changes: 28 additions & 2 deletions src/actions/tests/ast.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import {
} from "../../utils/test-head";

import readFixture from "./helpers/readFixture";
const { getSymbols, getEmptyLines, getOutOfScopeLocations } = selectors;
const {
getSymbols,
getEmptyLines,
getOutOfScopeLocations,
getSourceMetaData
} = selectors;
import getInScopeLines from "../../selectors/linesInScope";

const threadClient = {
Expand All @@ -34,7 +39,8 @@ const threadClient = {
const sourceTexts = {
"base.js": "function base(boo) {}",
"foo.js": "function base(boo) { return this.bazz; } outOfScope",
"scopes.js": readFixture("scopes.js")
"scopes.js": readFixture("scopes.js"),
"reactComponent.js": readFixture("reactComponent.js")
};

const evaluationResult = {
Expand All @@ -61,6 +67,26 @@ describe("ast", () => {
expect(emptyLines).toMatchSnapshot();
});
});
describe("setSourceMetaData", () => {
it("should detect react components", async () => {
const { dispatch, getState } = createStore(threadClient);
const source = makeSource("reactComponent.js");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText({ id: "reactComponent.js" }));

const sourceMetaData = getSourceMetaData(getState(), source.id);
expect(sourceMetaData).toEqual({ isReactComponent: true });
});
it("should not give false positive on non react components", async () => {
const { dispatch, getState } = createStore(threadClient);
const source = makeSource("base.js");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText({ id: "base.js" }));

const sourceMetaData = getSourceMetaData(getState(), source.id);
expect(sourceMetaData).toEqual({ isReactComponent: false });
});
});

describe("setSymbols", () => {
describe("when the source is loaded", () => {
Expand Down
7 changes: 7 additions & 0 deletions src/actions/tests/fixtures/reactComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React, { Component } from "react";

class FixtureComponent extends Component {
render() {
return null;
}
}
7 changes: 4 additions & 3 deletions src/components/Editor/Breakpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ function makeMarker(isDisabled: boolean) {
type Props = {
breakpoint: Object,
selectedSource: Object,
editor: Object
editor: Object,
sourceMetaData: Object
};

class Breakpoint extends Component<Props> {
Expand All @@ -36,7 +37,7 @@ class Breakpoint extends Component<Props> {
}

addBreakpoint() {
const { breakpoint, editor, selectedSource } = this.props;
const { breakpoint, editor, selectedSource, sourceMetaData } = this.props;

// Hidden Breakpoints are never rendered on the client
if (breakpoint.hidden) {
Expand All @@ -52,7 +53,7 @@ class Breakpoint extends Component<Props> {
const sourceId = selectedSource.get("id");
const line = toEditorLine(sourceId, breakpoint.location.line);

showSourceText(editor, selectedSource.toJS());
showSourceText(editor, selectedSource.toJS(), sourceMetaData);

editor.codeMirror.setGutterMarker(
line,
Expand Down
11 changes: 7 additions & 4 deletions src/components/Editor/Breakpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React, { Component } from "react";
import Breakpoint from "./Breakpoint";

import actions from "../../actions";
import { getSelectedSource } from "../../selectors";
import { getSelectedSource, getSourceMetaData } from "../../selectors";
import getVisibleBreakpoints from "../../selectors/visibleBreakpoints";
import { makeLocationId } from "../../utils/breakpoint";
import { isLoaded } from "../../utils/source";
Expand All @@ -16,7 +16,8 @@ import type { SourceRecord, BreakpointsMap } from "../../reducers/types";
type Props = {
selectedSource: SourceRecord,
breakpoints: BreakpointsMap,
editor: Object
editor: Object,
sourceMetaData: Object
};

class Breakpoints extends Component<Props> {
Expand All @@ -32,7 +33,7 @@ class Breakpoints extends Component<Props> {
}

render() {
const { breakpoints, selectedSource, editor } = this.props;
const { breakpoints, selectedSource, editor, sourceMetaData } = this.props;

if (!selectedSource || !breakpoints || selectedSource.get("isBlackBoxed")) {
return null;
Expand All @@ -46,6 +47,7 @@ class Breakpoints extends Component<Props> {
key={makeLocationId(bp.location)}
breakpoint={bp}
selectedSource={selectedSource}
sourceMetaData={sourceMetaData}
editor={editor}
/>
);
Expand All @@ -58,7 +60,8 @@ class Breakpoints extends Component<Props> {
export default connect(
state => ({
breakpoints: getVisibleBreakpoints(state),
selectedSource: getSelectedSource(state)
selectedSource: getSelectedSource(state),
sourceMetaData: getSourceMetaData(state, getSelectedSource(state).id)
}),
dispatch => bindActionCreators(actions, dispatch)
)(Breakpoints);
27 changes: 20 additions & 7 deletions src/components/Editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
getCoverageEnabled,
getConditionalPanelLine,
getFileSearchModifiers,
getFileSearchQuery
getFileSearchQuery,
getSourceMetaData,
} from "../../selectors";

import actions from "../../actions";
Expand All @@ -36,6 +37,7 @@ import EmptyLines from "./EmptyLines";
import GutterMenu from "./GutterMenu";
import EditorMenu from "./EditorMenu";
import ConditionalPanel from "./ConditionalPanel";
import type { SourceMetaDataType } from "../../reducers/ast";

import {
showSourceText,
Expand Down Expand Up @@ -81,10 +83,17 @@ type Props = {
startPanelSize: number,
endPanelSize: number,
conditionalPanelLine: number,
openConditionalPanel: Function,
closeConditionalPanel: Function,
continueToHere: Function,
setContextMenu: Function
sourceMetaData: SourceMetaDataType,

// Actions
openConditionalPanel: number => void,
closeConditionalPanel: void => void,
setContextMenu: (string, any) => void,
continueToHere: number => void,
toggleBreakpoint: number => void,
addOrToggleDisabledBreakpoint: number => void,
jumpToMappedLocation: any => void,
traverseResults: (boolean, Object) => void
};

type State = {
Expand Down Expand Up @@ -459,7 +468,7 @@ class Editor extends PureComponent<Props, State> {
}

setText(props) {
const { selectedSource } = props;
const { selectedSource, sourceMetaData } = props;
if (!this.state.editor) {
return;
}
Expand All @@ -477,7 +486,11 @@ class Editor extends PureComponent<Props, State> {
}

if (selectedSource) {
return showSourceText(this.state.editor, selectedSource.toJS());
return showSourceText(
this.state.editor,
selectedSource.toJS(),
sourceMetaData
);
}
}

Expand Down
23 changes: 21 additions & 2 deletions src/reducers/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ type EmptyLinesType = number[];
export type SymbolsMap = Map<string, SymbolDeclarations>;
export type EmptyLinesMap = Map<string, EmptyLinesType>;

export type SourceMetaDataType = {
isReactComponent: boolean
};

export type SourceMetaDataMap = Map<string, SourceMetaDataType>;

export type Preview =
| {| updating: true |}
| null
Expand All @@ -38,7 +44,8 @@ export type ASTState = {
symbols: SymbolsMap,
emptyLines: EmptyLinesMap,
outOfScopeLocations: ?Array<AstLocation>,
preview: Preview
preview: Preview,
sourceMetaData: SourceMetaDataMap
};

export function initialState() {
Expand All @@ -47,7 +54,8 @@ export function initialState() {
symbols: I.Map(),
emptyLines: I.Map(),
outOfScopeLocations: null,
preview: null
preview: null,
sourceMetaData: I.Map()
}: ASTState)
)();
}
Expand Down Expand Up @@ -111,6 +119,13 @@ function update(
return initialState();
}

case "SET_SOURCE_METADATA": {
return state.setIn(
["sourceMetaData", action.sourceId],
action.sourceMetaData
);
}

default: {
return state;
}
Expand Down Expand Up @@ -167,4 +182,8 @@ export function getPreview(state: OuterState) {
return state.ast.get("preview");
}

export function getSourceMetaData(state: OuterState, sourceId: string) {
return state.ast.getIn(["sourceMetaData", sourceId]) || {};
}

export default update;
11 changes: 9 additions & 2 deletions src/utils/editor/source-documents.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getMode } from "../source";
import type { Source } from "debugger-html";
import { isWasm, getWasmLineNumberFormatter, renderWasmText } from "../wasm";
import { resizeBreakpointGutter, resizeToggleButton } from "../ui";
import type { SourceMetaDataType } from "../../reducers/ast";

let sourceDocs = {};

Expand Down Expand Up @@ -81,19 +82,25 @@ function setEditorText(editor: Object, source: Source) {
* Handle getting the source document or creating a new
* document with the correct mode and text.
*/
function showSourceText(editor: Object, source: Source) {
function showSourceText(
editor: Object,
source: Source,
sourceMetaData: SourceMetaDataType
) {
if (!source) {
return;
}

let doc = getDocument(source.id);
if (editor.codeMirror.doc === doc) {
editor.setMode(getMode(source, sourceMetaData));
return;
}

if (doc) {
editor.replaceDocument(doc);
updateLineNumberFormat(editor, source.id);
editor.setMode(getMode(source, sourceMetaData));
return doc;
}

Expand All @@ -102,7 +109,7 @@ function showSourceText(editor: Object, source: Source) {
editor.replaceDocument(doc);

setEditorText(editor, source);
editor.setMode(getMode(source));
editor.setMode(getMode(source, sourceMetaData));
updateLineNumberFormat(editor, source.id);
}

Expand Down
10 changes: 9 additions & 1 deletion src/utils/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { basename } from "../utils/path";
import { parse as parseURL } from "url";

import type { Source } from "../types";
import type { SourceMetaDataType } from "../reducers/ast";

type transformUrlCallback = string => string;

Expand Down Expand Up @@ -199,13 +200,20 @@ function getSourceLineCount(source: Source) {
* @static
*/

function getMode(source: Source) {
function getMode(source: Source, sourceMetaData: SourceMetaDataType) {
const { contentType, text, isWasm, url } = source;

if (!text || isWasm) {
return { name: "text" };
}

if (
(url && url.match(/\.jsx$/i)) ||
(sourceMetaData && sourceMetaData.isReactComponent)
) {
return "jsx";
}

// if the url ends with .marko we set the name to Javascript so
// syntax highlighting works for marko too
if (url && url.match(/\.marko$/i)) {
Expand Down
Loading

0 comments on commit a1877f4

Please sign in to comment.