Skip to content

Commit

Permalink
add a GotoLineModal (firefox-devtools#4323)
Browse files Browse the repository at this point in the history
This commit adds a gotoline modal/functionality triggered with `Cmd+:`
(`CtrlOrCmd+Shift+;`) in the main debugger source window.

A string that cannot be `parseInt()`'d is a noop. A number higher
than the number of lines in the file will go to the end of the file.
  • Loading branch information
jfo authored and Johnny Khalil committed Oct 13, 2017
1 parent 531712a commit 8e633b8
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 1 deletion.
5 changes: 5 additions & 0 deletions assets/panel/debugger.properties
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,11 @@ watchExpressionsSeparatorLabel2=\u0020→
# and its real name (if available).
functionSearchSeparatorLabel=

# LOCALIZATION NOTE(gotoLineModal.placeholder): The placeholder
# text displayed when the user searches for specific lines in a file
gotoLineModal.placeholder=Go to line…
gotoLineModal.commandKey=CmdOrCtrl+Shift+;

# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder
# text displayed when the user searches for functions in a file
symbolSearch.search.functionsPlaceholder=Search functions…
Expand Down
52 changes: 52 additions & 0 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import EditorTabs from "./Editor/Tabs";

import SymbolModal from "./SymbolModal";

import GotoLineModal from "./GotoLineModal";

type Props = {
selectSource: Function,
selectedSource: SourceRecord,
Expand All @@ -71,6 +73,7 @@ class App extends Component {
renderEditorPane: Function;
renderVerticalLayout: Function;
toggleSymbolModal: Function;
toggleGoToLineModal: Function;
onEscape: Function;
onCommandSlash: Function;

Expand All @@ -86,6 +89,7 @@ class App extends Component {
this.getChildContext = this.getChildContext.bind(this);
this.onLayoutChange = this.onLayoutChange.bind(this);
this.toggleSymbolModal = this.toggleSymbolModal.bind(this);
this.toggleGoToLineModal = this.toggleGoToLineModal.bind(this);
this.renderEditorPane = this.renderEditorPane.bind(this);
this.renderVerticalLayout = this.renderVerticalLayout.bind(this);
this.onEscape = this.onEscape.bind(this);
Expand All @@ -98,10 +102,17 @@ class App extends Component {

componentDidMount() {
verticalLayoutBreakpoint.addListener(this.onLayoutChange);

shortcuts.on(
L10N.getStr("symbolSearch.search.key2"),
this.toggleSymbolModal
);

shortcuts.on(
L10N.getStr("gotoLineModal.commandKey"),
this.toggleGoToLineModal
);

shortcuts.on("Escape", this.onEscape);
shortcuts.on("Cmd+/", this.onCommandSlash);
}
Expand All @@ -112,6 +123,9 @@ class App extends Component {
L10N.getStr("symbolSearch.search.key2"),
this.toggleSymbolModal
);

shortcuts.off(L10N.getStr("gotoLineModal.key"), this.toggleGoToLineModal);

shortcuts.off("Escape", this.onEscape);
}

Expand Down Expand Up @@ -150,6 +164,28 @@ class App extends Component {
setActiveSearch("symbol");
}

toggleGoToLineModal(_, e: SyntheticEvent) {
const {
selectedSource,
activeSearch,
closeActiveSearch,
setActiveSearch
} = this.props;

e.preventDefault();
e.stopPropagation();

if (!selectedSource) {
return;
}

if (activeSearch == "line") {
return closeActiveSearch();
}

setActiveSearch("line");
}

onLayoutChange() {
if (isVisible()) {
this.setState({ horizontal: verticalLayoutBreakpoint.matches });
Expand Down Expand Up @@ -275,6 +311,21 @@ class App extends Component {
);
}

renderGotoLineModal() {
const { selectSource, selectedSource, activeSearch } = this.props;

if (activeSearch !== "line") {
return;
}

return (
<GotoLineModal
selectSource={selectSource}
selectedSource={selectedSource}
/>
);
}

renderShortcutsModal() {
const additionalClass = isMacOS ? "mac" : "";

Expand All @@ -298,6 +349,7 @@ class App extends Component {
? this.renderHorizontalLayout()
: this.renderVerticalLayout()}
{this.renderSymbolModal()}
{this.renderGotoLineModal()}
{this.renderShortcutsModal()}
</div>
);
Expand Down
123 changes: 123 additions & 0 deletions src/components/GotoLineModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// @flow

import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { getActiveSearch, getSelectedSource } from "../selectors";
import actions from "../actions";

import Modal from "./shared/Modal";

import SearchInput from "./shared/SearchInput";

import type { SourceRecord } from "../reducers/sources";
import type { SelectSourceOptions } from "../actions/sources";

type GotoLineModalState = {
query: ?string
};

import "./SymbolModal.css";

class GotoLineModal extends Component {
state: GotoLineModalState;

props: {
enabled: boolean,
selectSource: (string, ?SelectSourceOptions) => void,
selectedSource?: SourceRecord,
closeActiveSearch: () => void,
highlightLineRange: ({ start: number, end: number }) => void,
clearHighlightLineRange: () => void
};

constructor(props) {
super(props);
this.state = { query: "" };
}

onClick = (e: SyntheticEvent) => {
e.stopPropagation();
};

onChange = (e: SyntheticInputEvent) => {
const { selectedSource } = this.props;
if (!selectedSource || !selectedSource.get("text")) {
return;
}

this.setState({ query: e.target.value });
};

closeModal = () => {
this.props.closeActiveSearch();
this.props.clearHighlightLineRange();
};

onKeyUp = (e: SyntheticKeyboardEvent) => {
e.preventDefault();
const { selectSource, selectedSource, enabled } = this.props;
const { query } = this.state;

if (!enabled || !selectedSource) {
return;
}

if (e.key === "Enter" && query != null) {
const linenumber = parseInt(query.replace(/[^\d+]/g, ""), 10);
if (!isNaN(linenumber)) {
selectSource(selectedSource.get("id"), { line: linenumber });
}
this.closeModal();
return;
}

if (e.key === "Tab") {
this.closeModal();
return;
}
return;
};

renderInput() {
const { query } = this.state;

return (
<div key="input" className="input-wrapper">
<SearchInput
query={query}
placeholder={this.buildPlaceHolder()}
onChange={this.onChange}
onKeyUp={this.onKeyUp}
handleClose={this.closeModal}
/>
</div>
);
}

buildPlaceHolder = () => L10N.getFormatStr("gotoLineModal.placeholder");

render() {
const { enabled } = this.props;

if (!enabled) {
return null;
}

return (
<Modal in={enabled} handleClose={this.closeModal}>
{this.renderInput()}
</Modal>
);
}
}

export default connect(
state => {
const source = getSelectedSource(state);
return {
enabled: Boolean(getActiveSearch(state) === "line" && source)
};
},
dispatch => bindActionCreators(actions, dispatch)
)(GotoLineModal);
7 changes: 6 additions & 1 deletion src/reducers/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export type FileSearchModifiers = Record<{
}>;

export type SymbolSearchType = "functions" | "variables";
export type ActiveSearchType = "project" | "source" | "file" | "symbol";
export type ActiveSearchType =
| "project"
| "source"
| "file"
| "symbol"
| "line";

export type MatchedLocations = {
line: number,
Expand Down

0 comments on commit 8e633b8

Please sign in to comment.