chrome-devtools-frontend
Version:
Chrome DevTools UI
172 lines (155 loc) • 6.1 kB
text/typescript
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as i18n from '../../core/i18n/i18n.js';
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
import * as QuickOpen from '../../ui/legacy/components/quick_open/quick_open.js';
import * as UI from '../../ui/legacy/legacy.js';
import {SourcesView} from './SourcesView.js';
import type {UISourceCodeFrame} from './UISourceCodeFrame.js';
const UIStrings = {
/**
*@description Text in Go To Line Quick Open of the Sources panel
*/
noFileSelected: 'No file selected',
/**
*@description Text to show no results have been found
*/
noResultsFound: 'No results found',
/**
*@description Text in Go To Line Quick Open of the Sources panel
*/
typeANumberToGoToThatLine: 'Type a number to go to that line',
/**
*@description Text in Go To Line Quick Open of the Sources panel
*@example {000} PH1
*@example {bbb} PH2
*/
currentPositionXsTypeAnOffset: 'Type an offset between 0x{PH1} and 0x{PH2} to navigate to',
/**
*@description Text in the GoToLine dialog of the Sources pane that describes the current line number, file line number range, and use of the GoToLine dialog
*@example {100} PH1
*/
currentLineSTypeALineNumber: 'Type a line number between 1 and {PH1} to navigate to',
/**
*@description Text in Go To Line Quick Open of the Sources panel
*@example {abc} PH1
*/
goToOffsetXs: 'Go to offset 0x{PH1}',
/**
*@description Text in Go To Line Quick Open of the Sources panel
*@example {2} PH1
*@example {2} PH2
*/
goToLineSAndColumnS: 'Go to line {PH1} and column {PH2}',
/**
*@description Text in Go To Line Quick Open of the Sources panel
*@example {2} PH1
*/
goToLineS: 'Go to line {PH1}',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/sources/GoToLineQuickOpen.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class GoToLineQuickOpen extends QuickOpen.FilteredListWidget.Provider {
#goToLineStrings: string[] = [];
constructor() {
super('source-line');
}
override selectItem(_itemIndex: number|null, promptValue: string): void {
const sourceFrame = this.currentSourceFrame();
if (!sourceFrame) {
return;
}
const position = this.parsePosition(promptValue);
if (!position) {
return;
}
sourceFrame.revealPosition({lineNumber: position.line - 1, columnNumber: position.column - 1});
}
override itemCount(): number {
return this.#goToLineStrings.length;
}
override renderItem(itemIndex: number, _query: string, titleElement: Element, _subtitleElement: Element): void {
const icon = IconButton.Icon.create('colon');
titleElement.parentElement?.parentElement?.insertBefore(icon, titleElement.parentElement);
UI.UIUtils.createTextChild(titleElement, this.#goToLineStrings[itemIndex]);
}
override rewriteQuery(_query: string): string {
// For Go to Line Quick Open, we don't need to filter any item, set query to empty string, so the filter regex matching will be skipped
return '';
}
override queryChanged(query: string): void {
this.#goToLineStrings = [];
const position = this.parsePosition(query);
const sourceFrame = this.currentSourceFrame();
if (!position) {
if (!sourceFrame) {
this.#goToLineStrings.push(i18nString(UIStrings.typeANumberToGoToThatLine));
return;
}
const editorState = sourceFrame.textEditor.state;
const disassembly = sourceFrame.wasmDisassembly;
if (disassembly) {
const lastBytecodeOffset = disassembly.lineNumberToBytecodeOffset(disassembly.lineNumbers - 1);
const bytecodeOffsetDigits = lastBytecodeOffset.toString(16).length;
this.#goToLineStrings.push(i18nString(UIStrings.currentPositionXsTypeAnOffset, {
PH1: '0'.padStart(bytecodeOffsetDigits, '0'),
PH2: lastBytecodeOffset.toString(16),
}));
return;
}
const linesCount = sourceFrame.editorLocationToUILocation(editorState.doc.lines - 1).lineNumber + 1;
this.#goToLineStrings.push(i18nString(UIStrings.currentLineSTypeALineNumber, {PH1: linesCount}));
return;
}
if (sourceFrame?.wasmDisassembly) {
this.#goToLineStrings.push(i18nString(UIStrings.goToOffsetXs, {PH1: (position.column - 1).toString(16)}));
return;
}
if (position.column && position.column > 1) {
this.#goToLineStrings.push(i18nString(UIStrings.goToLineSAndColumnS, {PH1: position.line, PH2: position.column}));
return;
}
if (sourceFrame && position.line > sourceFrame.textEditor.state.doc.lines) {
return;
}
this.#goToLineStrings.push(i18nString(UIStrings.goToLineS, {PH1: position.line}));
}
override notFoundText(_query: string): string {
if (!this.currentSourceFrame()) {
return i18nString(UIStrings.noFileSelected);
}
return i18nString(UIStrings.noResultsFound);
}
private parsePosition(query: string): {
line: number,
column: number,
}|null {
const sourceFrame = this.currentSourceFrame();
if (sourceFrame?.wasmDisassembly) {
const parts = query.match(/0x([0-9a-fA-F]+)/);
if (!parts?.[0] || parts[0].length !== query.length) {
return null;
}
const column = parseInt(parts[0], 16) + 1;
return {line: 0, column};
}
const parts = query.match(/([0-9]+)(\:[0-9]*)?/);
if (!parts?.[0] || parts[0].length !== query.length) {
return null;
}
const line = parseInt(parts[1], 10);
let column = 0;
if (parts[2]) {
column = parseInt(parts[2].substring(1), 10);
}
return {line: Math.max(line | 0, 1), column: Math.max(column | 0, 1)};
}
private currentSourceFrame(): UISourceCodeFrame|null {
const sourcesView = UI.Context.Context.instance().flavor(SourcesView);
if (!sourcesView) {
return null;
}
return sourcesView.currentSourceFrame();
}
}