Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DevTools] Add open in editor #22649

Merged
merged 10 commits into from
Nov 3, 2021
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: 2 additions & 0 deletions packages/react-devtools-core/webpack.standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const __DEV__ = NODE_ENV === 'development';

const DEVTOOLS_VERSION = getVersionString();

const EDITOR_URL = process.env.EDITOR_URL || null;
const LOGGING_URL = process.env.LOGGING_URL || null;

const featureFlagTarget =
Expand Down Expand Up @@ -83,6 +84,7 @@ module.exports = {
__TEST__: NODE_ENV === 'test',
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-core"`,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
'process.env.LOGGING_URL': `"${LOGGING_URL}"`,
'process.env.NODE_ENV': `"${NODE_ENV}"`,
Expand Down
2 changes: 2 additions & 0 deletions packages/react-devtools-extensions/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const __DEV__ = NODE_ENV === 'development';

const DEVTOOLS_VERSION = getVersionString(process.env.DEVTOOLS_VERSION);

const EDITOR_URL = process.env.EDITOR_URL || null;
const LOGGING_URL = process.env.LOGGING_URL || null;

const featureFlagTarget = process.env.FEATURE_FLAG_TARGET || 'extension-oss';
Expand Down Expand Up @@ -92,6 +93,7 @@ module.exports = {
__TEST__: NODE_ENV === 'test',
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-extensions"`,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
'process.env.LOGGING_URL': `"${LOGGING_URL}"`,
'process.env.NODE_ENV': `"${NODE_ENV}"`,
Expand Down
3 changes: 3 additions & 0 deletions packages/react-devtools-inline/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ if (!NODE_ENV) {

const __DEV__ = NODE_ENV === 'development';

const EDITOR_URL = process.env.EDITOR_URL || null;

const DEVTOOLS_VERSION = getVersionString();

const babelOptions = {
Expand Down Expand Up @@ -76,6 +78,7 @@ module.exports = {
__TEST__: NODE_ENV === 'test',
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-inline"`,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
'process.env.NODE_ENV': `"${NODE_ENV}"`,
'process.env.DARK_MODE_DIMMED_WARNING_COLOR': `"${DARK_MODE_DIMMED_WARNING_COLOR}"`,
Expand Down
3 changes: 3 additions & 0 deletions packages/react-devtools-shared/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export const LOCAL_STORAGE_FILTER_PREFERENCES_KEY =
export const SESSION_STORAGE_LAST_SELECTION_KEY =
'React::DevTools::lastSelection';

export const LOCAL_STORAGE_OPEN_IN_EDITOR_URL =
'React::DevTools::openInEditorUrl';

export const LOCAL_STORAGE_PARSE_HOOK_NAMES_KEY =
'React::DevTools::parseHookNames';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type IconType =
| 'copy'
| 'delete'
| 'down'
| 'editor'
| 'expanded'
| 'export'
| 'filter'
Expand Down Expand Up @@ -72,6 +73,9 @@ export default function ButtonIcon({className = '', type}: Props) {
case 'down':
pathData = PATH_DOWN;
break;
case 'editor':
pathData = PATH_EDITOR;
break;
case 'expanded':
pathData = PATH_EXPANDED;
break;
Expand Down Expand Up @@ -268,3 +272,7 @@ const PATH_VIEW_DOM = `
const PATH_VIEW_SOURCE = `
M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z
`;

const PATH_EDITOR = `
M7 5h10v2h2V3c0-1.1-.9-1.99-2-1.99L7 1c-1.1 0-2 .9-2 2v4h2V5zm8.41 11.59L20 12l-4.59-4.59L14 8.83 17.17 12 14 15.17l1.41 1.42zM10 15.17L6.83 12 10 8.83 8.59 7.41 4 12l4.59 4.59L10 15.17zM17 19H7v-2H5v4c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2v-4h-2v2z
`;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import * as React from 'react';
import {useCallback, useContext} from 'react';
import {useCallback, useContext, useSyncExternalStore} from 'react';
import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
import {BridgeContext, StoreContext, OptionsContext} from '../context';
import Button from '../Button';
Expand All @@ -20,6 +20,8 @@ import {ElementTypeSuspense} from 'react-devtools-shared/src/types';
import CannotSuspendWarningMessage from './CannotSuspendWarningMessage';
import InspectedElementView from './InspectedElementView';
import {InspectedElementContext} from './InspectedElementContext';
import {getOpenInEditorURL} from '../../../utils';
import {LOCAL_STORAGE_OPEN_IN_EDITOR_URL} from '../../../constants';

import styles from './InspectedElement.css';

Expand Down Expand Up @@ -123,6 +125,21 @@ export default function InspectedElementWrapper(_: Props) {
inspectedElement != null &&
inspectedElement.canToggleSuspense;

const editorURL = useSyncExternalStore(
function subscribe(callback) {
window.addEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback);
return function unsubscribe() {
window.removeEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback);
};
},
function getState() {
return getOpenInEditorURL();
},
);

const canOpenInEditor =
editorURL && inspectedElement != null && inspectedElement.source != null;

const toggleErrored = useCallback(() => {
if (inspectedElement == null || targetErrorBoundaryID == null) {
return;
Expand Down Expand Up @@ -198,6 +215,18 @@ export default function InspectedElementWrapper(_: Props) {
}
}, [bridge, dispatch, element, isSuspended, modalDialogDispatch, store]);

const onOpenInEditor = useCallback(() => {
bvaughn marked this conversation as resolved.
Show resolved Hide resolved
const source = inspectedElement?.source;
if (source == null || editorURL == null) {
return;
}

const url = new URL(editorURL);
url.href = url.href.replace('{path}', source.fileName);
url.href = url.href.replace('{line}', String(source.lineNumber));
window.open(url);
}, [inspectedElement, editorURL]);

if (element === null) {
return (
<div className={styles.InspectedElement}>
Expand All @@ -223,7 +252,14 @@ export default function InspectedElementWrapper(_: Props) {
{element.displayName}
</div>
</div>

{canOpenInEditor && (
<Button
className={styles.IconButton}
onClick={onOpenInEditor}
title="Open in editor">
<ButtonIcon type="editor" />
</Button>
)}
{canToggleError && (
<Toggle
className={styles.IconButton}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
useRef,
useState,
} from 'react';
import {useSubscription} from '../hooks';
import {LOCAL_STORAGE_OPEN_IN_EDITOR_URL} from '../../../constants';
import {useLocalStorage, useSubscription} from '../hooks';
import {StoreContext} from '../context';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
Expand All @@ -37,6 +38,7 @@ import {
ElementTypeProfiler,
ElementTypeSuspense,
} from 'react-devtools-shared/src/types';
import {getDefaultOpenInEditorURL} from 'react-devtools-shared/src/utils';

import styles from './SettingsShared.css';

Expand Down Expand Up @@ -81,6 +83,11 @@ export default function ComponentsSettings(_: {||}) {
[setParseHookNames],
);

const [openInEditorURL, setOpenInEditorURL] = useLocalStorage<string>(
LOCAL_STORAGE_OPEN_IN_EDITOR_URL,
getDefaultOpenInEditorURL(),
);

const [componentFilters, setComponentFilters] = useState<
Array<ComponentFilter>,
>(() => [...store.componentFilters]);
Expand Down Expand Up @@ -271,6 +278,19 @@ export default function ComponentsSettings(_: {||}) {
<span className={styles.Warning}>(may be slow)</span>
</label>

<label className={styles.OpenInURLSetting}>
Open in Editor URL:{' '}
<input
className={styles.Input}
type="text"
placeholder={process.env.EDITOR_URL ?? 'vscode://file/{path}:{line}'}
value={openInEditorURL}
onChange={event => {
setOpenInEditorURL(event.target.value);
}}
/>
</label>

<div className={styles.Header}>Hide components where...</div>

<table className={styles.Table}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
margin-bottom: 0;
}

.OpenInURLSetting {
margin: 0.5rem 0;
}

.OptionGroup {
display: inline-flex;
flex-direction: row;
Expand All @@ -30,6 +34,10 @@
margin-right: 0.5rem;
}

.Spacer {
height: 0.5rem;
}

.Select {
}

Expand Down
3 changes: 3 additions & 0 deletions packages/react-devtools-shared/src/devtools/views/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ export function useLocalStorage<T>(
value instanceof Function ? (value: any)(storedValue) : value;
setStoredValue(valueToStore);
localStorageSetItem(key, JSON.stringify(valueToStore));

// Notify listeners that this setting has changed.
window.dispatchEvent(new Event(key));
} catch (error) {
console.log(error);
}
Expand Down
17 changes: 17 additions & 0 deletions packages/react-devtools-shared/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import {ElementTypeRoot} from 'react-devtools-shared/src/types';
import {
LOCAL_STORAGE_FILTER_PREFERENCES_KEY,
LOCAL_STORAGE_OPEN_IN_EDITOR_URL,
LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS,
LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY,
LOCAL_STORAGE_SHOW_INLINE_WARNINGS_AND_ERRORS_KEY,
Expand Down Expand Up @@ -386,6 +387,22 @@ export function setShowInlineWarningsAndErrors(value: boolean): void {
);
}

export function getDefaultOpenInEditorURL(): string {
return typeof process.env.EDITOR_URL === 'string'
? process.env.EDITOR_URL
: '';
}

export function getOpenInEditorURL(): string {
try {
const raw = localStorageGetItem(LOCAL_STORAGE_OPEN_IN_EDITOR_URL);
if (raw != null) {
return JSON.parse(raw);
}
} catch (error) {}
return getDefaultOpenInEditorURL();
}

export function separateDisplayNameAndHOCs(
displayName: string | null,
type: ElementType,
Expand Down
3 changes: 3 additions & 0 deletions packages/react-devtools-shell/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ if (!TARGET) {
process.exit(1);
}

const EDITOR_URL = process.env.EDITOR_URL || null;

const builtModulesDir = resolve(
__dirname,
'..',
Expand Down Expand Up @@ -69,6 +71,7 @@ const config = {
__PROFILE__: false,
__TEST__: NODE_ENV === 'test',
'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-shell"`,
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
'process.env.DARK_MODE_DIMMED_WARNING_COLOR': `"${DARK_MODE_DIMMED_WARNING_COLOR}"`,
Expand Down