Skip to content

Commit 36c2bf5

Browse files
authored
[DevTools] Allow all file links in Chrome DevTools to open in external editor (facebook#33985)
Stacked on facebook#33983. Allow React to be configured as the default handler of all links in Chrome DevTools. To do this you need to configure the Chrome DevTools setting for "Link Handling:" to be set to "React Developer Tools". By default this doesn't do anything but if you then check the box added in facebook#33983 it starts open local files directly in the external editor. This needs docs to show how to enable that option. (As far as I can tell this broke in Chrome Canary 🙄 but hopefully fixed before stable.)
1 parent 190758e commit 36c2bf5

File tree

4 files changed

+94
-7
lines changed

4 files changed

+94
-7
lines changed

packages/react-devtools-extensions/src/main/index.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ import {
1818
LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY,
1919
} from 'react-devtools-shared/src/constants';
2020
import {logEvent} from 'react-devtools-shared/src/Logger';
21-
import {normalizeUrlIfValid} from 'react-devtools-shared/src/utils';
21+
import {
22+
getAlwaysOpenInEditor,
23+
getOpenInEditorURL,
24+
normalizeUrlIfValid,
25+
} from 'react-devtools-shared/src/utils';
26+
import {checkConditions} from 'react-devtools-shared/src/devtools/views/Editor/utils';
2227

2328
import {
2429
setBrowserSelectionFromReact,
@@ -543,3 +548,32 @@ if (chrome.devtools.panels.setThemeChangeHandler) {
543548
// Firefox
544549
chrome.devtools.panels.onThemeChanged.addListener(onThemeChanged);
545550
}
551+
552+
// Firefox doesn't support resources handlers yet.
553+
if (chrome.devtools.panels.setOpenResourceHandler) {
554+
chrome.devtools.panels.setOpenResourceHandler(
555+
(
556+
resource,
557+
lineNumber = 1,
558+
// The column is a new feature so we have to specify a default if it doesn't exist
559+
columnNumber = 1,
560+
) => {
561+
const alwaysOpenInEditor = getAlwaysOpenInEditor();
562+
const editorURL = getOpenInEditorURL();
563+
if (alwaysOpenInEditor && editorURL) {
564+
const location = ['', resource.url, lineNumber, columnNumber];
565+
const {url, shouldDisableButton} = checkConditions(editorURL, location);
566+
if (!shouldDisableButton) {
567+
window.open(url);
568+
return;
569+
}
570+
}
571+
// Otherwise fallback to the built-in behavior.
572+
chrome.devtools.panels.openResource(
573+
resource.url,
574+
lineNumber - 1,
575+
columnNumber - 1,
576+
);
577+
},
578+
);
579+
}

packages/react-devtools-shared/src/devtools/views/Editor/EditorPane.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type Props = {selectedSource: ?SourceSelection};
3737

3838
function EditorPane({selectedSource}: Props) {
3939
const [showSettings, setShowSettings] = useState(false);
40+
const [showLinkInfo, setShowLinkInfo] = useState(false);
4041

4142
const editorURL = useSyncExternalStore(
4243
function subscribe(callback) {
@@ -50,6 +51,30 @@ function EditorPane({selectedSource}: Props) {
5051
},
5152
);
5253

54+
if (showLinkInfo) {
55+
return (
56+
<div className={styles.EditorPane}>
57+
<div className={styles.EditorToolbar}>
58+
<div style={{display: 'flex', flex: '1 1 auto'}}>
59+
To enable link handling in your browser's DevTools settings, look
60+
for the option Extension -> Link Handling. Select "React Developer
61+
Tools".
62+
</div>
63+
<div className={styles.VRule} />
64+
<Button
65+
onClick={() =>
66+
startTransition(() => {
67+
setShowLinkInfo(false);
68+
setShowSettings(false);
69+
})
70+
}>
71+
<ButtonIcon type="close" />
72+
</Button>
73+
</div>
74+
</div>
75+
);
76+
}
77+
5378
let editorToolbar;
5479
if (showSettings) {
5580
editorToolbar = (
@@ -87,7 +112,13 @@ function EditorPane({selectedSource}: Props) {
87112
{editorToolbar}
88113
<div className={styles.EditorInfo}>
89114
{editorURL ? (
90-
<CodeEditorByDefault />
115+
<CodeEditorByDefault
116+
onChange={alwaysOpenInEditor => {
117+
if (alwaysOpenInEditor) {
118+
startTransition(() => setShowLinkInfo(true));
119+
}
120+
}}
121+
/>
91122
) : (
92123
'Configure an external editor to open local files.'
93124
)}

packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorByDefault.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import {useLocalStorage} from '../hooks';
1313

1414
import styles from './SettingsShared.css';
1515

16-
export default function CodeEditorByDefault(_: {}): React.Node {
16+
export default function CodeEditorByDefault({
17+
onChange,
18+
}: {
19+
onChange?: boolean => void,
20+
}): React.Node {
1721
const [alwaysOpenInEditor, setAlwaysOpenInEditor] = useLocalStorage<boolean>(
1822
LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR,
1923
false,
@@ -24,9 +28,12 @@ export default function CodeEditorByDefault(_: {}): React.Node {
2428
<input
2529
type="checkbox"
2630
checked={alwaysOpenInEditor}
27-
onChange={({currentTarget}) =>
28-
setAlwaysOpenInEditor(currentTarget.checked)
29-
}
31+
onChange={({currentTarget}) => {
32+
setAlwaysOpenInEditor(currentTarget.checked);
33+
if (onChange) {
34+
onChange(currentTarget.checked);
35+
}
36+
}}
3037
className={styles.SettingRowCheckbox}
3138
/>
3239
Open local files directly in your code editor

packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import {SettingsContext} from './SettingsContext';
1313
import {StoreContext} from '../context';
1414
import {CHANGE_LOG_URL} from 'react-devtools-shared/src/devtools/constants';
1515
import {isInternalFacebookBuild} from 'react-devtools-feature-flags';
16-
import CodeEditorOptions from './CodeEditorOptions';
1716

1817
import styles from './SettingsShared.css';
18+
19+
import CodeEditorOptions from './CodeEditorOptions';
1920
import CodeEditorByDefault from './CodeEditorByDefault';
21+
import {LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR} from '../../../constants';
22+
import {useLocalStorage} from '../hooks';
2023

2124
function getChangeLogUrl(version: ?string): string | null {
2225
if (!version) {
@@ -47,6 +50,11 @@ export default function GeneralSettings(_: {}): React.Node {
4750
const showBackendVersion =
4851
backendVersion && backendVersion !== frontendVersion;
4952

53+
const [alwaysOpenInEditor] = useLocalStorage<boolean>(
54+
LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR,
55+
false,
56+
);
57+
5058
return (
5159
<div className={styles.SettingList}>
5260
{isInternalFacebookBuild && (
@@ -87,6 +95,13 @@ export default function GeneralSettings(_: {}): React.Node {
8795

8896
<div className={styles.SettingWrapper}>
8997
<CodeEditorByDefault />
98+
{alwaysOpenInEditor && (__IS_CHROME__ || __IS_EDGE__) ? (
99+
<div>
100+
To enable link handling in your browser's DevTools settings, look
101+
for the option Extension -> Link Handling. Select "React Developer
102+
Tools".
103+
</div>
104+
) : null}
90105
</div>
91106

92107
<div className={styles.SettingWrapper}>

0 commit comments

Comments
 (0)