chrome-devtools-frontend 1.0.1515988 → 1.0.1518653

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/docs/checklist/README.md +2 -2
  2. package/docs/checklist/javascript.md +1 -1
  3. package/docs/contributing/README.md +1 -1
  4. package/docs/contributing/settings-experiments-features.md +9 -8
  5. package/docs/cookbook/devtools_on_devtools.md +2 -2
  6. package/docs/cookbook/localization.md +10 -10
  7. package/docs/devtools-protocol.md +9 -8
  8. package/docs/ecosystem/automatic_workspace_folders.md +3 -3
  9. package/docs/get_the_code.md +0 -2
  10. package/docs/styleguide/ux/components.md +166 -85
  11. package/docs/styleguide/ux/numbers.md +3 -4
  12. package/front_end/core/common/README.md +13 -12
  13. package/front_end/core/host/GdpClient.ts +16 -1
  14. package/front_end/core/host/UserMetrics.ts +8 -2
  15. package/front_end/core/root/Runtime.ts +13 -0
  16. package/front_end/core/sdk/CSSMatchedStyles.ts +5 -1
  17. package/front_end/entrypoints/main/MainImpl.ts +6 -3
  18. package/front_end/generated/InspectorBackendCommands.js +10 -7
  19. package/front_end/generated/SupportedCSSProperties.js +21 -7
  20. package/front_end/generated/protocol-mapping.d.ts +16 -1
  21. package/front_end/generated/protocol-proxy-api.d.ts +13 -1
  22. package/front_end/generated/protocol.ts +95 -0
  23. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +170 -54
  24. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +14 -181
  25. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +13 -315
  26. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +224 -50
  27. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +310 -11
  28. package/front_end/models/ai_assistance/performance/AIContext.ts +15 -2
  29. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +41 -19
  30. package/front_end/models/badges/Badge.ts +8 -3
  31. package/front_end/models/badges/CodeWhispererBadge.ts +2 -4
  32. package/front_end/models/badges/StarterBadge.ts +2 -2
  33. package/front_end/models/badges/UserBadges.ts +59 -6
  34. package/front_end/models/formatter/FormatterWorkerPool.ts +3 -3
  35. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  36. package/front_end/models/trace/README.md +28 -1
  37. package/front_end/models/trace/handlers/UserTimingsHandler.ts +1 -1
  38. package/front_end/models/trace/helpers/Trace.ts +99 -43
  39. package/front_end/models/trace/types/TraceEvents.ts +9 -0
  40. package/front_end/panels/accessibility/ARIAAttributesView.ts +113 -191
  41. package/front_end/panels/accessibility/AccessibilityNodeView.ts +9 -9
  42. package/front_end/panels/accessibility/AccessibilitySubPane.ts +6 -4
  43. package/front_end/panels/accessibility/accessibilityProperties.css +2 -0
  44. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +16 -2
  45. package/front_end/panels/ai_assistance/components/ChatView.ts +9 -10
  46. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +42 -0
  47. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +32 -9
  48. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +7 -1
  49. package/front_end/panels/common/BadgeNotification.ts +67 -15
  50. package/front_end/panels/common/GdpSignUpDialog.ts +18 -9
  51. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  52. package/front_end/panels/console/ConsoleView.ts +6 -2
  53. package/front_end/panels/elements/ComputedStyleWidget.ts +1 -2
  54. package/front_end/panels/elements/ElementsPanel.ts +4 -0
  55. package/front_end/panels/elements/ElementsTreeElement.ts +18 -0
  56. package/front_end/panels/elements/ElementsTreeOutline.ts +13 -0
  57. package/front_end/panels/elements/LayoutPane.ts +1 -1
  58. package/front_end/panels/elements/StylePropertyTreeElement.ts +21 -6
  59. package/front_end/panels/media/TickingFlameChart.ts +1 -1
  60. package/front_end/panels/network/NetworkLogView.ts +5 -1
  61. package/front_end/panels/profiler/HeapSnapshotView.ts +34 -19
  62. package/front_end/panels/search/SearchResultsPane.ts +126 -145
  63. package/front_end/panels/search/SearchView.ts +43 -59
  64. package/front_end/panels/settings/components/SyncSection.ts +16 -8
  65. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +6 -1
  66. package/front_end/panels/sources/OutlineQuickOpen.ts +3 -1
  67. package/front_end/panels/sources/SourcesPanel.ts +3 -0
  68. package/front_end/panels/timeline/AppenderUtils.ts +2 -2
  69. package/front_end/panels/timeline/ExtensionTrackAppender.ts +13 -4
  70. package/front_end/panels/timeline/GPUTrackAppender.ts +2 -1
  71. package/front_end/panels/timeline/InteractionsTrackAppender.ts +5 -1
  72. package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +2 -1
  73. package/front_end/panels/timeline/ThreadAppender.ts +12 -3
  74. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +9 -4
  75. package/front_end/panels/timeline/TimelinePanel.ts +3 -2
  76. package/front_end/panels/timeline/TimelineUIUtils.ts +18 -12
  77. package/front_end/panels/timeline/TimingsTrackAppender.ts +6 -1
  78. package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +95 -82
  79. package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
  80. package/front_end/panels/timeline/components/cpuThrottlingSelector.css +17 -15
  81. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +3 -0
  82. package/front_end/third_party/chromium/README.chromium +1 -1
  83. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  84. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  85. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +6 -9
  86. package/front_end/third_party/codemirror.next/package.json +2 -1
  87. package/front_end/third_party/diff/README.chromium +1 -0
  88. package/front_end/third_party/puppeteer/README.chromium +2 -2
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js +0 -20
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  99. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +2 -23
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js +0 -20
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  107. package/front_end/third_party/puppeteer/package/package.json +1 -1
  108. package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +1 -21
  109. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  110. package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
  111. package/front_end/ui/components/text_editor/config.ts +36 -8
  112. package/front_end/ui/components/tooltips/Tooltip.ts +71 -34
  113. package/front_end/ui/legacy/README.md +33 -24
  114. package/front_end/ui/legacy/SearchableView.ts +19 -26
  115. package/front_end/ui/legacy/TextPrompt.ts +166 -1
  116. package/front_end/ui/legacy/Treeoutline.ts +16 -2
  117. package/front_end/ui/legacy/UIUtils.ts +15 -2
  118. package/front_end/ui/legacy/XElement.ts +0 -43
  119. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +20 -4
  120. package/front_end/ui/visual_logging/KnownContextValues.ts +24 -6
  121. package/front_end/ui/visual_logging/README.md +43 -27
  122. package/package.json +1 -1
