Skip to content

Commit b61aae3

Browse files
authored
Add new 'open externally' button (#134)
1 parent ff343d8 commit b61aae3

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

front_end/global_typings/react_native.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ declare global {
99
namespace globalThis {
1010
var enableReactNativePerfMetrics: boolean|undefined;
1111
var enableReactNativePerfMetricsGlobalPostMessage: boolean|undefined;
12+
var enableReactNativeOpenInExternalEditor: boolean|undefined;
13+
var reactNativeOpenInEditorButtonImage: string|undefined;
1214
var FB_ONLY__reactNativeFeedbackLink: string|undefined;
1315
}
1416
}

front_end/panels/sources/UISourceCodeFrame.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import * as Persistence from '../../models/persistence/persistence.js';
3636
import * as TextUtils from '../../models/text_utils/text_utils.js';
3737
import * as Workspace from '../../models/workspace/workspace.js';
3838
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
39+
import * as Adorners from '../../ui/components/adorners/adorners.js';
3940
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
4041
import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
4142
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
@@ -78,6 +79,7 @@ export class UISourceCodeFrame extends
7879
// recreated when the binding changes
7980
private plugins: Plugin[] = [];
8081
private readonly errorPopoverHelper: UI.PopoverHelper.PopoverHelper;
82+
private openInExternalEditorToolbarButton?: UI.Toolbar.ToolbarButton;
8183
#sourcesPanelOpenedMetricsRecorded = false;
8284

8385
constructor(uiSourceCode: Workspace.UISourceCode.UISourceCode) {
@@ -93,6 +95,37 @@ export class UISourceCodeFrame extends
9395
this.uiSourceCodeEventListeners = [];
9496
this.messageAndDecorationListeners = [];
9597

98+
if (this.canOpenInExternalEditor()) {
99+
this.openInExternalEditorToolbarButton =
100+
new UI.Toolbar.ToolbarButton('Open in editor', undefined, 'Open in editor');
101+
const maybeBackgroundImage = globalThis.reactNativeOpenInEditorButtonImage;
102+
if (typeof maybeBackgroundImage === 'string' && maybeBackgroundImage !== '') {
103+
const adorner = new Adorners.Adorner.Adorner();
104+
adorner.classList.add('open-in-external-editor-adorner');
105+
adorner.style.setProperty('background-image', maybeBackgroundImage);
106+
this.openInExternalEditorToolbarButton.element.classList.add(
107+
'toolbar-has-glyph', 'open-in-external-editor-button');
108+
this.openInExternalEditorToolbarButton.setGlyphOrAdorner(adorner);
109+
} else {
110+
this.openInExternalEditorToolbarButton.setGlyph('open-externally');
111+
}
112+
this.openInExternalEditorToolbarButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => {
113+
const body: {url: string, lineNumber?: number} = {
114+
url: this.uiSourceCode().url(),
115+
};
116+
117+
const state = this.textEditor.state;
118+
const line = state.doc.lineAt(state.selection.main.head);
119+
const {lineNumber} = this.editorLocationToUILocation(line.number);
120+
body.lineNumber = lineNumber;
121+
122+
fetch('/open-stack-frame', {
123+
method: 'POST',
124+
body: JSON.stringify(body),
125+
}).catch(e => console.error(e));
126+
});
127+
}
128+
96129
this.boundOnBindingChanged = this.onBindingChanged.bind(this);
97130

98131
Common.Settings.Settings.instance()
@@ -108,6 +141,14 @@ export class UISourceCodeFrame extends
108141
this.initializeUISourceCode();
109142
}
110143

144+
private canOpenInExternalEditor(): boolean {
145+
if (!globalThis.enableReactNativeOpenInExternalEditor) {
146+
return false;
147+
}
148+
149+
return this.uiSourceCode().url().startsWith('http') ?? false;
150+
}
151+
111152
private async workingCopy(): Promise<TextUtils.ContentProvider.DeferredContent> {
112153
if (this.uiSourceCodeInternal.isDirty()) {
113154
return {content: this.uiSourceCodeInternal.workingCopy(), isEncoded: false};
@@ -456,6 +497,10 @@ export class UISourceCodeFrame extends
456497
return leftToolbarItems;
457498
}
458499

500+
if (this.openInExternalEditorToolbarButton) {
501+
leftToolbarItems.push(this.openInExternalEditorToolbarButton);
502+
}
503+
459504
return [...leftToolbarItems, new UI.Toolbar.ToolbarSeparator(true), ...rightToolbarItems];
460505
}
461506

front_end/ui/legacy/toolbar.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,22 @@ devtools-icon.leading-issue-icon {
600600
.fusebox-connection-status .toolbar-glyph {
601601
color: white !important;
602602
}
603+
604+
.toolbar-item.open-in-external-editor-button {
605+
background-color: transparent;
606+
border: none;
607+
}
608+
609+
.toolbar-item.open-in-external-editor-button:hover {
610+
background-color: var(--sys-color-state-hover-on-subtle);
611+
}
612+
613+
.open-in-external-editor-adorner {
614+
background-repeat: no-repeat;
615+
background-origin: content-box;
616+
background-size: contain;
617+
padding: 4px;
618+
width: 28px;
619+
height: 28px;
620+
margin-right: -4px;
621+
}

0 commit comments

Comments
 (0)