mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-11-06 13:19:17 +08:00
[BreakpointsSidebarPane] Add listeners for interacting with sidebar
This adds listeners to the BreakpointsSidebarPane for jump to source and enabling/disabling the checkbox, such that interacting with the sidebar actually propagates the action to the SourcesPanel (jump)/ BreakpointManager (enable/disable). This is still behind an experiment. Screenshot: https://imgur.com/a/CT0JqLA Doc: https://bit.ly/3pa4y92 Bug: 1324904, 1346231 Change-Id: Ic620d1c24cef465c81ffd77fb06a425752971555 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3899252 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Commit-Queue: Kim-Anh Tran <kimanh@chromium.org>
This commit is contained in:
committed by
Devtools-frontend LUCI CQ
parent
9a6f08ad08
commit
fd2ae9a89d
@@ -2,7 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import * as Common from '../../core/common/common.js';
|
||||
import * as Platform from '../../core/platform/platform.js';
|
||||
import {assertNotNullOrUndefined} from '../../core/platform/platform.js';
|
||||
import * as SDK from '../../core/sdk/sdk.js';
|
||||
import * as Bindings from '../../models/bindings/bindings.js';
|
||||
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
||||
@@ -16,6 +18,7 @@ let breakpointsViewControllerInstance: BreakpointsSidebarController;
|
||||
|
||||
export class BreakpointsSidebarPane extends UI.ThrottledWidget.ThrottledWidget {
|
||||
readonly #breakpointsView: SourcesComponents.BreakpointsView.BreakpointsView;
|
||||
readonly #controller: BreakpointsSidebarController;
|
||||
|
||||
static instance(): BreakpointsSidebarPane {
|
||||
if (!breakpointsSidebarPaneInstance) {
|
||||
@@ -26,22 +29,46 @@ export class BreakpointsSidebarPane extends UI.ThrottledWidget.ThrottledWidget {
|
||||
|
||||
constructor() {
|
||||
super(true);
|
||||
|
||||
this.#controller = BreakpointsSidebarController.instance();
|
||||
this.#breakpointsView = new SourcesComponents.BreakpointsView.BreakpointsView();
|
||||
this.#breakpointsView.addEventListener(
|
||||
SourcesComponents.BreakpointsView.CheckboxToggledEvent.eventName, (event: Event) => {
|
||||
this.#onCheckBoxToggledEvent(event);
|
||||
});
|
||||
this.#breakpointsView.addEventListener(
|
||||
SourcesComponents.BreakpointsView.BreakpointSelectedEvent.eventName, (event: Event) => {
|
||||
this.#onBreakpointSelectedEvent(event);
|
||||
});
|
||||
this.contentElement.appendChild(this.#breakpointsView);
|
||||
this.update();
|
||||
}
|
||||
|
||||
async doUpdate(): Promise<void> {
|
||||
await BreakpointsSidebarController.instance().update();
|
||||
await this.#controller.update();
|
||||
}
|
||||
|
||||
set data(data: SourcesComponents.BreakpointsView.BreakpointsViewData) {
|
||||
this.#breakpointsView.data = data;
|
||||
}
|
||||
|
||||
#onCheckBoxToggledEvent(event: Event): void {
|
||||
const checkboxToggledEvent = event as SourcesComponents.BreakpointsView.CheckboxToggledEvent;
|
||||
const {breakpointItem, checked} = checkboxToggledEvent.data;
|
||||
|
||||
this.#controller.breakpointStateChanged(breakpointItem, checked);
|
||||
}
|
||||
|
||||
#onBreakpointSelectedEvent(event: Event): void {
|
||||
const breakpointSelectedEvent = event as SourcesComponents.BreakpointsView.BreakpointSelectedEvent;
|
||||
const breakpointItem = breakpointSelectedEvent.data.breakpointItem;
|
||||
|
||||
void this.#controller.jumpToSource(breakpointItem);
|
||||
}
|
||||
}
|
||||
export class BreakpointsSidebarController implements UI.ContextFlavorListener.ContextFlavorListener {
|
||||
readonly #breakpointManager: Bindings.BreakpointManager.BreakpointManager;
|
||||
readonly #breakpointItemToLocationMap =
|
||||
new WeakMap<SourcesComponents.BreakpointsView.BreakpointItem, Bindings.BreakpointManager.BreakpointLocation[]>();
|
||||
|
||||
constructor() {
|
||||
this.#breakpointManager = Bindings.BreakpointManager.BreakpointManager.instance();
|
||||
@@ -60,6 +87,27 @@ export class BreakpointsSidebarController implements UI.ContextFlavorListener.Co
|
||||
void this.update();
|
||||
}
|
||||
|
||||
breakpointStateChanged(breakpointItem: SourcesComponents.BreakpointsView.BreakpointItem, checked: boolean): void {
|
||||
const locations = this.#getLocationsForBreakpointItem(breakpointItem);
|
||||
locations.forEach((value: Bindings.BreakpointManager.BreakpointLocation) => {
|
||||
const breakpoint = value.breakpoint;
|
||||
breakpoint.setEnabled(checked);
|
||||
});
|
||||
}
|
||||
|
||||
async jumpToSource(breakpointItem: SourcesComponents.BreakpointsView.BreakpointItem): Promise<void> {
|
||||
const uiLocations = this.#getLocationsForBreakpointItem(breakpointItem).map(location => location.uiLocation);
|
||||
let uiLocation: Workspace.UISourceCode.UILocation|undefined;
|
||||
for (const uiLocationCandidate of uiLocations) {
|
||||
if (!uiLocation || uiLocationCandidate.compareTo(uiLocation) < 0) {
|
||||
uiLocation = uiLocationCandidate;
|
||||
}
|
||||
}
|
||||
if (uiLocation) {
|
||||
await Common.Revealer.reveal(uiLocation);
|
||||
}
|
||||
}
|
||||
|
||||
async update(): Promise<void> {
|
||||
const data = await this.getUpdatedBreakpointViewData();
|
||||
BreakpointsSidebarPane.instance().data = data;
|
||||
@@ -98,6 +146,7 @@ export class BreakpointsSidebarController implements UI.ContextFlavorListener.Co
|
||||
const status: SourcesComponents.BreakpointsView.BreakpointStatus = this.#getBreakpointState(locations);
|
||||
const item = {location: locationText, codeSnippet, isHit, status} as
|
||||
SourcesComponents.BreakpointsView.BreakpointItem;
|
||||
this.#breakpointItemToLocationMap.set(item, locations);
|
||||
|
||||
let group = urlToGroup.get(sourceURL);
|
||||
if (group) {
|
||||
@@ -112,6 +161,13 @@ export class BreakpointsSidebarController implements UI.ContextFlavorListener.Co
|
||||
return {groups: Array.from(urlToGroup.values())};
|
||||
}
|
||||
|
||||
#getLocationsForBreakpointItem(breakpointItem: SourcesComponents.BreakpointsView.BreakpointItem):
|
||||
Bindings.BreakpointManager.BreakpointLocation[] {
|
||||
const locations = this.#breakpointItemToLocationMap.get(breakpointItem);
|
||||
assertNotNullOrUndefined(locations);
|
||||
return locations;
|
||||
}
|
||||
|
||||
async #getHitUILocation(): Promise<Workspace.UISourceCode.UILocation|null> {
|
||||
const details = UI.Context.Context.instance().flavor(SDK.DebuggerModel.DebuggerPausedDetails);
|
||||
if (details && details.callFrames.length) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import * as Common from '../../../../../front_end/core/common/common.js';
|
||||
import * as Bindings from '../../../../../front_end/models/bindings/bindings.js';
|
||||
import * as Workspace from '../../../../../front_end/models/workspace/workspace.js';
|
||||
import * as SourcesComponents from '../../../../../front_end/panels/sources/components/components.js';
|
||||
@@ -48,6 +49,56 @@ function createBreakpointLocations(testData: LocationTestData[]): Bindings.Break
|
||||
}
|
||||
|
||||
describeWithEnvironment('BreakpointsSidebarController', () => {
|
||||
it('changes breakpoint state', async () => {
|
||||
const testData = [
|
||||
createLocationTestData(HELLO_JS_FILE, 3, 10),
|
||||
];
|
||||
initializeBreakpointManagerWithMockdata(testData);
|
||||
|
||||
const controller = Sources.BreakpointsSidebarPane.BreakpointsSidebarController.instance({forceNew: true});
|
||||
const data = await controller.getUpdatedBreakpointViewData();
|
||||
assert.lengthOf(data.groups, 1);
|
||||
assert.lengthOf(data.groups[0].breakpointItems, 1);
|
||||
const breakpointItem = data.groups[0].breakpointItems[0];
|
||||
assert.strictEqual(breakpointItem.status, SourcesComponents.BreakpointsView.BreakpointStatus.ENABLED);
|
||||
|
||||
const locations = Bindings.BreakpointManager.BreakpointManager.instance().allBreakpointLocations();
|
||||
assert.lengthOf(locations, 1);
|
||||
|
||||
const breakpoint = locations[0].breakpoint as sinon.SinonStubbedInstance<Bindings.BreakpointManager.Breakpoint>;
|
||||
controller.breakpointStateChanged(breakpointItem, false);
|
||||
assert.isTrue(breakpoint.setEnabled.calledWith(false));
|
||||
});
|
||||
|
||||
it('triggers a jump to source', async () => {
|
||||
const testData = [
|
||||
createLocationTestData(HELLO_JS_FILE, 3, 10),
|
||||
];
|
||||
initializeBreakpointManagerWithMockdata(testData);
|
||||
|
||||
const controller = Sources.BreakpointsSidebarPane.BreakpointsSidebarController.instance({forceNew: true});
|
||||
const data = await controller.getUpdatedBreakpointViewData();
|
||||
assert.lengthOf(data.groups, 1);
|
||||
assert.lengthOf(data.groups[0].breakpointItems, 1);
|
||||
const breakpointItem = data.groups[0].breakpointItems[0];
|
||||
|
||||
Common.Revealer.registerRevealer({
|
||||
contextTypes() {
|
||||
return [
|
||||
Workspace.UISourceCode.UILocation,
|
||||
];
|
||||
},
|
||||
destination: Common.Revealer.RevealerDestination.SOURCES_PANEL,
|
||||
async loadRevealer() {
|
||||
return Sources.SourcesPanel.UILocationRevealer.instance();
|
||||
},
|
||||
});
|
||||
|
||||
const revealStub = sinon.stub(Sources.SourcesPanel.UILocationRevealer.instance(), 'reveal');
|
||||
await controller.jumpToSource(breakpointItem);
|
||||
assert.isTrue(revealStub.called);
|
||||
});
|
||||
|
||||
describe('getUpdatedBreakpointViewData', () => {
|
||||
it('extracts breakpoint data', async () => {
|
||||
const testData = [
|
||||
|
||||
Reference in New Issue
Block a user