@@ -303,7 +303,17 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
303
303
  baseProfile!: HeapProfileHeader|null;
304
304
  trackingOverviewGrid?: HeapTimelineOverview;
305
305
  currentSearchResultIndex = -1;
306
- currentQuery?: HeapSnapshotModel.HeapSnapshotModel.SearchConfig;
306
+ currentSearch?: HeapSnapshotModel.HeapSnapshotModel.SearchConfig;
307
+
308
+ get currentQuery(): string|undefined {
309
+ return this.currentSearch?.query;
310
+ }
311
+ set currentQuery(value: string) {
312
+ if (this.currentSearch) {
313
+ this.currentSearch.query = value;
314
+ }
315
+ }
316
+
307
317
  constructor(dataDisplayDelegate: DataDisplayDelegate, profile: HeapProfileHeader) {
308
318
  super({
309
319
  title: i18nString(UIStrings.heapSnapshot),
@@ -643,8 +653,13 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
643
653
 
644
654
  performSearch(searchConfig: UI.SearchableView.SearchConfig, shouldJump: boolean, jumpBackwards?: boolean): void {
645
655
  const nextQuery = new HeapSnapshotModel.HeapSnapshotModel.SearchConfig(
646
- searchConfig.query.trim(), searchConfig.caseSensitive, searchConfig.wholeWord, searchConfig.isRegex, shouldJump,
647
- jumpBackwards || false);
656
+ searchConfig.query.trim(),
657
+ searchConfig.caseSensitive,
658
+ searchConfig.wholeWord,
659
+ searchConfig.isRegex,
660
+ shouldJump,
661
+ jumpBackwards || false,
662
+ );
648
663
 
649
664
  void this.searchThrottler.schedule(this.performSearchInternal.bind(this, nextQuery));
650
665
  }
@@ -657,7 +672,7 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
657
672
  return;
658
673
  }
659
674
 
660
- this.currentQuery = nextQuery;
675
+ this.currentSearch = nextQuery;
661
676
  const query = nextQuery.query.trim();
662
677
 
663
678
  if (!query) {
@@ -682,7 +697,7 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
682
697
  }
683
698
 
684
699
  const filter = this.dataGrid.nodeFilter();
685
- this.searchResults = filter ? await this.profile.snapshotProxy.search(this.currentQuery, filter) : [];
700
+ this.searchResults = filter ? await this.profile.snapshotProxy.search(this.currentSearch, filter) : [];
686
701
 
687
702
  this.searchableViewInternal.updateSearchMatchesCount(this.searchResults.length);
688
703
  if (this.searchResults.length) {
@@ -724,10 +739,10 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
724
739
  if (!this.dataGrid) {
725
740
  return;
726
741
  }
727
- let child: (HeapSnapshotGridNode|null) = (this.dataGrid.rootNode().children[0] as HeapSnapshotGridNode | null);
742
+ let child: DataGrid.DataGrid.DataGridNode<HeapSnapshotGridNode>|null = this.dataGrid.rootNode().children[0];
728
743
  while (child) {
729
744
  child.refresh();
730
- child = (child.traverseNextNode(false, null, true) as HeapSnapshotGridNode | null);
745
+ child = child.traverseNextNode(false, null, true);
731
746
  }
732
747
  }
733
748
 
@@ -735,28 +750,28 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
735
750
  if (this.baseProfile === this.profiles()[this.baseSelect.selectedIndex()]) {
736
751
  return;
737
752
  }
738
- this.baseProfile = (this.profiles()[this.baseSelect.selectedIndex()] as HeapProfileHeader);
753
+ this.baseProfile = this.profiles()[this.baseSelect.selectedIndex()];
739
754
  const dataGrid = (this.dataGrid as HeapSnapshotDiffDataGrid);
740
755
  // Change set base data source only if main data source is already set.
741
756
  if (dataGrid.snapshot) {
742
757
  void this.baseProfile.loadPromise.then(dataGrid.setBaseDataSource.bind(dataGrid));
743
758
  }
744
759
 
745
- if (!this.currentQuery || !this.searchResults) {
760
+ if (!this.currentSearch || !this.searchResults) {
746
761
  return;
747
762
  }
748
763
 
749
764
  // The current search needs to be performed again. First negate out previous match
750
765
  // count by calling the search finished callback with a negative number of matches.
751
766
  // Then perform the search again with the same query and callback.
752
- this.performSearch(this.currentQuery, false);
767
+ this.performSearch(this.currentSearch, false);
753
768
  }
754
769
 
755
- static readonly ALWAYS_AVAILABLE_FILTERS = [
770
+ static readonly ALWAYS_AVAILABLE_FILTERS: ReadonlyArray<{uiName: string, filterName: string}> = [
756
771
  {uiName: i18nString(UIStrings.duplicatedStrings), filterName: 'duplicatedStrings'},
757
772
  {uiName: i18nString(UIStrings.objectsRetainedByDetachedDomNodes), filterName: 'objectsRetainedByDetachedDomNodes'},
758
773
  {uiName: i18nString(UIStrings.objectsRetainedByConsole), filterName: 'objectsRetainedByConsole'},
759
- ] as ReadonlyArray<{uiName: string, filterName: string}>;
774
+ ];
760
775
 
761
776
  changeFilter(): void {
762
777
  let selectedIndex = this.filterSelect.selectedIndex();
@@ -773,19 +788,19 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
773
788
  return;
774
789
  }
775
790
  (this.dataGrid as HeapSnapshotConstructorsDataGrid)
776
- .filterSelectIndexChanged((this.profiles() as HeapProfileHeader[]), profileIndex, filterName);
791
+ .filterSelectIndexChanged(this.profiles(), profileIndex, filterName);
777
792
 
778
- if (!this.currentQuery || !this.searchResults) {
793
+ if (!this.currentSearch || !this.searchResults) {
779
794
  return;
780
795
  }
781
796
 
782
797
  // The current search needs to be performed again. First negate out previous match
783
798
  // count by calling the search finished callback with a negative number of matches.
784
799
  // Then perform the search again with the same query and callback.
785
- this.performSearch(this.currentQuery, false);
800
+ this.performSearch(this.currentSearch, false);
786
801
  }
787
802
 
788
- profiles(): ProfileHeader[] {
803
+ profiles(): HeapProfileHeader[] {
789
804
  return this.profile.profileType().getProfiles();
790
805
  }
791
806
 
@@ -866,7 +881,7 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
866
881
  return;
867
882
  }
868
883
  if (!this.baseProfile) {
869
- this.baseProfile = (this.profiles()[this.baseSelect.selectedIndex()] as HeapProfileHeader);
884
+ this.baseProfile = this.profiles()[this.baseSelect.selectedIndex()];
870
885
  }
871
886
 
872
887
  const baseSnapshotProxy = await this.baseProfile.loadPromise;
@@ -900,14 +915,14 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
900
915
 
901
916
  void this.updateDataSourceAndView();
902
917
 
903
- if (!this.currentQuery || !this.searchResults) {
918
+ if (!this.currentSearch || !this.searchResults) {
904
919
  return;
905
920
  }
906
921
 
907
922
  // The current search needs to be performed again. First negate out previous match
908
923
  // count by calling the search finished callback with a negative number of matches.
909
924
  // Then perform the search again the with same query and callback.
910
- this.performSearch(this.currentQuery, false);
925
+ this.performSearch(this.currentSearch, false);
911
926
  }
912
927
 
913
928
  async selectLiveObject(perspectiveName: string, snapshotObjectId: string): Promise<void> {
@@ -2,14 +2,15 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
  /* eslint-disable rulesdir/no-imperative-dom-api */
5
+ /* eslint-disable rulesdir/no-lit-render-outside-of-view */
5
6
 
6
7
  import * as Common from '../../core/common/common.js';
7
8
  import * as i18n from '../../core/i18n/i18n.js';
8
9
  import * as Platform from '../../core/platform/platform.js';
9
10
  import * as TextUtils from '../../models/text_utils/text_utils.js';
10
11
  import type * as Workspace from '../../models/workspace/workspace.js';
11
- import * as Components from '../../ui/legacy/components/utils/utils.js';
12
12
  import * as UI from '../../ui/legacy/legacy.js';
13
+ import {html, render} from '../../ui/lit/lit.js';
13
14
 
14
15
  import searchResultsPaneStyles from './searchResultsPane.css.js';
15
16
  import type {SearchResult} from './SearchScope.js';
@@ -34,22 +35,95 @@ const UIStrings = {
34
35
  const str_ = i18n.i18n.registerUIStrings('panels/search/SearchResultsPane.ts', UIStrings);
35
36
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
36
37
 
38
+ interface SearchMatch {
39
+ lineContent: string;
40
+ matchRanges: TextUtils.TextRange.SourceRange[];
41
+ resultLabel: string|number;
42
+ }
43
+
37
44
  export class SearchResultsPane extends UI.Widget.VBox {
38
45
  private readonly searchConfig: Workspace.SearchConfig.SearchConfig;
39
- private readonly searchResults: SearchResult[];
40
- private readonly treeElements: SearchResultsTreeElement[];
46
+ private readonly searchResults = new Map<SearchResult, UI.TreeOutline.TreeElement>();
47
+ private expandedResults = new WeakSet<SearchResult>();
48
+ private readonly searchMatches = new WeakMap<SearchResult, SearchMatch[]>();
41
49
  private treeOutline: UI.TreeOutline.TreeOutlineInShadow;
42
- private matchesExpandedCount: number;
50
+ private matchesExpandedCount = 0;
51
+
52
+ private addTreeElement(searchResult: SearchResult): void {
53
+ const treeElement = new UI.TreeOutline.TreeElement('', true);
54
+ treeElement.toggleOnClick = true;
55
+ this.searchResults.set(searchResult, treeElement);
56
+ this.treeOutline.appendChild(treeElement);
57
+ if (!this.treeOutline.selectedTreeElement) {
58
+ treeElement.select(/* omitFocus */ true, /* selectedByUser */ true);
59
+ }
60
+ treeElement.listItemElement.classList.add('search-result');
61
+ // clang-format off
62
+ render(html`
63
+ <span class="search-result-file-name">${searchResult.label()}
64
+ <span class="search-result-dash">${'\u2014'}</span>
65
+ <span class="search-result-qualifier">${searchResult.description()}</span>
66
+ </span>
67
+ <span class="search-result-matches-count"
68
+ aria-label=${i18nString(UIStrings.matchesCountS, {PH1: searchResult.matchesCount()})}>
69
+ ${searchResult.matchesCount()}
70
+ </span>`,
71
+ treeElement.listItemElement);
72
+ // clang-format on
73
+ treeElement.tooltip = searchResult.description();
74
+ }
75
+
76
+ private appendSearchMatchElement(searchResult: SearchResult, match: SearchMatch, i: number): void {
77
+ const {lineContent, matchRanges, resultLabel} = match;
78
+ const element = this.searchResults.get(searchResult);
79
+ if (!element) {
80
+ return;
81
+ }
82
+ const searchMatchElement = new UI.TreeOutline.TreeElement();
83
+ element.appendChild(searchMatchElement);
84
+ // clang-format off
85
+ render(html`
86
+ <button class="devtools-link text-button link-style search-match-link"
87
+ jslog="Link; context: search-match; track: click" role="link" tabindex="0"
88
+ @click=${() => void Common.Revealer.reveal(searchResult.matchRevealable(i))}>
89
+ <span class="search-match-line-number"
90
+ aria-label=${typeof resultLabel === 'number' && !isNaN(resultLabel)
91
+ ? i18nString(UIStrings.lineS, {PH1: resultLabel}) : resultLabel}>
92
+ ${resultLabel}
93
+ </span>
94
+ <span class="search-match-content" aria-label="${lineContent} line">
95
+ ${lineContent}
96
+ </span>
97
+ </button>`,
98
+ searchMatchElement.listItemElement);
99
+ // clang-format on
100
+ const contentSpan = searchMatchElement.listItemElement.querySelector('.search-match-content') as HTMLElement;
101
+ UI.UIUtils.highlightRangesWithStyleClass(contentSpan, matchRanges, 'highlighted-search-result');
102
+ searchMatchElement.listItemElement.className = 'search-match';
103
+ searchMatchElement.listItemElement.addEventListener('keydown', event => {
104
+ if (event.key === 'Enter') {
105
+ event.consume(true);
106
+ void Common.Revealer.reveal(searchResult.matchRevealable(i));
107
+ }
108
+ });
109
+ searchMatchElement.tooltip = lineContent;
110
+ }
43
111
 
44
112
  constructor(searchConfig: Workspace.SearchConfig.SearchConfig) {
45
113
  super({useShadowDom: true});
46
114
  this.searchConfig = searchConfig;
47
-
48
- this.searchResults = [];
49
- this.treeElements = [];
50
115
  this.treeOutline = new UI.TreeOutline.TreeOutlineInShadow();
51
116
  this.treeOutline.registerRequiredCSS(searchResultsPaneStyles);
52
117
  this.treeOutline.setHideOverflow(true);
118
+ this.treeOutline.addEventListener(
119
+ UI.TreeOutline.Events.ElementExpanded,
120
+ (event: Common.EventTarget.EventTargetEvent<UI.TreeOutline.TreeElement>) => {
121
+ const searchResult = this.searchResults.entries().find(entry => entry[1] === event.data)?.[0];
122
+ if (!searchResult) {
123
+ return;
124
+ }
125
+ this.expandSearchResult(searchResult);
126
+ });
53
127
 
54
128
  this.contentElement.appendChild(this.treeOutline.element);
55
129
 
@@ -57,116 +131,43 @@ export class SearchResultsPane extends UI.Widget.VBox {
57
131
  }
58
132
 
59
133
  addSearchResult(searchResult: SearchResult): void {
60
- this.searchResults.push(searchResult);
61
134
  this.addTreeElement(searchResult);
62
- }
63
-
64
- showAllMatches(): void {
65
- this.treeElements.forEach(treeElement => {
66
- treeElement.expand();
67
- treeElement.showAllMatches();
68
- });
69
- }
70
-
71
- collapseAllResults(): void {
72
- this.treeElements.forEach(treeElement => {
73
- treeElement.collapse();
74
- });
75
- }
76
-
77
- private addTreeElement(searchResult: SearchResult): void {
78
- const treeElement = new SearchResultsTreeElement(this.searchConfig, searchResult);
79
- this.treeOutline.appendChild(treeElement);
80
- if (!this.treeOutline.selectedTreeElement) {
81
- treeElement.select(/* omitFocus */ true, /* selectedByUser */ true);
82
- }
83
135
  // Expand until at least a certain number of matches is expanded.
84
136
  if (this.matchesExpandedCount < matchesExpandedByDefault) {
85
- treeElement.expand();
137
+ this.searchResults.get(searchResult)?.expand();
86
138
  }
87
- this.matchesExpandedCount += searchResult.matchesCount();
88
- this.treeElements.push(treeElement);
89
- }
90
- }
91
-
92
- export const matchesExpandedByDefault = 200;
93
- export const matchesShownAtOnce = 20;
94
-
95
- export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
96
- private searchConfig: Workspace.SearchConfig.SearchConfig;
97
- private searchResult: SearchResult;
98
- private initialized: boolean;
99
- override toggleOnClick: boolean;
100
-
101
- constructor(searchConfig: Workspace.SearchConfig.SearchConfig, searchResult: SearchResult) {
102
- super('', true);
103
- this.searchConfig = searchConfig;
104
- this.searchResult = searchResult;
105
- this.initialized = false;
106
- this.toggleOnClick = true;
107
139
  }
108
140
 
109
- override onexpand(): void {
110
- if (this.initialized) {
111
- return;
141
+ showAllMatches(): void {
142
+ for (const searchResult of this.searchResults.keys()) {
143
+ const startMatchIndex = this.searchMatches.get(searchResult)?.length ?? 0;
144
+ this.appendSearchMatches(searchResult, startMatchIndex, searchResult.matchesCount());
112
145
  }
113
-
114
- this.updateMatchesUI();
115
- this.initialized = true;
116
146
  }
117
147
 
118
- showAllMatches(): void {
119
- this.removeChildren();
120
- this.appendSearchMatches(0, this.searchResult.matchesCount());
148
+ collapseAllResults(): void {
149
+ for (const treeElement of this.searchResults.values()) {
150
+ treeElement.collapse();
151
+ }
121
152
  }
122
153
 
123
- private updateMatchesUI(): void {
124
- this.removeChildren();
125
- const toIndex = Math.min(this.searchResult.matchesCount(), matchesShownAtOnce);
126
- if (toIndex < this.searchResult.matchesCount()) {
127
- this.appendSearchMatches(0, toIndex - 1);
128
- this.appendShowMoreMatchesElement(toIndex - 1);
154
+ private expandSearchResult(searchResult: SearchResult): void {
155
+ const toIndex = Math.min(searchResult.matchesCount(), matchesShownAtOnce);
156
+ if (toIndex < searchResult.matchesCount()) {
157
+ this.appendSearchMatches(searchResult, 0, toIndex - 1);
158
+ this.appendShowMoreMatchesElement(searchResult, toIndex - 1);
129
159
  } else {
130
- this.appendSearchMatches(0, toIndex);
160
+ this.appendSearchMatches(searchResult, 0, toIndex);
131
161
  }
162
+ this.matchesExpandedCount += toIndex;
163
+ this.requestUpdate();
132
164
  }
133
165
 
134
- override onattach(): void {
135
- this.updateSearchMatches();
166
+ private collapseSearchResult(searchResult: SearchResult): void {
167
+ this.searchResults.get(searchResult)?.collapse();
136
168
  }
137
169
 
138
- private updateSearchMatches(): void {
139
- this.listItemElement.classList.add('search-result');
140
-
141
- const fileNameSpan = span(this.searchResult.label(), 'search-result-file-name');
142
- fileNameSpan.appendChild(span('\u2014', 'search-result-dash'));
143
- fileNameSpan.appendChild(span(this.searchResult.description(), 'search-result-qualifier'));
144
-
145
- this.tooltip = this.searchResult.description();
146
- this.listItemElement.appendChild(fileNameSpan);
147
- const matchesCountSpan = document.createElement('span');
148
- matchesCountSpan.className = 'search-result-matches-count';
149
-
150
- matchesCountSpan.textContent = `${this.searchResult.matchesCount()}`;
151
- UI.ARIAUtils.setLabel(
152
- matchesCountSpan, i18nString(UIStrings.matchesCountS, {PH1: this.searchResult.matchesCount()}));
153
-
154
- this.listItemElement.appendChild(matchesCountSpan);
155
- if (this.expanded) {
156
- this.updateMatchesUI();
157
- }
158
-
159
- function span(text: string, className: string): Element {
160
- const span = document.createElement('span');
161
- span.className = className;
162
- span.textContent = text;
163
- return span;
164
- }
165
- }
166
-
167
- private appendSearchMatches(fromIndex: number, toIndex: number): void {
168
- const searchResult = this.searchResult;
169
-
170
+ private appendSearchMatches(searchResult: SearchResult, fromIndex: number, toIndex: number): void {
170
171
  const queries = this.searchConfig.queries();
171
172
  const regexes = [];
172
173
  for (let i = 0; i < queries.length; ++i) {
@@ -174,6 +175,12 @@ export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
174
175
  queries[i], !this.searchConfig.ignoreCase(), this.searchConfig.isRegex()));
175
176
  }
176
177
 
178
+ const searchMatches = this.searchMatches.get(searchResult) ?? [];
179
+ this.searchMatches.set(searchResult, searchMatches);
180
+ if (searchMatches.length > toIndex) {
181
+ return;
182
+ }
183
+
177
184
  for (let i = fromIndex; i < toIndex; ++i) {
178
185
  let lineContent = searchResult.matchLineContent(i);
179
186
  let matchRanges: TextUtils.TextRange.SourceRange[] = [];
@@ -195,55 +202,24 @@ export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
195
202
  ({lineSegment: lineContent, matchRanges} = lineSegmentForMultipleMatches(lineContent, matchRanges));
196
203
  }
197
204
 
198
- const anchor = Components.Linkifier.Linkifier.linkifyRevealable(
199
- searchResult.matchRevealable(i), '', undefined, undefined, undefined, 'search-match');
200
- anchor.classList.add('search-match-link');
201
- anchor.tabIndex = 0;
202
- const labelSpan = document.createElement('span');
203
- labelSpan.classList.add('search-match-line-number');
204
205
  const resultLabel = searchResult.matchLabel(i);
205
- labelSpan.textContent = resultLabel;
206
- if (typeof resultLabel === 'number' && !isNaN(resultLabel)) {
207
- UI.ARIAUtils.setLabel(labelSpan, i18nString(UIStrings.lineS, {PH1: resultLabel}));
208
- } else {
209
- UI.ARIAUtils.setLabel(labelSpan, resultLabel);
210
- }
211
- anchor.appendChild(labelSpan);
212
-
213
- const contentSpan = this.createContentSpan(lineContent, matchRanges);
214
- anchor.appendChild(contentSpan);
215
-
216
- const searchMatchElement = new UI.TreeOutline.TreeElement();
217
- this.appendChild(searchMatchElement);
218
- searchMatchElement.listItemElement.className = 'search-match';
219
- searchMatchElement.listItemElement.appendChild(anchor);
220
- searchMatchElement.listItemElement.addEventListener('keydown', event => {
221
- if (event.key === 'Enter') {
222
- event.consume(true);
223
- void Common.Revealer.reveal(searchResult.matchRevealable(i));
224
- }
225
- });
226
- searchMatchElement.tooltip = lineContent;
206
+ searchMatches.push({lineContent, matchRanges, resultLabel});
207
+ this.appendSearchMatchElement(searchResult, searchMatches[i], i);
227
208
  }
228
209
  }
229
210
 
230
- private appendShowMoreMatchesElement(startMatchIndex: number): void {
231
- const matchesLeftCount = this.searchResult.matchesCount() - startMatchIndex;
211
+ private appendShowMoreMatchesElement(searchResult: SearchResult, startMatchIndex: number): void {
212
+ const element = this.searchResults.get(searchResult);
213
+ if (!element) {
214
+ return;
215
+ }
216
+ const matchesLeftCount = searchResult.matchesCount() - startMatchIndex;
232
217
  const showMoreMatchesText = i18nString(UIStrings.showDMore, {PH1: matchesLeftCount});
233
218
  const showMoreMatchesTreeElement = new UI.TreeOutline.TreeElement(showMoreMatchesText);
234
- this.appendChild(showMoreMatchesTreeElement);
219
+ element.appendChild(showMoreMatchesTreeElement);
235
220
  showMoreMatchesTreeElement.listItemElement.classList.add('show-more-matches');
236
221
  showMoreMatchesTreeElement.onselect =
237
- this.showMoreMatchesElementSelected.bind(this, showMoreMatchesTreeElement, startMatchIndex);
238
- }
239
-
240
- private createContentSpan(lineContent: string, matchRanges: TextUtils.TextRange.SourceRange[]): Element {
241
- const contentSpan = document.createElement('span');
242
- contentSpan.className = 'search-match-content';
243
- contentSpan.textContent = lineContent;
244
- UI.ARIAUtils.setLabel(contentSpan, `${lineContent} line`);
245
- UI.UIUtils.highlightRangesWithStyleClass(contentSpan, matchRanges, 'highlighted-search-result');
246
- return contentSpan;
222
+ this.showMoreMatchesElementSelected.bind(this, searchResult, showMoreMatchesTreeElement, startMatchIndex);
247
223
  }
248
224
 
249
225
  private regexMatchRanges(lineContent: string, regex: RegExp): TextUtils.TextRange.SourceRange[] {
@@ -258,13 +234,18 @@ export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
258
234
  }
259
235
 
260
236
  private showMoreMatchesElementSelected(
261
- showMoreMatchesTreeElement: UI.TreeOutline.TreeElement, startMatchIndex: number): boolean {
262
- this.removeChild(showMoreMatchesTreeElement);
263
- this.appendSearchMatches(startMatchIndex, this.searchResult.matchesCount());
264
- return false;
237
+ searchResult: SearchResult, showMoreMatchesTreeElement: UI.TreeOutline.TreeElement,
238
+ startMatchIndex: number): boolean {
239
+ const parentElement = showMoreMatchesTreeElement.parent;
240
+ parentElement?.removeChild(showMoreMatchesTreeElement);
241
+ this.appendSearchMatches(searchResult, startMatchIndex, searchResult.matchesCount());
242
+ return true;
265
243
  }
266
244
  }
267
245
 
246
+ export const matchesExpandedByDefault = 200;
247
+ export const matchesShownAtOnce = 20;
248
+
268
249
  const DEFAULT_OPTS = {
269
250
  prefixLength: 25,
270
251
  maxLength: 1000,