chrome-devtools-frontend 1.0.940714 → 1.0.942529

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 (241) hide show
  1. package/config/gni/all_devtools_files.gni +0 -57
  2. package/config/gni/devtools_grd_files.gni +55 -15
  3. package/config/gni/devtools_image_files.gni +2 -3
  4. package/front_end/.eslintrc.js +12 -1
  5. package/front_end/Images/src/feedback_button_icon.svg +3 -0
  6. package/front_end/Images/src/{feedback_thin_16x16_icon.svg → survey_feedback_icon.svg} +1 -1
  7. package/front_end/Tests.js +15 -0
  8. package/front_end/core/common/Color.ts +5 -0
  9. package/front_end/core/i18n/locales/en-US.json +31 -28
  10. package/front_end/core/i18n/locales/en-XL.json +31 -28
  11. package/front_end/core/sdk/DebuggerModel.ts +4 -14
  12. package/front_end/core/sdk/sdk-meta.ts +17 -3
  13. package/front_end/emulated_devices/module.json +1 -3
  14. package/front_end/entrypoints/devtools_app/devtools_app.json +1 -9
  15. package/front_end/entrypoints/main/MainImpl.ts +26 -0
  16. package/front_end/entrypoints/shell/shell.js +0 -11
  17. package/front_end/entrypoints/shell/shell.json +0 -2
  18. package/front_end/entrypoints/worker_app/worker_app.json +0 -4
  19. package/front_end/generated/InspectorBackendCommands.js +1 -0
  20. package/front_end/generated/protocol.d.ts +2 -0
  21. package/front_end/global_typings/global_defs.d.ts +5 -0
  22. package/front_end/legacy_test_runner/bindings_test_runner/IsolatedFilesystemTestRunner.js +2 -2
  23. package/front_end/legacy_test_runner/console_test_runner/console_test_runner.js +14 -2
  24. package/front_end/legacy_test_runner/legacy_test_runner.ts +10 -1
  25. package/front_end/legacy_test_runner/test_runner/TestRunner.js +11 -0
  26. package/front_end/models/formatter/SourceFormatter.ts +0 -10
  27. package/front_end/models/workspace/UISourceCode.ts +9 -42
  28. package/front_end/panels/animation/AnimationTimeline.ts +3 -3
  29. package/front_end/panels/application/ApplicationPanelSidebar.ts +3 -3
  30. package/front_end/panels/application/application-meta.ts +0 -3
  31. package/front_end/panels/application/components/EndpointsGrid.ts +1 -1
  32. package/front_end/panels/application/components/ReportsGrid.ts +1 -1
  33. package/front_end/panels/console/ConsolePinPane.ts +21 -26
  34. package/front_end/panels/coverage/CoverageDecorationManager.ts +4 -5
  35. package/front_end/panels/coverage/CoverageView.ts +2 -105
  36. package/front_end/panels/css_overview/components/CSSOverviewStartView.ts +11 -56
  37. package/front_end/panels/css_overview/components/cssOverviewStartView.css +1 -8
  38. package/front_end/panels/elements/ElementsTreeElement.ts +4 -9
  39. package/front_end/panels/elements/components/StylePropertyEditor.ts +2 -0
  40. package/front_end/panels/elements/components/adornerSettingsPane.css +0 -4
  41. package/front_end/panels/emulation/DeviceModeToolbar.ts +3 -1
  42. package/front_end/panels/emulation/DeviceModeView.ts +2 -1
  43. package/front_end/panels/emulation/InspectedPagePlaceholder.ts +3 -1
  44. package/front_end/panels/emulation/MediaQueryInspector.ts +3 -1
  45. package/front_end/panels/emulation/emulation-meta.ts +2 -4
  46. package/front_end/panels/issues/issues-meta.ts +0 -2
  47. package/front_end/panels/layers/module.json +0 -1
  48. package/front_end/panels/lighthouse/LighthousePanel.ts +2 -4
  49. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +1 -4
  50. package/front_end/panels/lighthouse/lighthouseStartView.css +4 -0
  51. package/front_end/panels/lighthouse/module.json +1 -4
  52. package/front_end/panels/media/media-meta.ts +0 -3
  53. package/front_end/panels/network/ResourceWebSocketFrameView.ts +2 -1
  54. package/front_end/panels/network/network-meta.ts +0 -3
  55. package/front_end/panels/profiler/module.json +1 -4
  56. package/front_end/panels/screencast/module.json +1 -4
  57. package/front_end/panels/security/security-meta.ts +0 -3
  58. package/front_end/panels/sources/BreakpointEditDialog.ts +16 -30
  59. package/front_end/panels/sources/CSSPlugin.ts +310 -331
  60. package/front_end/panels/sources/CallStackSidebarPane.ts +28 -34
  61. package/front_end/panels/sources/CoveragePlugin.ts +121 -6
  62. package/front_end/panels/sources/DebuggerPlugin.ts +1166 -1243
  63. package/front_end/panels/sources/EditingLocationHistoryManager.ts +71 -101
  64. package/front_end/panels/sources/GoToLineQuickOpen.ts +4 -3
  65. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +3 -3
  66. package/front_end/panels/sources/JavaScriptCompilerPlugin.ts +26 -23
  67. package/front_end/panels/sources/Plugin.ts +20 -4
  68. package/front_end/panels/sources/ProfilePlugin.ts +185 -0
  69. package/front_end/panels/sources/ScriptFormatterEditorAction.ts +3 -3
  70. package/front_end/panels/sources/ScriptOriginPlugin.ts +0 -10
  71. package/front_end/panels/sources/SnippetsPlugin.ts +1 -10
  72. package/front_end/panels/sources/SourcesPanel.ts +15 -10
  73. package/front_end/panels/sources/SourcesView.ts +10 -8
  74. package/front_end/panels/sources/TabbedEditorContainer.ts +31 -27
  75. package/front_end/panels/sources/UISourceCodeFrame.ts +335 -470
  76. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +3 -2
  77. package/front_end/panels/sources/sources-legacy.ts +0 -6
  78. package/front_end/panels/sources/sources.ts +0 -2
  79. package/front_end/panels/timeline/module.json +0 -3
  80. package/front_end/third_party/codemirror.next/bundle.ts +9 -13
  81. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  82. package/front_end/third_party/codemirror.next/chunk/javascript.js +2 -2
  83. package/front_end/third_party/codemirror.next/chunk/markdown.js +2 -6
  84. package/front_end/third_party/codemirror.next/chunk/php.js +2 -6
  85. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  86. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  87. package/front_end/third_party/codemirror.next/chunk/xml.js +2 -2
  88. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +279 -198
  89. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  90. package/front_end/third_party/codemirror.next/package.json +13 -11
  91. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1128 -1158
  92. package/front_end/third_party/lighthouse/locales/ar-XB.json +211 -79
  93. package/front_end/third_party/lighthouse/locales/ar.json +213 -81
  94. package/front_end/third_party/lighthouse/locales/bg.json +211 -79
  95. package/front_end/third_party/lighthouse/locales/ca.json +212 -80
  96. package/front_end/third_party/lighthouse/locales/cs.json +211 -79
  97. package/front_end/third_party/lighthouse/locales/da.json +211 -79
  98. package/front_end/third_party/lighthouse/locales/de.json +211 -79
  99. package/front_end/third_party/lighthouse/locales/el.json +213 -81
  100. package/front_end/third_party/lighthouse/locales/en-GB.json +211 -79
  101. package/front_end/third_party/lighthouse/locales/en-US.json +186 -75
  102. package/front_end/third_party/lighthouse/locales/en-XA.json +211 -79
  103. package/front_end/third_party/lighthouse/locales/en-XL.json +186 -75
  104. package/front_end/third_party/lighthouse/locales/es-419.json +211 -79
  105. package/front_end/third_party/lighthouse/locales/es.json +212 -80
  106. package/front_end/third_party/lighthouse/locales/fi.json +211 -79
  107. package/front_end/third_party/lighthouse/locales/fil.json +211 -79
  108. package/front_end/third_party/lighthouse/locales/fr.json +211 -79
  109. package/front_end/third_party/lighthouse/locales/he.json +212 -80
  110. package/front_end/third_party/lighthouse/locales/hi.json +214 -82
  111. package/front_end/third_party/lighthouse/locales/hr.json +211 -79
  112. package/front_end/third_party/lighthouse/locales/hu.json +211 -79
  113. package/front_end/third_party/lighthouse/locales/id.json +211 -79
  114. package/front_end/third_party/lighthouse/locales/it.json +211 -79
  115. package/front_end/third_party/lighthouse/locales/ja.json +211 -79
  116. package/front_end/third_party/lighthouse/locales/ko.json +211 -79
  117. package/front_end/third_party/lighthouse/locales/lt.json +211 -79
  118. package/front_end/third_party/lighthouse/locales/lv.json +214 -82
  119. package/front_end/third_party/lighthouse/locales/nl.json +211 -79
  120. package/front_end/third_party/lighthouse/locales/no.json +211 -79
  121. package/front_end/third_party/lighthouse/locales/pl.json +211 -79
  122. package/front_end/third_party/lighthouse/locales/pt-PT.json +211 -79
  123. package/front_end/third_party/lighthouse/locales/pt.json +211 -79
  124. package/front_end/third_party/lighthouse/locales/ro.json +212 -80
  125. package/front_end/third_party/lighthouse/locales/ru.json +211 -79
  126. package/front_end/third_party/lighthouse/locales/sk.json +211 -79
  127. package/front_end/third_party/lighthouse/locales/sl.json +211 -79
  128. package/front_end/third_party/lighthouse/locales/sr-Latn.json +211 -79
  129. package/front_end/third_party/lighthouse/locales/sr.json +211 -79
  130. package/front_end/third_party/lighthouse/locales/sv.json +211 -79
  131. package/front_end/third_party/lighthouse/locales/ta.json +218 -86
  132. package/front_end/third_party/lighthouse/locales/te.json +251 -119
  133. package/front_end/third_party/lighthouse/locales/th.json +211 -79
  134. package/front_end/third_party/lighthouse/locales/tr.json +211 -79
  135. package/front_end/third_party/lighthouse/locales/uk.json +212 -80
  136. package/front_end/third_party/lighthouse/locales/vi.json +211 -79
  137. package/front_end/third_party/lighthouse/locales/zh-HK.json +211 -79
  138. package/front_end/third_party/lighthouse/locales/zh-TW.json +211 -79
  139. package/front_end/third_party/lighthouse/locales/zh.json +211 -79
  140. package/front_end/third_party/lighthouse/report/bundle.d.ts +72 -34
  141. package/front_end/third_party/lighthouse/report/bundle.js +698 -492
  142. package/front_end/third_party/lighthouse/report-assets/report-generator.js +1 -2
  143. package/front_end/third_party/lighthouse/report-assets/report.js +40 -35
  144. package/front_end/third_party/lighthouse/report-assets/standalone-template.html +2 -4
  145. package/front_end/ui/components/code_highlighter/CodeHighlighter.ts +60 -68
  146. package/front_end/ui/components/data_grid/dataGrid.css +12 -10
  147. package/front_end/ui/components/docs/css_overview/start_view.html +25 -0
  148. package/front_end/ui/components/docs/css_overview/start_view.ts +14 -0
  149. package/front_end/ui/components/docs/icon_button/basic.ts +3 -3
  150. package/front_end/ui/components/docs/panel_feedback/basic.html +25 -0
  151. package/front_end/ui/components/docs/panel_feedback/basic.ts +20 -0
  152. package/front_end/ui/components/docs/panel_feedback/button.html +25 -0
  153. package/front_end/ui/components/docs/panel_feedback/button.ts +18 -0
  154. package/front_end/ui/components/helpers/get-stylesheet.ts +0 -13
  155. package/front_end/ui/components/markdown_view/MarkdownImagesMap.ts +1 -1
  156. package/front_end/ui/components/panel_feedback/FeedbackButton.ts +67 -0
  157. package/front_end/ui/components/panel_feedback/PanelFeedback.ts +100 -0
  158. package/front_end/ui/components/panel_feedback/panelFeedback.css +76 -0
  159. package/front_end/ui/components/panel_feedback/panel_feedback.ts +6 -0
  160. package/front_end/ui/components/report_view/reportValue.css +1 -0
  161. package/front_end/ui/components/survey_link/SurveyLink.ts +1 -1
  162. package/front_end/ui/components/text_editor/TextEditor.ts +79 -36
  163. package/front_end/ui/components/text_editor/config.ts +42 -26
  164. package/front_end/ui/components/text_editor/javascript.ts +2 -3
  165. package/front_end/ui/components/text_editor/position.ts +17 -0
  166. package/front_end/ui/components/text_editor/text_editor.ts +1 -0
  167. package/front_end/ui/components/text_editor/theme.ts +5 -1
  168. package/front_end/ui/legacy/Dialog.ts +3 -1
  169. package/front_end/ui/legacy/DropTarget.ts +2 -1
  170. package/front_end/ui/legacy/EmptyWidget.ts +2 -1
  171. package/front_end/ui/legacy/FilterBar.ts +2 -1
  172. package/front_end/ui/legacy/GlassPane.ts +4 -2
  173. package/front_end/ui/legacy/Infobar.ts +5 -8
  174. package/front_end/ui/legacy/InspectorView.ts +6 -1
  175. package/front_end/ui/legacy/ListWidget.ts +2 -1
  176. package/front_end/ui/legacy/PopoverHelper.ts +2 -1
  177. package/front_end/ui/legacy/ProgressIndicator.ts +2 -1
  178. package/front_end/ui/legacy/RemoteDebuggingTerminatedScreen.ts +2 -1
  179. package/front_end/ui/legacy/ReportView.ts +2 -1
  180. package/front_end/ui/legacy/RootView.ts +2 -1
  181. package/front_end/ui/legacy/SearchableView.ts +2 -1
  182. package/front_end/ui/legacy/ShortcutRegistry.ts +11 -7
  183. package/front_end/ui/legacy/SoftContextMenu.ts +2 -1
  184. package/front_end/ui/legacy/SoftDropDown.ts +4 -2
  185. package/front_end/ui/legacy/SplitWidget.ts +2 -1
  186. package/front_end/ui/legacy/SuggestBox.ts +2 -1
  187. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  188. package/front_end/ui/legacy/TargetCrashedScreen.ts +2 -1
  189. package/front_end/ui/legacy/TextPrompt.ts +2 -1
  190. package/front_end/ui/legacy/Toolbar.ts +3 -2
  191. package/front_end/ui/legacy/Treeoutline.ts +3 -2
  192. package/front_end/ui/legacy/UIUtils.ts +16 -13
  193. package/front_end/ui/legacy/ViewManager.ts +2 -1
  194. package/front_end/ui/legacy/Widget.ts +1 -1
  195. package/front_end/ui/legacy/components/perf_ui/ChartViewport.ts +2 -1
  196. package/front_end/ui/legacy/components/perf_ui/FilmStripView.ts +3 -1
  197. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +2 -1
  198. package/front_end/ui/legacy/components/perf_ui/LineLevelProfile.ts +35 -131
  199. package/front_end/ui/legacy/components/perf_ui/OverviewGrid.ts +2 -1
  200. package/front_end/ui/legacy/components/perf_ui/TimelineGrid.ts +3 -1
  201. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +2 -1
  202. package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +2 -2
  203. package/front_end/ui/legacy/components/source_frame/BinaryResourceViewFactory.ts +3 -6
  204. package/front_end/ui/legacy/components/source_frame/FontView.ts +1 -0
  205. package/front_end/ui/legacy/components/source_frame/ImageView.ts +1 -0
  206. package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -0
  207. package/front_end/ui/legacy/components/source_frame/ResourceSourceFrame.ts +19 -14
  208. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +502 -252
  209. package/front_end/ui/legacy/components/source_frame/XMLView.ts +2 -0
  210. package/front_end/ui/legacy/components/source_frame/module.json +0 -3
  211. package/front_end/ui/legacy/components/source_frame/source_frame-legacy.ts +0 -11
  212. package/front_end/ui/legacy/components/source_frame/source_frame.ts +0 -2
  213. package/front_end/ui/legacy/components/text_editor/CodeMirrorTextEditor.ts +2 -0
  214. package/front_end/ui/legacy/components/text_editor/cmdevtools.css +3 -1
  215. package/front_end/ui/legacy/components/text_editor/module.json +0 -3
  216. package/front_end/ui/legacy/components/utils/Linkifier.ts +7 -15
  217. package/front_end/ui/legacy/radioButton.css +1 -13
  218. package/front_end/ui/legacy/softContextMenu.css +1 -0
  219. package/front_end/ui/legacy/themeColors.css +36 -0
  220. package/front_end/ui/legacy/theme_support/theme_support_impl.ts +7 -9
  221. package/front_end/ui/legacy/utils/append-style.ts +9 -4
  222. package/front_end/ui/legacy/utils/create-shadow-root-with-core-styles.ts +2 -2
  223. package/front_end/ui/legacy/utils/inject-core-styles.ts +7 -4
  224. package/package.json +1 -1
  225. package/scripts/build/generate_css_js_files.js +23 -9
  226. package/scripts/build/ninja/generate_css.gni +10 -1
  227. package/scripts/eslint_rules/lib/check_css_import.js +2 -2
  228. package/scripts/eslint_rules/tests/check_css_import_test.js +12 -0
  229. package/front_end/Images/radioDot-dark-theme.png +0 -0
  230. package/front_end/Images/radioDot.png +0 -0
  231. package/front_end/panels/application/module.json +0 -7
  232. package/front_end/panels/emulation/module.json +0 -11
  233. package/front_end/panels/issues/module.json +0 -6
  234. package/front_end/panels/layer_viewer/module.json +0 -6
  235. package/front_end/panels/media/module.json +0 -6
  236. package/front_end/panels/network/module.json +0 -6
  237. package/front_end/panels/security/module.json +0 -5
  238. package/front_end/third_party/lighthouse/report-assets/report.css +0 -1774
  239. package/front_end/ui/legacy/components/perf_ui/module.json +0 -13
  240. package/front_end/ui/legacy/components/source_frame/SourcesTextEditor.ts +0 -1030
  241. package/front_end/ui/legacy/module.json +0 -41
@@ -33,12 +33,11 @@ import * as i18n from '../../../../core/i18n/i18n.js';
33
33
  import * as Platform from '../../../../core/platform/platform.js';
34
34
  import * as Formatter from '../../../../models/formatter/formatter.js';
35
35
  import * as TextUtils from '../../../../models/text_utils/text_utils.js';
36
- import type * as Workspace from '../../../../models/workspace/workspace.js';
36
+ import * as CodeMirror from '../../../../third_party/codemirror.next/codemirror.next.js';
37
+ import * as CodeHighlighter from '../../../components/code_highlighter/code_highlighter.js';
38
+ import * as TextEditor from '../../../components/text_editor/text_editor.js';
37
39
  import * as UI from '../../legacy.js';
38
40
 
39
- import type {SourcesTextEditorDelegate} from './SourcesTextEditor.js';
40
- import {Events, SourcesTextEditor} from './SourcesTextEditor.js';
41
-
42
41
  const UIStrings = {
43
42
  /**
44
43
  *@description Text for the source of something
@@ -85,9 +84,25 @@ const UIStrings = {
85
84
  const str_ = i18n.i18n.registerUIStrings('ui/legacy/components/source_frame/SourceFrame.ts', UIStrings);
86
85
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
87
86
 
88
- export class SourceFrameImpl extends UI.View.SimpleView implements UI.SearchableView.Searchable,
89
- UI.SearchableView.Replaceable,
90
- SourcesTextEditorDelegate, Transformer {
87
+ export interface SourceFrameOptions {
88
+ // Whether to show line numbers. Defaults to true.
89
+ lineNumbers?: boolean;
90
+ // Whether to wrap lines. Defaults to false.
91
+ lineWrapping?: boolean;
92
+ }
93
+
94
+ export const enum Events {
95
+ EditorUpdate = 'EditorUpdate',
96
+ EditorScroll = 'EditorScroll',
97
+ }
98
+
99
+ export type EventTypes = {
100
+ [Events.EditorUpdate]: CodeMirror.ViewUpdate,
101
+ [Events.EditorScroll]: void,
102
+ };
103
+
104
+ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.View.SimpleView>(
105
+ UI.View.SimpleView) implements UI.SearchableView.Searchable, UI.SearchableView.Replaceable, Transformer {
91
106
  private readonly lazyContent: () => Promise<TextUtils.ContentProvider.DeferredContent>;
92
107
  private prettyInternal: boolean;
93
108
  private rawContent: string|null;
@@ -96,13 +111,14 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
96
111
  private readonly prettyToggle: UI.Toolbar.ToolbarToggle;
97
112
  private shouldAutoPrettyPrint: boolean;
98
113
  private readonly progressToolbarItem: UI.Toolbar.ToolbarItem;
99
- private textEditorInternal: SourcesTextEditor;
100
- private prettyCleanGeneration: number|null;
101
- private cleanGeneration: number;
114
+ private textEditorInternal: TextEditor.TextEditor.TextEditor;
115
+ private prettyBaseDoc: CodeMirror.Text|null = null;
116
+ private baseDoc: CodeMirror.Text;
117
+ private displayedSelection: CodeMirror.EditorSelection|null = null;
102
118
  private searchConfig: UI.SearchableView.SearchConfig|null;
103
119
  private delayedFindSearchMatches: (() => void)|null;
104
120
  private currentSearchResultIndex: number;
105
- private searchResults: TextUtils.TextRange.TextRange[];
121
+ private searchResults: SearchMatch[];
106
122
  private searchRegex: RegExp|null;
107
123
  private loadError: boolean;
108
124
  private muteChangeEventsForSetContent: boolean;
@@ -118,12 +134,11 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
118
134
  private selectionToSet: TextUtils.TextRange.TextRange|null;
119
135
  private loadedInternal: boolean;
120
136
  private contentRequested: boolean;
121
- private highlighterTypeInternal: string;
122
137
  private wasmDisassemblyInternal: Common.WasmDisassembly.WasmDisassembly|null;
123
138
  contentSet: boolean;
124
139
  constructor(
125
140
  lazyContent: () => Promise<TextUtils.ContentProvider.DeferredContent>,
126
- codeMirrorOptions?: UI.TextEditor.Options) {
141
+ private readonly options: SourceFrameOptions = {}) {
127
142
  super(i18nString(UIStrings.source));
128
143
 
129
144
  this.lazyContent = lazyContent;
@@ -141,11 +156,11 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
141
156
 
142
157
  this.progressToolbarItem = new UI.Toolbar.ToolbarItem(document.createElement('div'));
143
158
 
144
- this.textEditorInternal = new SourcesTextEditor(this, codeMirrorOptions);
145
- this.textEditorInternal.show(this.element);
159
+ this.textEditorInternal = new TextEditor.TextEditor.TextEditor(this.placeholderEditorState(''));
160
+ this.textEditorInternal.style.flexGrow = '1';
161
+ this.element.appendChild(this.textEditorInternal);
146
162
 
147
- this.prettyCleanGeneration = null;
148
- this.cleanGeneration = 0;
163
+ this.baseDoc = this.textEditorInternal.state.doc;
149
164
 
150
165
  this.searchConfig = null;
151
166
  this.delayedFindSearchMatches = null;
@@ -154,36 +169,110 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
154
169
  this.searchRegex = null;
155
170
  this.loadError = false;
156
171
 
157
- this.textEditorInternal.addEventListener(Events.EditorFocused, this.resetCurrentSearchResultIndex, this);
158
- this.textEditorInternal.addEventListener(Events.SelectionChanged, this.updateSourcePosition, this);
159
- this.textEditorInternal.addEventListener(UI.TextEditor.Events.TextChanged, event => {
160
- if (!this.muteChangeEventsForSetContent) {
161
- this.onTextChanged(event.data.oldRange, event.data.newRange);
162
- }
163
- });
164
172
  this.muteChangeEventsForSetContent = false;
165
173
 
166
174
  this.sourcePosition = new UI.Toolbar.ToolbarText();
167
175
 
168
176
  this.searchableView = null;
169
177
  this.editable = false;
170
- this.textEditorInternal.setReadOnly(true);
171
178
 
172
179
  this.positionToReveal = null;
173
180
  this.lineToScrollTo = null;
174
181
  this.selectionToSet = null;
175
182
  this.loadedInternal = false;
176
183
  this.contentRequested = false;
177
- this.highlighterTypeInternal = '';
178
184
 
179
185
  this.wasmDisassemblyInternal = null;
180
186
  this.contentSet = false;
181
187
  }
182
188
 
189
+ private placeholderEditorState(content: string): CodeMirror.EditorState {
190
+ return CodeMirror.EditorState.create({
191
+ doc: content,
192
+ extensions: [
193
+ CodeMirror.EditorState.readOnly.of(true),
194
+ this.options.lineNumbers !== false ? CodeMirror.lineNumbers() : [],
195
+ TextEditor.Config.theme(),
196
+ ],
197
+ });
198
+ }
199
+
200
+ protected editorConfiguration(doc: string): CodeMirror.Extension {
201
+ return [
202
+ CodeMirror.EditorView.updateListener.of(update => this.dispatchEventToListeners(Events.EditorUpdate, update)),
203
+ TextEditor.Config.baseConfiguration(doc),
204
+ TextEditor.Config.sourcesAutocompletion.instance(),
205
+ TextEditor.Config.showWhitespace.instance(),
206
+ TextEditor.Config.allowScrollPastEof.instance(),
207
+ TextEditor.Config.codeFolding.instance(),
208
+ TextEditor.Config.autoDetectIndent.instance(),
209
+ CodeMirror.EditorView.theme({
210
+ '&.cm-editor': {height: '100%'},
211
+ '.cm-scroller': {overflow: 'auto'},
212
+ '.cm-lineNumbers .cm-gutterElement.cm-nonBreakableLine': {color: 'var(--color-non-breakable-line)'},
213
+ '.cm-searchMatch': {
214
+ border: '1px solid var(--color-search-match-border)',
215
+ borderRadius: '3px',
216
+ margin: '0 -1px',
217
+ '&.cm-searchMatch-selected': {
218
+ borderRadius: '1px',
219
+ backgroundColor: 'var(--color-selected-search-match-background)',
220
+ borderColor: 'var(--color-selected-search-match-background)',
221
+ '&, & *': {
222
+ color: 'var(--color-selected-search-match) !important',
223
+ },
224
+ },
225
+ },
226
+ }),
227
+ CodeMirror.EditorView.domEventHandlers({
228
+ focus: () => this.onFocus(),
229
+ blur: () => this.onBlur(),
230
+ scroll: () => this.dispatchEventToListeners(Events.EditorScroll),
231
+ contextmenu: event => this.onContextMenu(event),
232
+ }),
233
+ CodeMirror.lineNumbers({
234
+ domEventHandlers:
235
+ {contextmenu: (_view, block, event) => this.onLineGutterContextMenu(block.from, event as MouseEvent)},
236
+ }),
237
+ CodeMirror.EditorView.updateListener.of(
238
+ (update):
239
+ void => {
240
+ if (update.selectionSet || update.docChanged) {
241
+ this.updateSourcePosition();
242
+ }
243
+ if (update.docChanged) {
244
+ this.onTextChanged();
245
+ }
246
+ }),
247
+ activeSearchState,
248
+ CodeMirror.Prec.lowest(searchHighlighter),
249
+ config.lineNumbers.of([]),
250
+ config.language.of([]),
251
+ this.wasmDisassemblyInternal ? markNonBreakableLines(this.wasmDisassemblyInternal) : nonBreakableLines,
252
+ this.options.lineWrapping ? CodeMirror.EditorView.lineWrapping : [],
253
+ this.options.lineNumbers !== false ? CodeMirror.lineNumbers() : [],
254
+ ];
255
+ }
256
+
257
+ protected onBlur(): void {
258
+ }
259
+
260
+ protected onFocus(): void {
261
+ this.resetCurrentSearchResultIndex();
262
+ }
263
+
183
264
  get wasmDisassembly(): Common.WasmDisassembly.WasmDisassembly|null {
184
265
  return this.wasmDisassemblyInternal;
185
266
  }
186
267
 
268
+ editorLocationToUILocation(lineNumber: number, columnNumber: number): {
269
+ lineNumber: number,
270
+ columnNumber: number,
271
+ };
272
+ editorLocationToUILocation(lineNumber: number): {
273
+ lineNumber: number,
274
+ columnNumber: number|undefined,
275
+ };
187
276
  editorLocationToUILocation(lineNumber: number, columnNumber?: number): {
188
277
  lineNumber: number,
189
278
  columnNumber?: number|undefined,
@@ -215,47 +304,62 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
215
304
  this.prettyToggle.setVisible(canPrettyPrint);
216
305
  }
217
306
 
307
+ setEditable(editable: boolean): void {
308
+ this.editable = editable;
309
+ if (this.loaded && editable !== !this.textEditor.state.readOnly) {
310
+ this.textEditor.dispatch({effects: config.editable.reconfigure(CodeMirror.EditorState.readOnly.of(!editable))});
311
+ }
312
+ }
313
+
218
314
  private async setPretty(value: boolean): Promise<void> {
219
315
  this.prettyInternal = value;
220
316
  this.prettyToggle.setEnabled(false);
221
317
 
222
318
  const wasLoaded = this.loaded;
223
- const selection = this.selection();
319
+ const {textEditor} = this;
320
+ const selection = textEditor.state.selection.main;
321
+ const startPos = textEditor.toLineColumn(selection.from), endPos = textEditor.toLineColumn(selection.to);
224
322
  let newSelection;
225
323
  if (this.prettyInternal) {
226
324
  const formatInfo = await this.requestFormattedContent();
227
325
  this.formattedMap = formatInfo.formattedMapping;
228
- this.setContent(formatInfo.formattedContent, null);
229
- this.prettyCleanGeneration = this.textEditorInternal.markClean();
230
- const start = this.rawToPrettyLocation(selection.startLine, selection.startColumn);
231
- const end = this.rawToPrettyLocation(selection.endLine, selection.endColumn);
232
- newSelection = new TextUtils.TextRange.TextRange(start[0], start[1], end[0], end[1]);
326
+ await this.setContent(formatInfo.formattedContent);
327
+ this.prettyBaseDoc = textEditor.state.doc;
328
+ const start = this.rawToPrettyLocation(startPos.lineNumber, startPos.columnNumber);
329
+ const end = this.rawToPrettyLocation(endPos.lineNumber, endPos.columnNumber);
330
+ newSelection = textEditor.createSelection(
331
+ {lineNumber: start[0], columnNumber: start[1]}, {lineNumber: end[0], columnNumber: end[1]});
233
332
  } else {
234
- this.setContent(this.rawContent, null);
235
- this.cleanGeneration = this.textEditorInternal.markClean();
236
- const start = this.prettyToRawLocation(selection.startLine, selection.startColumn);
237
- const end = this.prettyToRawLocation(selection.endLine, selection.endColumn);
238
- newSelection = new TextUtils.TextRange.TextRange(start[0], start[1], end[0], end[1]);
333
+ await this.setContent(this.rawContent || '');
334
+ this.baseDoc = textEditor.state.doc;
335
+ const start = this.prettyToRawLocation(startPos.lineNumber, startPos.columnNumber);
336
+ const end = this.prettyToRawLocation(endPos.lineNumber, endPos.columnNumber);
337
+ newSelection = textEditor.createSelection(
338
+ {lineNumber: start[0], columnNumber: start[1]}, {lineNumber: end[0], columnNumber: end[1]});
239
339
  }
240
340
  if (wasLoaded) {
241
- this.textEditor.revealPosition(newSelection.endLine, newSelection.endColumn, this.editable);
242
- this.textEditor.setSelection(newSelection);
341
+ textEditor.revealPosition(newSelection, false);
243
342
  }
244
343
  this.prettyToggle.setEnabled(true);
245
344
  this.updatePrettyPrintState();
246
345
  }
247
346
 
248
- private updateLineNumberFormatter(): void {
347
+ private getLineNumberFormatter(): CodeMirror.Extension {
348
+ if (this.options.lineNumbers === false) {
349
+ return [];
350
+ }
351
+ let formatNumber = null;
249
352
  if (this.wasmDisassemblyInternal) {
250
353
  const disassembly = this.wasmDisassemblyInternal;
251
354
  const lastBytecodeOffset = disassembly.lineNumberToBytecodeOffset(disassembly.lineNumbers - 1);
252
355
  const bytecodeOffsetDigits = lastBytecodeOffset.toString(16).length + 1;
253
- this.textEditorInternal.setLineNumberFormatter(lineNumber => {
254
- const bytecodeOffset = disassembly.lineNumberToBytecodeOffset(lineNumber - 1);
356
+ formatNumber = (lineNumber: number): string => {
357
+ const bytecodeOffset =
358
+ disassembly.lineNumberToBytecodeOffset(Math.min(disassembly.lineNumbers, lineNumber) - 1);
255
359
  return `0x${bytecodeOffset.toString(16).padStart(bytecodeOffsetDigits, '0')}`;
256
- });
360
+ };
257
361
  } else if (this.prettyInternal) {
258
- this.textEditorInternal.setLineNumberFormatter(lineNumber => {
362
+ formatNumber = (lineNumber: number): string => {
259
363
  const line = this.prettyToRawLocation(lineNumber - 1, 0)[0] + 1;
260
364
  if (lineNumber === 1) {
261
365
  return String(line);
@@ -264,17 +368,18 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
264
368
  return String(line);
265
369
  }
266
370
  return '-';
267
- });
268
- } else {
269
- this.textEditorInternal.setLineNumberFormatter(lineNumber => {
270
- return String(lineNumber);
271
- });
371
+ };
272
372
  }
373
+ return formatNumber ? CodeMirror.lineNumbers({formatNumber}) : [];
374
+ }
375
+
376
+ private updateLineNumberFormatter(): void {
377
+ this.textEditor.dispatch({effects: config.lineNumbers.reconfigure(this.getLineNumberFormatter())});
273
378
  }
274
379
 
275
380
  private updatePrettyPrintState(): void {
276
381
  this.prettyToggle.setToggled(this.prettyInternal);
277
- this.textEditorInternal.element.classList.toggle('pretty-printed', this.prettyInternal);
382
+ this.textEditorInternal.classList.toggle('pretty-printed', this.prettyInternal);
278
383
  this.updateLineNumberFormatter();
279
384
  }
280
385
 
@@ -292,13 +397,6 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
292
397
  return this.formattedMap.originalToFormatted(line, column);
293
398
  }
294
399
 
295
- setEditable(editable: boolean): void {
296
- this.editable = editable;
297
- if (this.loadedInternal) {
298
- this.textEditorInternal.setReadOnly(!editable);
299
- }
300
- }
301
-
302
400
  hasLoadError(): boolean {
303
401
  return this.loadError;
304
402
  }
@@ -322,7 +420,7 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
322
420
  return this.loadedInternal;
323
421
  }
324
422
 
325
- get textEditor(): SourcesTextEditor {
423
+ get textEditor(): TextEditor.TextEditor.TextEditor {
326
424
  return this.textEditorInternal;
327
425
  }
328
426
 
@@ -330,6 +428,14 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
330
428
  return this.prettyInternal;
331
429
  }
332
430
 
431
+ get contentType(): string {
432
+ return this.loadError ? '' : this.getContentType();
433
+ }
434
+
435
+ protected getContentType(): string {
436
+ return '';
437
+ }
438
+
333
439
  private async ensureContentLoaded(): Promise<void> {
334
440
  if (!this.contentRequested) {
335
441
  this.contentRequested = true;
@@ -351,7 +457,7 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
351
457
 
352
458
  progressIndicator.setWorked(1);
353
459
 
354
- if (!error && this.highlighterTypeInternal === 'application/wasm') {
460
+ if (!error && this.contentType === 'application/wasm') {
355
461
  const worker = Common.Worker.WorkerWrapper.fromURL(
356
462
  new URL('../../../../entrypoints/wasmparser_worker/wasmparser_worker-entrypoint.js', import.meta.url));
357
463
  const promise = new Promise<{
@@ -407,24 +513,14 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
407
513
  this.prettyToggle.setEnabled(true);
408
514
 
409
515
  if (error) {
410
- this.setContent(null, error);
516
+ this.loadError = true;
517
+ this.textEditor.editor.setState(this.placeholderEditorState(error));
411
518
  this.prettyToggle.setEnabled(false);
412
-
413
- // Occasionally on load, there can be a race in which it appears the CodeMirror plugin
414
- // runs the highlighter type assignment out of order. In case of an error then, set
415
- // the highlighter type after a short delay. This appears to only occur the first
416
- // time that CodeMirror is initialized, likely because the highlighter type was first
417
- // initialized based on the file type, and the syntax highlighting is in a race
418
- // with the new highlighter assignment. As the option is just an option and is not
419
- // observable, we can't handle waiting for it here.
420
- // https://github.com/codemirror/CodeMirror/issues/6019
421
- // CRBug 1011445
422
- setTimeout(() => this.setHighlighterType('text/plain'), 50);
423
519
  } else {
424
520
  if (this.shouldAutoPrettyPrint && TextUtils.TextUtils.isMinified(content)) {
425
521
  await this.setPretty(true);
426
522
  } else {
427
- this.setContent(this.rawContent, null);
523
+ await this.setContent(this.rawContent || '');
428
524
  }
429
525
  }
430
526
  this.contentSet = true;
@@ -436,14 +532,28 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
436
532
  return this.formattedContentPromise;
437
533
  }
438
534
  this.formattedContentPromise =
439
- Formatter.ScriptFormatter.formatScriptContent(this.highlighterTypeInternal, this.rawContent || '');
535
+ Formatter.ScriptFormatter.formatScriptContent(this.contentType, this.rawContent || '');
440
536
  return this.formattedContentPromise;
441
537
  }
442
538
 
443
- revealPosition(line: number, column?: number, shouldHighlight?: boolean): void {
539
+ revealPosition(position: {lineNumber: number, columnNumber?: number}|number, shouldHighlight?: boolean): void {
444
540
  this.lineToScrollTo = null;
445
541
  this.selectionToSet = null;
446
- this.positionToReveal = {line: line, column: column, shouldHighlight: shouldHighlight};
542
+ let line = 0, column = 0;
543
+ if (typeof position === 'number') {
544
+ const {doc} = this.textEditor.state;
545
+ if (position > doc.length) {
546
+ line = doc.lines - 1;
547
+ } else if (position >= 0) {
548
+ const lineObj = doc.lineAt(position);
549
+ line = lineObj.number - 1;
550
+ column = position - lineObj.from;
551
+ }
552
+ } else {
553
+ line = position.lineNumber;
554
+ column = position.columnNumber ?? 0;
555
+ }
556
+ this.positionToReveal = {line, column, shouldHighlight: shouldHighlight};
447
557
  this.innerRevealPositionIfNeeded();
448
558
  }
449
559
 
@@ -456,15 +566,14 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
456
566
  return;
457
567
  }
458
568
 
459
- const {lineNumber, columnNumber} =
460
- this.uiLocationToEditorLocation(this.positionToReveal.line, this.positionToReveal.column);
569
+ const location = this.uiLocationToEditorLocation(this.positionToReveal.line, this.positionToReveal.column);
461
570
 
462
- this.textEditorInternal.revealPosition(lineNumber, columnNumber, this.positionToReveal.shouldHighlight);
571
+ const {textEditor} = this;
572
+ textEditor.revealPosition(textEditor.createSelection(location), this.positionToReveal.shouldHighlight);
463
573
  this.positionToReveal = null;
464
574
  }
465
575
 
466
576
  private clearPositionToReveal(): void {
467
- this.textEditorInternal.clearPositionHighlight();
468
577
  this.positionToReveal = null;
469
578
  }
470
579
 
@@ -477,24 +586,28 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
477
586
  private innerScrollToLineIfNeeded(): void {
478
587
  if (this.lineToScrollTo !== null) {
479
588
  if (this.loaded && this.isShowing()) {
480
- this.textEditorInternal.scrollToLine(this.lineToScrollTo);
589
+ const {textEditor} = this;
590
+ const position = textEditor.toOffset({lineNumber: this.lineToScrollTo, columnNumber: 0});
591
+ textEditor.dispatch({effects: CodeMirror.EditorView.scrollTo.of(CodeMirror.EditorSelection.cursor(position))});
481
592
  this.lineToScrollTo = null;
482
593
  }
483
594
  }
484
595
  }
485
596
 
486
- selection(): TextUtils.TextRange.TextRange {
487
- return this.textEditor.selection();
488
- }
489
-
490
597
  setSelection(textRange: TextUtils.TextRange.TextRange): void {
491
598
  this.selectionToSet = textRange;
492
599
  this.innerSetSelectionIfNeeded();
493
600
  }
494
601
 
495
602
  private innerSetSelectionIfNeeded(): void {
496
- if (this.selectionToSet && this.loaded && this.isShowing()) {
497
- this.textEditorInternal.setSelection(this.selectionToSet, true);
603
+ const sel = this.selectionToSet;
604
+ if (sel && this.loaded && this.isShowing()) {
605
+ const {textEditor} = this;
606
+ textEditor.dispatch({
607
+ selection: textEditor.createSelection(
608
+ {lineNumber: sel.startLine, columnNumber: sel.startColumn},
609
+ {lineNumber: sel.endLine, columnNumber: sel.endColumn}),
610
+ });
498
611
  this.selectionToSet = null;
499
612
  }
500
613
  }
@@ -505,9 +618,9 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
505
618
  this.innerScrollToLineIfNeeded();
506
619
  }
507
620
 
508
- onTextChanged(_oldRange: TextUtils.TextRange.TextRange, _newRange: TextUtils.TextRange.TextRange): void {
621
+ onTextChanged(): void {
509
622
  const wasPretty = this.pretty;
510
- this.prettyInternal = this.prettyCleanGeneration !== null && this.textEditor.isClean(this.prettyCleanGeneration);
623
+ this.prettyInternal = Boolean(this.prettyBaseDoc && this.textEditor.state.doc.eq(this.prettyBaseDoc));
511
624
  if (this.prettyInternal !== wasPretty) {
512
625
  this.updatePrettyPrintState();
513
626
  }
@@ -519,14 +632,14 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
519
632
  }
520
633
 
521
634
  isClean(): boolean {
522
- return this.textEditor.isClean(this.cleanGeneration) ||
523
- (this.prettyCleanGeneration !== null && this.textEditor.isClean(this.prettyCleanGeneration));
635
+ return this.textEditor.state.doc.eq(this.baseDoc) ||
636
+ (this.prettyBaseDoc !== null && this.textEditor.state.doc.eq(this.prettyBaseDoc));
524
637
  }
525
638
 
526
639
  contentCommitted(): void {
527
- this.cleanGeneration = this.textEditorInternal.markClean();
528
- this.prettyCleanGeneration = null;
529
- this.rawContent = this.textEditor.text();
640
+ this.baseDoc = this.textEditorInternal.state.doc;
641
+ this.prettyBaseDoc = null;
642
+ this.rawContent = this.textEditor.state.doc.toString();
530
643
  this.formattedMap = null;
531
644
  this.formattedContentPromise = null;
532
645
  if (this.prettyInternal) {
@@ -562,52 +675,49 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
562
675
  return mimeType;
563
676
  }
564
677
 
565
- setHighlighterType(highlighterType: string): void {
566
- this.highlighterTypeInternal = highlighterType;
567
- this.updateHighlighterType('');
568
- }
569
-
570
- highlighterType(): string {
571
- return this.highlighterTypeInternal;
678
+ protected async getLanguageSupport(content: string): Promise<CodeMirror.Extension> {
679
+ const mimeType = this.simplifyMimeType(content, this.contentType) || '';
680
+ const languageDesc = await CodeHighlighter.CodeHighlighter.languageFromMIME(mimeType);
681
+ if (!languageDesc) {
682
+ return [];
683
+ }
684
+ if (mimeType === 'text/jsx') {
685
+ return [
686
+ languageDesc,
687
+ CodeMirror.javascript.javascriptLanguage.data.of({autocomplete: CodeMirror.completeAnyWord}),
688
+ ];
689
+ }
690
+ return languageDesc;
572
691
  }
573
692
 
574
- private updateHighlighterType(content: string): void {
575
- this.textEditorInternal.setMimeType(this.simplifyMimeType(content, this.highlighterTypeInternal));
693
+ async updateLanguageMode(content: string): Promise<void> {
694
+ const langExtension = await this.getLanguageSupport(content);
695
+ this.textEditor.dispatch({effects: config.language.reconfigure(langExtension)});
576
696
  }
577
697
 
578
- setContent(content: string|null, loadError: string|null): void {
698
+ async setContent(content: string): Promise<void> {
579
699
  this.muteChangeEventsForSetContent = true;
580
- if (!this.loadedInternal) {
581
- this.loadedInternal = true;
582
- if (!loadError) {
583
- this.textEditorInternal.setText(content || '');
584
- this.cleanGeneration = this.textEditorInternal.markClean();
585
- this.textEditorInternal.setReadOnly(!this.editable);
586
- this.loadError = false;
587
- } else {
588
- this.textEditorInternal.setText(loadError || '');
589
- this.highlighterTypeInternal = 'text/plain';
590
- this.textEditorInternal.setReadOnly(true);
591
- this.loadError = true;
592
- }
593
- } else {
594
- const scrollTop = this.textEditorInternal.scrollTop();
595
- const selection = this.textEditorInternal.selection();
596
- this.textEditorInternal.setText(content || '');
597
- this.textEditorInternal.setScrollTop(scrollTop);
598
- this.textEditorInternal.setSelection(selection);
599
- }
600
-
601
- // Mark non-breakable lines in the Wasm disassembly after setting
602
- // up the content for the text editor (which creates the gutter).
603
- if (this.wasmDisassemblyInternal) {
604
- for (const lineNumber of this.wasmDisassemblyInternal.nonBreakableLineNumbers()) {
605
- this.textEditorInternal.toggleLineClass(lineNumber, 'cm-non-breakable-line', true);
606
- }
700
+ const {textEditor} = this;
701
+ const wasLoaded = this.loadedInternal;
702
+ const scrollTop = textEditor.editor.scrollDOM.scrollTop;
703
+ this.loadedInternal = true;
704
+
705
+ const languageSupport = await this.getLanguageSupport(content);
706
+ const editorState = CodeMirror.EditorState.create({
707
+ doc: content,
708
+ extensions: [
709
+ this.editorConfiguration(content),
710
+ languageSupport,
711
+ this.getLineNumberFormatter(),
712
+ config.editable.of(this.editable ? [] : CodeMirror.EditorState.readOnly.of(true)),
713
+ ],
714
+ });
715
+ this.baseDoc = editorState.doc;
716
+ textEditor.editor.setState(editorState);
717
+ if (wasLoaded) {
718
+ textEditor.editor.scrollDOM.scrollTop = scrollTop;
607
719
  }
608
-
609
- this.updateLineNumberFormatter();
610
- this.updateHighlighterType(content || '');
720
+ this.editorInitialized();
611
721
  this.wasShownOrLoaded();
612
722
 
613
723
  if (this.delayedFindSearchMatches) {
@@ -617,6 +727,9 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
617
727
  this.muteChangeEventsForSetContent = false;
618
728
  }
619
729
 
730
+ protected editorInitialized(): void {
731
+ }
732
+
620
733
  setSearchableView(view: UI.SearchableView.SearchableView|null): void {
621
734
  this.searchableView = view;
622
735
  }
@@ -624,24 +737,25 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
624
737
  private doFindSearchMatches(
625
738
  searchConfig: UI.SearchableView.SearchConfig, shouldJump: boolean, jumpBackwards: boolean): void {
626
739
  this.currentSearchResultIndex = -1;
627
- this.searchResults = [];
628
740
 
629
- const regex = searchConfig.toSearchRegex();
630
- this.searchRegex = regex;
631
- this.searchResults = this.collectRegexMatches(regex);
741
+ this.searchRegex = searchConfig.toSearchRegex(true);
742
+ this.searchResults = this.collectRegexMatches(this.searchRegex);
632
743
 
633
744
  if (this.searchableView) {
634
745
  this.searchableView.updateSearchMatchesCount(this.searchResults.length);
635
746
  }
636
747
 
748
+ const editor = this.textEditor;
637
749
  if (!this.searchResults.length) {
638
- this.textEditorInternal.cancelSearchResultsHighlight();
750
+ if (editor.state.field(activeSearchState)) {
751
+ editor.dispatch({effects: setActiveSearch.of(null)});
752
+ }
639
753
  } else if (shouldJump && jumpBackwards) {
640
754
  this.jumpToPreviousSearchResult();
641
755
  } else if (shouldJump) {
642
756
  this.jumpToNextSearchResult();
643
757
  } else {
644
- this.textEditorInternal.highlightSearchResults(regex, null);
758
+ editor.dispatch({effects: setActiveSearch.of(new ActiveSearch(this.searchRegex, null))});
645
759
  }
646
760
  }
647
761
 
@@ -670,7 +784,11 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
670
784
  if (this.searchableView) {
671
785
  this.searchableView.updateCurrentMatchIndex(this.currentSearchResultIndex);
672
786
  }
673
- this.textEditorInternal.highlightSearchResults((this.searchRegex as RegExp), null);
787
+ const editor = this.textEditor;
788
+ const currentActiveSearch = editor.state.field(activeSearchState);
789
+ if (currentActiveSearch && currentActiveSearch.currentRange) {
790
+ editor.dispatch({effects: setActiveSearch.of(new ActiveSearch(currentActiveSearch.regexp, null))});
791
+ }
674
792
  }
675
793
 
676
794
  private resetSearch(): void {
@@ -687,10 +805,13 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
687
805
  if (!this.loaded) {
688
806
  return;
689
807
  }
690
- this.textEditorInternal.cancelSearchResultsHighlight();
691
- if (range) {
692
- this.setSelection(range);
693
- }
808
+ const editor = this.textEditor;
809
+ editor.dispatch({
810
+ effects: setActiveSearch.of(null),
811
+ selection: range ? {anchor: range.from, head: range.to} : undefined,
812
+ scrollIntoView: true,
813
+ userEvent: 'select.search.cancel',
814
+ });
694
815
  }
695
816
 
696
817
  jumpToLastSearchResult(): void {
@@ -699,8 +820,7 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
699
820
 
700
821
  private searchResultIndexForCurrentSelection(): number {
701
822
  return Platform.ArrayUtilities.lowerBound(
702
- this.searchResults, this.textEditorInternal.selection().collapseToEnd(),
703
- TextUtils.TextRange.TextRange.comparator);
823
+ this.searchResults, this.textEditor.state.selection.main, (a, b): number => a.to - b.to);
704
824
  }
705
825
 
706
826
  jumpToNextSearchResult(): void {
@@ -730,8 +850,14 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
730
850
  if (this.searchableView) {
731
851
  this.searchableView.updateCurrentMatchIndex(this.currentSearchResultIndex);
732
852
  }
733
- this.textEditorInternal.highlightSearchResults(
734
- (this.searchRegex as RegExp), this.searchResults[this.currentSearchResultIndex]);
853
+ const editor = this.textEditor;
854
+ const range = this.searchResults[this.currentSearchResultIndex];
855
+ editor.dispatch({
856
+ effects: setActiveSearch.of(new ActiveSearch(this.searchRegex as RegExp, range)),
857
+ selection: {anchor: range.from, head: range.to},
858
+ scrollIntoView: true,
859
+ userEvent: 'select.search',
860
+ });
735
861
  }
736
862
 
737
863
  replaceSelectionWith(searchConfig: UI.SearchableView.SearchConfig, replacement: string): void {
@@ -739,146 +865,153 @@ export class SourceFrameImpl extends UI.View.SimpleView implements UI.Searchable
739
865
  if (!range) {
740
866
  return;
741
867
  }
742
- this.textEditorInternal.highlightSearchResults((this.searchRegex as RegExp), null);
743
-
744
- const oldText = this.textEditorInternal.text(range);
745
- const regex = searchConfig.toSearchRegex();
746
- let text;
747
- if (regex.__fromRegExpQuery) {
748
- text = oldText.replace(regex, replacement);
749
- } else {
750
- text = oldText.replace(regex, function() {
751
- return replacement;
752
- });
753
- }
754
868
 
755
- const newRange = this.textEditorInternal.editRange(range, text);
756
- this.textEditorInternal.setSelection(newRange.collapseToEnd());
869
+ const insert = (this.searchRegex as RegExp).__fromRegExpQuery ? range.insertPlaceholders(replacement) : replacement;
870
+ const editor = this.textEditor;
871
+ const changes = editor.state.changes({from: range.from, to: range.to, insert});
872
+ editor.dispatch(
873
+ {changes, selection: {anchor: changes.mapPos(editor.state.selection.main.to, 1)}, userEvent: 'input.replace'});
757
874
  }
758
875
 
759
876
  replaceAllWith(searchConfig: UI.SearchableView.SearchConfig, replacement: string): void {
760
877
  this.resetCurrentSearchResultIndex();
761
878
 
762
- let text = this.textEditorInternal.text();
763
- const range = this.textEditorInternal.fullRange();
764
-
765
879
  const regex = searchConfig.toSearchRegex(true);
766
- if (regex.__fromRegExpQuery) {
767
- text = text.replace(regex, replacement);
768
- } else {
769
- text = text.replace(regex, function() {
770
- return replacement;
771
- });
772
- }
773
-
774
880
  const ranges = this.collectRegexMatches(regex);
775
881
  if (!ranges.length) {
776
882
  return;
777
883
  }
778
884
 
779
- // Calculate the position of the end of the last range to be edited.
780
- const currentRangeIndex = Platform.ArrayUtilities.lowerBound(
781
- ranges, this.textEditorInternal.selection(), TextUtils.TextRange.TextRange.comparator);
782
- const lastRangeIndex = Platform.NumberUtilities.mod(currentRangeIndex - 1, ranges.length);
783
- const lastRange = ranges[lastRangeIndex];
784
- const replacementLineEndings = Platform.StringUtilities.findLineEndingIndexes(replacement);
785
- const replacementLineCount = replacementLineEndings.length;
786
- const lastLineNumber = lastRange.startLine + replacementLineEndings.length - 1;
787
- let lastColumnNumber: number = lastRange.startColumn;
788
- if (replacementLineEndings.length > 1) {
789
- lastColumnNumber =
790
- replacementLineEndings[replacementLineCount - 1] - replacementLineEndings[replacementLineCount - 2] - 1;
791
- }
885
+ const isRegExp = regex.__fromRegExpQuery;
886
+ const changes = ranges.map(
887
+ match =>
888
+ ({from: match.from, to: match.to, insert: isRegExp ? match.insertPlaceholders(replacement) : replacement}));
792
889
 
793
- this.textEditorInternal.editRange(range, text);
794
- this.textEditorInternal.revealPosition(lastLineNumber, lastColumnNumber);
795
- this.textEditorInternal.setSelection(
796
- TextUtils.TextRange.TextRange.createFromLocation(lastLineNumber, lastColumnNumber));
890
+ this.textEditor.dispatch({changes, scrollIntoView: true, userEvent: 'input.replace.all'});
797
891
  }
798
892
 
799
- private collectRegexMatches(regexObject: RegExp): TextUtils.TextRange.TextRange[] {
893
+ private collectRegexMatches(regexObject: RegExp): SearchMatch[] {
800
894
  const ranges = [];
801
- for (let i = 0; i < this.textEditorInternal.linesCount; ++i) {
802
- let line = this.textEditorInternal.line(i);
803
- let offset = 0;
804
- let match;
805
- do {
806
- match = regexObject.exec(line);
807
- if (match) {
808
- const matchEndIndex = match.index + Math.max(match[0].length, 1);
809
- if (match[0].length) {
810
- ranges.push(new TextUtils.TextRange.TextRange(i, offset + match.index, i, offset + matchEndIndex));
811
- }
812
- offset += matchEndIndex;
813
- line = line.substring(matchEndIndex);
895
+ let pos = 0;
896
+ for (const line of this.textEditor.state.doc.iterLines()) {
897
+ regexObject.lastIndex = 0;
898
+ for (;;) {
899
+ const match = regexObject.exec(line);
900
+ if (!match) {
901
+ break;
814
902
  }
815
- } while (match && line);
903
+ if (match[0].length) {
904
+ const from = pos + match.index;
905
+ ranges.push(new SearchMatch(from, from + match[0].length, match));
906
+ }
907
+ }
908
+ pos += line.length + 1;
816
909
  }
817
910
  return ranges;
818
911
  }
819
912
 
820
- populateLineGutterContextMenu(_contextMenu: UI.ContextMenu.ContextMenu, _editorLineNumber: number): Promise<void> {
821
- return Promise.resolve();
822
- }
823
-
824
- populateTextAreaContextMenu(
825
- _contextMenu: UI.ContextMenu.ContextMenu, _editorLineNumber: number, _editorColumnNumber: number): Promise<void> {
826
- return Promise.resolve();
827
- }
828
-
829
913
  canEditSource(): boolean {
830
914
  return this.editable;
831
915
  }
832
916
 
833
917
  private updateSourcePosition(): void {
834
- const selections = this.textEditorInternal.selections();
835
- if (!selections.length) {
918
+ const {textEditor} = this, {state} = textEditor, {selection} = state;
919
+ if (this.displayedSelection?.eq(selection)) {
836
920
  return;
837
921
  }
838
- if (selections.length > 1) {
839
- this.sourcePosition.setText(i18nString(UIStrings.dSelectionRegions, {PH1: selections.length}));
922
+ this.displayedSelection = selection;
923
+
924
+ if (selection.ranges.length > 1) {
925
+ this.sourcePosition.setText(i18nString(UIStrings.dSelectionRegions, {PH1: selection.ranges.length}));
840
926
  return;
841
927
  }
842
- let textRange: TextUtils.TextRange.TextRange = selections[0];
843
- if (textRange.isEmpty()) {
844
- const location = this.prettyToRawLocation(textRange.endLine, textRange.endColumn);
928
+ const {main} = state.selection;
929
+ if (main.empty) {
930
+ const {lineNumber, columnNumber} = textEditor.toLineColumn(main.head);
931
+ const location = this.prettyToRawLocation(lineNumber, columnNumber);
845
932
  if (this.wasmDisassemblyInternal) {
846
933
  const disassembly = this.wasmDisassemblyInternal;
847
934
  const lastBytecodeOffset = disassembly.lineNumberToBytecodeOffset(disassembly.lineNumbers - 1);
848
935
  const bytecodeOffsetDigits = lastBytecodeOffset.toString(16).length;
849
936
  const bytecodeOffset = disassembly.lineNumberToBytecodeOffset(location[0]);
850
-
851
937
  this.sourcePosition.setText(i18nString(
852
938
  UIStrings.bytecodePositionXs, {PH1: bytecodeOffset.toString(16).padStart(bytecodeOffsetDigits, '0')}));
853
939
  } else {
854
- if (!this.canEditSource()) {
855
- this.textEditorInternal.revealPosition(textRange.endLine, textRange.endColumn, true);
856
- }
857
940
  this.sourcePosition.setText(i18nString(UIStrings.lineSColumnS, {PH1: location[0] + 1, PH2: location[1] + 1}));
858
941
  }
859
- return;
860
- }
861
- textRange = textRange.normalize();
862
-
863
- const selectedText = this.textEditorInternal.text(textRange);
864
- if (textRange.startLine === textRange.endLine) {
865
- this.sourcePosition.setText(i18nString(UIStrings.dCharactersSelected, {PH1: selectedText.length}));
866
942
  } else {
867
- this.sourcePosition.setText(i18nString(
868
- UIStrings.dLinesDCharactersSelected,
869
- {PH1: textRange.endLine - textRange.startLine + 1, PH2: selectedText.length}));
943
+ const startLine = state.doc.lineAt(main.from), endLine = state.doc.lineAt(main.to);
944
+ if (startLine.number === endLine.number) {
945
+ this.sourcePosition.setText(i18nString(UIStrings.dCharactersSelected, {PH1: main.to - main.from}));
946
+ } else {
947
+ this.sourcePosition.setText(i18nString(
948
+ UIStrings.dLinesDCharactersSelected,
949
+ {PH1: endLine.number - startLine.number + 1, PH2: main.to - main.from}));
950
+ }
870
951
  }
871
952
  }
953
+
954
+ onContextMenu(event: MouseEvent): boolean {
955
+ event.consume(true); // Consume event now to prevent document from handling the async menu
956
+ const contextMenu = new UI.ContextMenu.ContextMenu(event);
957
+ const {state} = this.textEditor;
958
+ const pos = state.selection.main.from, line = state.doc.lineAt(pos);
959
+ this.populateTextAreaContextMenu(contextMenu, line.number - 1, pos - line.from);
960
+ contextMenu.appendApplicableItems(this);
961
+ contextMenu.show();
962
+ return true;
963
+ }
964
+
965
+ protected populateTextAreaContextMenu(_menu: UI.ContextMenu.ContextMenu, _lineNumber: number, _columnNumber: number):
966
+ void {
967
+ }
968
+
969
+ onLineGutterContextMenu(position: number, event: MouseEvent): boolean {
970
+ event.consume(true); // Consume event now to prevent document from handling the async menu
971
+ const contextMenu = new UI.ContextMenu.ContextMenu(event);
972
+ const lineNumber = this.textEditor.state.doc.lineAt(position).number - 1;
973
+ this.populateLineGutterContextMenu(contextMenu, lineNumber);
974
+ contextMenu.appendApplicableItems(this);
975
+ contextMenu.show();
976
+ return true;
977
+ }
978
+
979
+ protected populateLineGutterContextMenu(_menu: UI.ContextMenu.ContextMenu, _lineNumber: number): void {
980
+ }
981
+
982
+ focus(): void {
983
+ this.textEditor.focus();
984
+ }
872
985
  }
873
986
 
874
- export interface LineDecorator {
875
- decorate(uiSourceCode: Workspace.UISourceCode.UISourceCode, textEditor: SourcesTextEditor, type: string): void;
987
+ class SearchMatch {
988
+ constructor(readonly from: number, readonly to: number, readonly match: RegExpMatchArray) {
989
+ }
990
+
991
+ insertPlaceholders(replacement: string): string {
992
+ return replacement.replace(/\$(\$|&|\d+|<[^>]+>)/g, (_, selector): string => {
993
+ if (selector === '$') {
994
+ return '$';
995
+ }
996
+ if (selector === '&') {
997
+ return this.match[0];
998
+ }
999
+ if (selector[0] === '<') {
1000
+ return (this.match.groups && this.match.groups[selector.slice(1, selector.length - 1)]) || '';
1001
+ }
1002
+ return this.match[Number.parseInt(selector, 10)] || '';
1003
+ });
1004
+ }
876
1005
  }
877
1006
 
878
1007
  export interface Transformer {
879
- editorLocationToUILocation(lineNumber: number, columnNumber?: number): {
1008
+ editorLocationToUILocation(lineNumber: number, columnNumber: number): {
880
1009
  lineNumber: number,
881
- columnNumber?: number|undefined,
1010
+ columnNumber: number,
1011
+ };
1012
+ editorLocationToUILocation(lineNumber: number): {
1013
+ lineNumber: number,
1014
+ columnNumber: number|undefined,
882
1015
  };
883
1016
 
884
1017
  uiLocationToEditorLocation(lineNumber: number, columnNumber?: number): {
@@ -887,16 +1020,6 @@ export interface Transformer {
887
1020
  };
888
1021
  }
889
1022
 
890
- const registeredLineDecorators: LineDecoratorRegistration[] = [];
891
-
892
- export function registerLineDecorator(registration: LineDecoratorRegistration): void {
893
- registeredLineDecorators.push(registration);
894
- }
895
-
896
- export function getRegisteredLineDecorators(): LineDecoratorRegistration[] {
897
- return registeredLineDecorators;
898
- }
899
-
900
1023
  // TODO(crbug.com/1167717): Make this a const enum again
901
1024
  // eslint-disable-next-line rulesdir/const_enum
902
1025
  export enum DecoratorType {
@@ -905,7 +1028,134 @@ export enum DecoratorType {
905
1028
  COVERAGE = 'coverage',
906
1029
  }
907
1030
 
908
- export interface LineDecoratorRegistration {
909
- lineDecorator: () => LineDecorator;
910
- decoratorType: DecoratorType;
1031
+ const config = {
1032
+ editable: new CodeMirror.Compartment(),
1033
+ language: new CodeMirror.Compartment(),
1034
+ lineNumbers: new CodeMirror.Compartment(),
1035
+ };
1036
+
1037
+ class ActiveSearch {
1038
+ constructor(readonly regexp: RegExp, readonly currentRange: {from: number, to: number}|null) {
1039
+ }
1040
+
1041
+ map(change: CodeMirror.ChangeDesc): ActiveSearch {
1042
+ return change.empty || !this.currentRange ?
1043
+ this :
1044
+ new ActiveSearch(
1045
+ this.regexp, {from: change.mapPos(this.currentRange.from), to: change.mapPos(this.currentRange.to)});
1046
+ }
1047
+
1048
+ static eq(a: ActiveSearch|null, b: ActiveSearch|null): boolean {
1049
+ return Boolean(
1050
+ a === b ||
1051
+ a && b && a.currentRange?.from === b.currentRange?.from && a.currentRange?.to === b.currentRange?.to &&
1052
+ a.regexp.source === b.regexp.source && a.regexp.flags === b.regexp.flags);
1053
+ }
1054
+ }
1055
+
1056
+ const setActiveSearch = CodeMirror.StateEffect.define<ActiveSearch|null>(
1057
+ {map: (value, mapping): ActiveSearch | null => value && value.map(mapping)});
1058
+
1059
+ const activeSearchState = CodeMirror.StateField.define<ActiveSearch|null>({
1060
+ create(): null {
1061
+ return null;
1062
+ },
1063
+ update(state, tr): ActiveSearch |
1064
+ null {
1065
+ return tr.effects.reduce(
1066
+ (state, effect) => effect.is(setActiveSearch) ? effect.value : state, state && state.map(tr.changes));
1067
+ },
1068
+ });
1069
+
1070
+ const searchMatchDeco = CodeMirror.Decoration.mark({class: 'cm-searchMatch'});
1071
+ const currentSearchMatchDeco = CodeMirror.Decoration.mark({class: 'cm-searchMatch cm-searchMatch-selected'});
1072
+
1073
+ const searchHighlighter = CodeMirror.ViewPlugin.fromClass(class {
1074
+ decorations: CodeMirror.DecorationSet;
1075
+
1076
+ constructor(view: CodeMirror.EditorView) {
1077
+ this.decorations = this.computeDecorations(view);
1078
+ }
1079
+
1080
+ update(update: CodeMirror.ViewUpdate): void {
1081
+ const active = update.state.field(activeSearchState);
1082
+ if (!ActiveSearch.eq(active, update.startState.field(activeSearchState)) ||
1083
+ (active && (update.viewportChanged || update.docChanged))) {
1084
+ this.decorations = this.computeDecorations(update.view);
1085
+ }
1086
+ }
1087
+
1088
+ private computeDecorations(view: CodeMirror.EditorView): CodeMirror.DecorationSet {
1089
+ const active = view.state.field(activeSearchState);
1090
+ if (!active) {
1091
+ return CodeMirror.Decoration.none;
1092
+ }
1093
+
1094
+ const builder = new CodeMirror.RangeSetBuilder<CodeMirror.Decoration>();
1095
+ const {doc} = view.state;
1096
+ for (const {from, to} of view.visibleRanges) {
1097
+ let pos = from;
1098
+ for (const line of doc.iterLines(doc.lineAt(from).number, doc.lineAt(to).number + 1)) {
1099
+ active.regexp.lastIndex = 0;
1100
+ for (;;) {
1101
+ const match = active.regexp.exec(line);
1102
+ if (!match) {
1103
+ break;
1104
+ }
1105
+ const start = pos + match.index, end = start + match[0].length;
1106
+ const current = active.currentRange && active.currentRange.from === start && active.currentRange.to === end;
1107
+ builder.add(start, end, current ? currentSearchMatchDeco : searchMatchDeco);
1108
+ }
1109
+ pos += line.length + 1;
1110
+ }
1111
+ }
1112
+ return builder.finish();
1113
+ }
1114
+ }, {decorations: (value): CodeMirror.DecorationSet => value.decorations});
1115
+
1116
+ const nonBreakableLineMark = new (class extends CodeMirror.GutterMarker {
1117
+ elementClass = 'cm-nonBreakableLine';
1118
+ })();
1119
+
1120
+ // Effect to add lines (by position) to the set of non-breakable lines.
1121
+ export const addNonBreakableLines = CodeMirror.StateEffect.define<readonly number[]>();
1122
+
1123
+ const nonBreakableLines = CodeMirror.StateField.define<CodeMirror.RangeSet<CodeMirror.GutterMarker>>({
1124
+ create(): CodeMirror.RangeSet<CodeMirror.GutterMarker> {
1125
+ return CodeMirror.RangeSet.empty;
1126
+ },
1127
+ update(deco, tr): CodeMirror.RangeSet<CodeMirror.GutterMarker> {
1128
+ return tr.effects.reduce((deco, effect) => {
1129
+ return !effect.is(addNonBreakableLines) ?
1130
+ deco :
1131
+ deco.update({add: effect.value.map(pos => nonBreakableLineMark.range(pos))});
1132
+ }, deco.map(tr.changes));
1133
+ },
1134
+ provide: field => CodeMirror.lineNumberMarkers.from(field),
1135
+ });
1136
+
1137
+ export function isBreakableLine(state: CodeMirror.EditorState, line: CodeMirror.Line): boolean {
1138
+ const nonBreakable = state.field(nonBreakableLines);
1139
+ if (!nonBreakable.size) {
1140
+ return true;
1141
+ }
1142
+ let found = false;
1143
+ nonBreakable.between(line.from, line.from, () => {
1144
+ found = true;
1145
+ });
1146
+ return !found;
1147
+ }
1148
+
1149
+ function markNonBreakableLines(disassembly: Common.WasmDisassembly.WasmDisassembly): CodeMirror.Extension {
1150
+ // Mark non-breakable lines in the Wasm disassembly after setting
1151
+ // up the content for the text editor (which creates the gutter).
1152
+ return nonBreakableLines.init(state => {
1153
+ const marks = [];
1154
+ for (const lineNumber of disassembly.nonBreakableLineNumbers()) {
1155
+ if (lineNumber < state.doc.lines) {
1156
+ marks.push(nonBreakableLineMark.range(state.doc.line(lineNumber + 1).from));
1157
+ }
1158
+ }
1159
+ return CodeMirror.RangeSet.of(marks);
1160
+ });
911
1161
  }