index.js (25454B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 import PropTypes from "devtools/client/shared/vendor/react-prop-types"; 6 import React, { PureComponent } from "devtools/client/shared/vendor/react"; 7 import { div } from "devtools/client/shared/vendor/react-dom-factories"; 8 import { bindActionCreators } from "devtools/client/shared/vendor/redux"; 9 import ReactDOM from "devtools/client/shared/vendor/react-dom"; 10 import { connect } from "devtools/client/shared/vendor/react-redux"; 11 12 import { getLineText, isLineBlackboxed } from "./../../utils/source"; 13 import { createLocation } from "./../../utils/location"; 14 import { asSettled, isFulfilled, isRejected } from "../../utils/async-value"; 15 16 import { 17 getActiveSearch, 18 getSelectedLocation, 19 getSelectedSource, 20 getSelectedSourceTextContent, 21 getSelectedBreakableLines, 22 getConditionalPanelLocation, 23 getIsCurrentThreadPaused, 24 getSkipPausing, 25 getInlinePreview, 26 getBlackBoxRanges, 27 isSourceBlackBoxed, 28 getHighlightedLineRangeForSelectedSource, 29 isSourceMapIgnoreListEnabled, 30 isSourceOnSourceMapIgnoreList, 31 isMapScopesEnabled, 32 getSelectedTraceIndex, 33 getShouldScrollToSelectedLocation, 34 getShouldHighlightSelectedLocation, 35 getSelectedTraceLocation, 36 } from "../../selectors/index"; 37 38 // Redux actions 39 import actions from "../../actions/index"; 40 41 import SearchInFileBar from "./SearchInFileBar"; 42 import HighlightLines from "./HighlightLines"; 43 import Preview from "./Preview/index"; 44 import Breakpoints from "./Breakpoints"; 45 import ColumnBreakpoints from "./ColumnBreakpoints"; 46 import DebugLine from "./DebugLine"; 47 import HighlightLine from "./HighlightLine"; 48 import ConditionalPanel from "./ConditionalPanel"; 49 import TracePanel from "./TracePanel"; 50 import InlinePreviews from "./InlinePreviews"; 51 import Exceptions from "./Exceptions"; 52 53 import { 54 fromEditorLine, 55 getEditor, 56 removeEditor, 57 toSourceLine, 58 toEditorPosition, 59 onMouseOver, 60 } from "../../utils/editor/index"; 61 62 import { updateEditorSizeCssVariables } from "../../utils/ui"; 63 64 const { debounce } = require("resource://devtools/shared/debounce.js"); 65 const classnames = require("resource://devtools/client/shared/classnames.js"); 66 67 const { appinfo } = Services; 68 const isMacOS = appinfo.OS === "Darwin"; 69 70 function isSecondary(ev) { 71 return isMacOS && ev.ctrlKey && ev.button === 0; 72 } 73 74 function isCmd(ev) { 75 return isMacOS ? ev.metaKey : ev.ctrlKey; 76 } 77 78 const cssVars = { 79 searchbarHeight: "var(--editor-searchbar-height)", 80 }; 81 82 class Editor extends PureComponent { 83 static get propTypes() { 84 return { 85 selectedSource: PropTypes.object, 86 selectedSourceTextContent: PropTypes.object, 87 selectedSourceIsBlackBoxed: PropTypes.bool, 88 closeTabForSource: PropTypes.func.isRequired, 89 toggleBreakpointAtLine: PropTypes.func.isRequired, 90 conditionalPanelLocation: PropTypes.object, 91 closeConditionalPanel: PropTypes.func.isRequired, 92 openConditionalPanel: PropTypes.func.isRequired, 93 updateViewport: PropTypes.func.isRequired, 94 isPaused: PropTypes.bool.isRequired, 95 addBreakpointAtLine: PropTypes.func.isRequired, 96 continueToHere: PropTypes.func.isRequired, 97 jumpToMappedLocation: PropTypes.func.isRequired, 98 selectedLocation: PropTypes.object, 99 startPanelSize: PropTypes.number.isRequired, 100 endPanelSize: PropTypes.number.isRequired, 101 searchInFileEnabled: PropTypes.bool.isRequired, 102 inlinePreviewEnabled: PropTypes.bool.isRequired, 103 skipPausing: PropTypes.bool.isRequired, 104 blackboxedRanges: PropTypes.object.isRequired, 105 breakableLines: PropTypes.object.isRequired, 106 highlightedLineRange: PropTypes.object, 107 isSourceOnIgnoreList: PropTypes.bool, 108 isOriginalSourceAndMapScopesEnabled: PropTypes.bool, 109 shouldScrollToSelectedLocation: PropTypes.bool, 110 setInScopeLines: PropTypes.func, 111 }; 112 } 113 114 $editorWrapper; 115 constructor(props) { 116 super(props); 117 118 this.state = { 119 editor: null, 120 }; 121 } 122 123 // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507 124 UNSAFE_componentWillReceiveProps(nextProps) { 125 let { editor } = this.state; 126 const prevEditor = editor; 127 128 if (!editor) { 129 // See Bug 1913061 130 if (!nextProps.selectedSource) { 131 return; 132 } 133 editor = this.setupEditor(); 134 } 135 136 this.setTextContent(nextProps, editor, prevEditor); 137 } 138 139 async setTextContent(nextProps, editor, prevEditor) { 140 const shouldUpdateText = 141 nextProps.selectedSource !== this.props.selectedSource || 142 nextProps.selectedSourceTextContent?.value !== 143 this.props.selectedSourceTextContent?.value || 144 // If the selectedSource gets set before the editor get selected, make sure we update the text 145 prevEditor !== editor; 146 147 const shouldScroll = 148 nextProps.selectedLocation && 149 nextProps.shouldScrollToSelectedLocation && 150 this.shouldScrollToLocation(nextProps); 151 152 if (shouldUpdateText) { 153 await this.setText(nextProps, editor); 154 } 155 156 if (shouldScroll) { 157 await this.scrollToLocation(nextProps, editor); 158 } 159 // Note: Its important to get the scope lines after 160 // the scrolling is complete to make sure codemirror 161 // has loaded the content for the current viewport. 162 // 163 // Also if scope mapping is on, the babel parser worker 164 // will be used instead (for scope mapping) as the preview data relies 165 // on it for original variable mapping. 166 if (nextProps.isPaused && !nextProps.isOriginalSourceAndMapScopesEnabled) { 167 this.props.setInScopeLines(editor); 168 } 169 } 170 171 onEditorUpdated = viewUpdate => { 172 if (viewUpdate.docChanged || viewUpdate.geometryChanged) { 173 updateEditorSizeCssVariables(viewUpdate.view.dom); 174 this.props.updateViewport(); 175 } else if (viewUpdate.selectionSet) { 176 this.onCursorChange(); 177 } 178 }; 179 180 setupEditor() { 181 const editor = getEditor(); 182 183 // disables the default search shortcuts 184 editor._initShortcuts = () => {}; 185 186 const node = ReactDOM.findDOMNode(this); 187 const mountEl = node.querySelector(".editor-mount"); 188 if (node instanceof HTMLElement) { 189 editor.appendToLocalElement(mountEl); 190 } 191 192 editor.setUpdateListener(this.onEditorUpdated); 193 editor.setGutterEventListeners({ 194 click: (event, cm, line) => { 195 // Ignore clicks on the code folding button 196 if ( 197 event.target.className.includes("cm6-dt-foldgutter__toggle-button") 198 ) { 199 return; 200 } 201 // Clicking any where on the fold gutter (except on a code folding button) 202 // should toggle the breakpoint for this line, if possible. 203 if (event.target.className.includes("cm-foldGutter")) { 204 this.props.toggleBreakpointAtLine(line); 205 return; 206 } 207 this.onGutterClick(cm, line, null, event); 208 }, 209 contextmenu: (event, cm, line) => this.openMenu(event, line), 210 }); 211 editor.addEditorDOMEventListeners({ 212 click: (event, cm, line, column) => this.onClick(event, line, column), 213 contextmenu: (event, cm, line, column) => 214 this.openMenu(event, line, column), 215 mouseover: onMouseOver(editor), 216 }); 217 218 this.setState({ editor }); 219 // Used for tests 220 Object.defineProperty(window, "codeMirrorSourceEditorTestInstance", { 221 get() { 222 return editor; 223 }, 224 }); 225 return editor; 226 } 227 228 componentDidMount() { 229 const { shortcuts } = this.context; 230 231 shortcuts.on(L10N.getStr("toggleBreakpoint.key"), this.onToggleBreakpoint); 232 shortcuts.on( 233 L10N.getStr("toggleCondPanel.breakpoint.key"), 234 this.onToggleConditionalPanel 235 ); 236 shortcuts.on( 237 L10N.getStr("toggleCondPanel.logPoint.key"), 238 this.onToggleLogPanel 239 ); 240 shortcuts.on( 241 L10N.getStr("sourceTabs.closeTab.key"), 242 this.onCloseShortcutPress 243 ); 244 shortcuts.on("Esc", this.onEscape); 245 } 246 247 onCloseShortcutPress = e => { 248 const { selectedSource } = this.props; 249 if (selectedSource) { 250 e.preventDefault(); 251 e.stopPropagation(); 252 this.props.closeTabForSource(selectedSource); 253 } 254 }; 255 256 componentDidUpdate(prevProps, prevState) { 257 const { 258 selectedSource, 259 blackboxedRanges, 260 isSourceOnIgnoreList, 261 breakableLines, 262 } = this.props; 263 const { editor } = this.state; 264 265 if (!selectedSource || !editor) { 266 return; 267 } 268 269 const shouldUpdateBreakableLines = 270 prevProps.breakableLines.size !== this.props.breakableLines.size || 271 prevProps.selectedSource?.id !== selectedSource.id || 272 // Make sure we update after the editor has loaded 273 (!prevState.editor && !!editor); 274 275 if (shouldUpdateBreakableLines) { 276 editor.setLineGutterMarkers([ 277 { 278 id: editor.markerTypes.EMPTY_LINE_MARKER, 279 lineClassName: "empty-line", 280 condition: line => { 281 const lineNumber = fromEditorLine(selectedSource, line); 282 return !breakableLines.has(lineNumber); 283 }, 284 }, 285 ]); 286 } 287 288 editor.setLineGutterMarkers([ 289 { 290 id: editor.markerTypes.BLACKBOX_LINE_GUTTER_MARKER, 291 lineClassName: "blackboxed-line", 292 condition: line => { 293 const lineNumber = fromEditorLine(selectedSource, line); 294 return isLineBlackboxed( 295 blackboxedRanges[selectedSource.url], 296 lineNumber, 297 isSourceOnIgnoreList 298 ); 299 }, 300 }, 301 ]); 302 303 if ( 304 prevProps.selectedSource?.id !== selectedSource.id || 305 prevProps.blackboxedRanges[selectedSource.url]?.length !== 306 blackboxedRanges[selectedSource.url]?.length || 307 (!prevState.editor && !!editor) 308 ) { 309 if (blackboxedRanges[selectedSource.url] == undefined) { 310 editor.removeLineContentMarker(editor.markerTypes.BLACKBOX_LINE_MARKER); 311 return; 312 } 313 314 const lines = []; 315 for (const range of blackboxedRanges[selectedSource.url]) { 316 for (let line = range.start.line; line <= range.end.line; line++) { 317 lines.push({ line }); 318 } 319 } 320 321 editor.setLineContentMarker({ 322 id: editor.markerTypes.BLACKBOX_LINE_MARKER, 323 lineClassName: "blackboxed-line", 324 // If the the whole source is blackboxed, lets just mark all positions. 325 shouldMarkAllLines: !blackboxedRanges[selectedSource.url].length, 326 lines, 327 }); 328 } 329 } 330 331 componentWillUnmount() { 332 const { editor } = this.state; 333 const { shortcuts } = this.context; 334 shortcuts.off(L10N.getStr("sourceTabs.closeTab.key")); 335 shortcuts.off(L10N.getStr("toggleBreakpoint.key")); 336 shortcuts.off(L10N.getStr("toggleCondPanel.breakpoint.key")); 337 shortcuts.off(L10N.getStr("toggleCondPanel.logPoint.key")); 338 339 if (this.abortController) { 340 this.abortController.abort(); 341 this.abortController = null; 342 } 343 344 if (editor) { 345 editor.destroy(); 346 this.setState({ editor: null }); 347 removeEditor(); 348 } 349 } 350 351 getCurrentPosition() { 352 const { editor } = this.state; 353 const { selectedLocation } = this.props; 354 if (!selectedLocation) { 355 return null; 356 } 357 let { line, column } = selectedLocation; 358 // When no specific line has been selected, fallback to the current cursor posiiton 359 if (line == 0) { 360 const selectionCursor = editor.getSelectionCursor(); 361 line = toSourceLine(selectedLocation.source, selectionCursor.from.line); 362 column = selectionCursor.from.ch; 363 } 364 return { line, column }; 365 } 366 367 onToggleBreakpoint = e => { 368 e.preventDefault(); 369 e.stopPropagation(); 370 371 const currentPosition = this.getCurrentPosition(); 372 if (!currentPosition || typeof currentPosition.line !== "number") { 373 return; 374 } 375 376 this.props.toggleBreakpointAtLine(currentPosition.line); 377 }; 378 379 onToggleLogPanel = e => { 380 e.stopPropagation(); 381 e.preventDefault(); 382 this.toggleBreakpointPanel(true); 383 }; 384 385 onToggleConditionalPanel = e => { 386 e.stopPropagation(); 387 e.preventDefault(); 388 this.toggleBreakpointPanel(false); 389 }; 390 391 toggleBreakpointPanel(logPanel) { 392 const { 393 conditionalPanelLocation, 394 closeConditionalPanel, 395 openConditionalPanel, 396 selectedSource, 397 } = this.props; 398 399 const currentPosition = this.getCurrentPosition(); 400 401 if (conditionalPanelLocation) { 402 return closeConditionalPanel(); 403 } 404 405 if (!selectedSource || typeof currentPosition?.line !== "number") { 406 return null; 407 } 408 409 return openConditionalPanel( 410 createLocation({ 411 line: currentPosition.line, 412 column: currentPosition.column, 413 source: selectedSource, 414 }), 415 logPanel 416 ); 417 } 418 419 onEditorScroll = debounce(this.props.updateViewport, 75); 420 421 /* 422 * The default Esc command is overridden in the CodeMirror keymap to allow 423 * the Esc keypress event to be catched by the toolbox and trigger the 424 * split console. Restore it here, but preventDefault if and only if there 425 * is a multiselection. 426 */ 427 onEscape() {} 428 429 // Note: The line is optional, if not passed it fallsback to lineAtHeight. 430 openMenu(event, line, ch) { 431 event.stopPropagation(); 432 event.preventDefault(); 433 434 const { 435 selectedSource, 436 selectedSourceTextContent, 437 conditionalPanelLocation, 438 closeConditionalPanel, 439 } = this.props; 440 441 const { editor } = this.state; 442 443 if (!selectedSource || !editor) { 444 return; 445 } 446 447 // only allow one conditionalPanel location. 448 if (conditionalPanelLocation) { 449 closeConditionalPanel(); 450 } 451 452 const target = event.target; 453 const { id: sourceId } = selectedSource; 454 455 if (typeof line != "number") { 456 return; 457 } 458 459 if ( 460 target.classList.contains("cm-gutter") || 461 target.classList.contains("cm-gutterElement") 462 ) { 463 const location = createLocation({ 464 line, 465 column: undefined, 466 source: selectedSource, 467 }); 468 469 const lineText = getLineText( 470 sourceId, 471 selectedSourceTextContent, 472 line 473 ).trim(); 474 475 const lineObject = { from: { line, ch }, to: { line, ch } }; 476 477 this.props.showEditorGutterContextMenu( 478 event, 479 lineObject, 480 location, 481 lineText 482 ); 483 return; 484 } 485 486 if (target.getAttribute("id") === "columnmarker") { 487 return; 488 } 489 490 const location = createLocation({ 491 source: selectedSource, 492 line: fromEditorLine(selectedSource, line), 493 column: editor.isWasm ? 0 : ch, 494 }); 495 496 const lineObject = editor.getSelectionCursor(); 497 this.props.showEditorContextMenu(event, editor, lineObject, location); 498 } 499 500 /** 501 * CodeMirror event handler, called whenever the cursor moves 502 * for user-driven or programatic reasons. 503 */ 504 onCursorChange = () => { 505 const { editor } = this.state; 506 if (!editor || !this.props.selectedSource) { 507 return; 508 } 509 const { selectedLocation } = this.props; 510 const selectionCursor = editor.getSelectionCursor(); 511 const { line, ch } = selectionCursor.to; 512 513 // Avoid triggering an infinite loop of select location action 514 // if we moved the cursor to the location already stored in redux 515 // 516 // About columns: consider the reducer's column set to undefined as equivalent to selecting column 0 in CodeMirror. 517 // This also avoids dispatching duplicated `selectLocation` actions when selecting a line without a specific column. 518 if ( 519 selectedLocation.line == line && 520 ((typeof selectedLocation.column != "number" && ch == 0) || 521 selectedLocation.column == ch) 522 ) { 523 return; 524 } 525 526 this.props.selectLocation( 527 createLocation({ 528 source: this.props.selectedSource, 529 line: toSourceLine(this.props.selectedSource, line), 530 column: ch, 531 }), 532 { 533 // Reset the context, so that we don't switch to original 534 // while moving the cursor within a bundle 535 keepContext: false, 536 537 // Avoid highlighting the selected line 538 highlight: false, 539 540 // Avoid scrolling to the selected line, it's already visible 541 scroll: false, 542 } 543 ); 544 }; 545 546 onGutterClick = (cm, line, gutter, ev) => { 547 const { 548 selectedSource, 549 conditionalPanelLocation, 550 closeConditionalPanel, 551 addBreakpointAtLine, 552 continueToHere, 553 breakableLines, 554 blackboxedRanges, 555 isSourceOnIgnoreList, 556 } = this.props; 557 558 // ignore right clicks in the gutter 559 if (isSecondary(ev) || ev.button === 2 || !selectedSource) { 560 return; 561 } 562 563 if (conditionalPanelLocation) { 564 closeConditionalPanel(); 565 return; 566 } 567 568 if (gutter === "CodeMirror-foldgutter") { 569 return; 570 } 571 572 const sourceLine = toSourceLine(selectedSource, line); 573 if (typeof sourceLine !== "number") { 574 return; 575 } 576 577 // ignore clicks on a non-breakable line 578 if (!breakableLines.has(sourceLine)) { 579 return; 580 } 581 582 if (isCmd(ev)) { 583 continueToHere( 584 createLocation({ 585 line: sourceLine, 586 column: undefined, 587 source: selectedSource, 588 }) 589 ); 590 return; 591 } 592 593 addBreakpointAtLine( 594 sourceLine, 595 ev.altKey, 596 ev.shiftKey || 597 isLineBlackboxed( 598 blackboxedRanges[selectedSource.url], 599 sourceLine, 600 isSourceOnIgnoreList 601 ) 602 ); 603 }; 604 605 onClick(e, line, ch) { 606 const { selectedSource, jumpToMappedLocation } = this.props; 607 608 if (!selectedSource) { 609 return; 610 } 611 612 const sourceLocation = createLocation({ 613 source: selectedSource, 614 line: fromEditorLine(selectedSource, line), 615 column: this.state.editor.isWasm ? 0 : ch, 616 }); 617 618 if (e.metaKey && e.altKey) { 619 jumpToMappedLocation(sourceLocation); 620 } 621 } 622 623 shouldScrollToLocation(nextProps) { 624 if ( 625 !nextProps.selectedLocation?.line || 626 !nextProps.selectedSourceTextContent 627 ) { 628 return false; 629 } 630 631 const { selectedLocation, selectedSourceTextContent } = this.props; 632 const contentChanged = 633 !selectedSourceTextContent?.value && 634 nextProps.selectedSourceTextContent?.value; 635 const locationChanged = selectedLocation !== nextProps.selectedLocation; 636 637 return contentChanged || locationChanged; 638 } 639 640 async scrollToLocation(nextProps, editor) { 641 const { selectedLocation } = nextProps; 642 const { line, column } = toEditorPosition(selectedLocation); 643 await editor.scrollTo(line, column); 644 645 // Prevent focusing codemirror if we don't want to highlight the line 646 // mostly to avoid changing the focus from where it currently is. 647 // For example in the Search Bar. 648 if (this.props.shouldHighlightSelectedLocation) { 649 editor.focus(); 650 } 651 // This should also scroll the editor to the specified position 652 await editor.setCursorAt(line, column); 653 } 654 655 async setText(props, editor) { 656 const { selectedSource, selectedSourceTextContent } = props; 657 658 if (!editor) { 659 return; 660 } 661 662 // check if we previously had a selected source 663 if (!selectedSource) { 664 return; 665 } 666 667 if (!selectedSourceTextContent?.value) { 668 this.showLoadingMessage(editor); 669 return; 670 } 671 672 if (isRejected(selectedSourceTextContent)) { 673 let { value } = selectedSourceTextContent; 674 if (typeof value !== "string") { 675 value = "Unexpected source error"; 676 } 677 678 this.showErrorMessage(value); 679 return; 680 } 681 await editor.setText(selectedSourceTextContent.value.value, { 682 documentId: selectedSource.id, 683 }); 684 } 685 686 showErrorMessage(msg) { 687 const { editor } = this.state; 688 if (!editor) { 689 return; 690 } 691 692 let error; 693 if (msg.includes("WebAssembly binary source is not available")) { 694 error = L10N.getStr("wasmIsNotAvailable"); 695 } else { 696 error = L10N.getFormatStr("errorLoadingText3", msg); 697 } 698 editor.setText(error); 699 } 700 701 showLoadingMessage(editor) { 702 editor.setText(L10N.getStr("loadingText")); 703 } 704 705 getInlineEditorStyles() { 706 const { searchInFileEnabled } = this.props; 707 708 if (searchInFileEnabled) { 709 return { 710 height: `calc(100% - ${cssVars.searchbarHeight})`, 711 }; 712 } 713 714 return { 715 height: "100%", 716 }; 717 } 718 719 // eslint-disable-next-line complexity 720 renderItems() { 721 const { 722 selectedSource, 723 conditionalPanelLocation, 724 isPaused, 725 isTraceSelected, 726 inlinePreviewEnabled, 727 highlightedLineRange, 728 isOriginalSourceAndMapScopesEnabled, 729 selectedSourceTextContent, 730 selectedTraceLocation, 731 } = this.props; 732 const { editor } = this.state; 733 734 if (!selectedSource || !editor) { 735 return null; 736 } 737 738 // Only load the sub components if the content has loaded without issues. 739 if (selectedSourceTextContent && !isFulfilled(selectedSourceTextContent)) { 740 return null; 741 } 742 743 return React.createElement( 744 React.Fragment, 745 null, 746 React.createElement(Breakpoints, { editor }), 747 selectedTraceLocation 748 ? React.createElement(TracePanel, { 749 editor, 750 }) 751 : null, 752 (isPaused || isTraceSelected) && 753 selectedSource.isOriginal && 754 !selectedSource.isPrettyPrinted && 755 !isOriginalSourceAndMapScopesEnabled 756 ? null 757 : React.createElement(Preview, { 758 editor, 759 editorRef: this.$editorWrapper, 760 }), 761 React.createElement(DebugLine, { editor, selectedSource }), 762 React.createElement(HighlightLine, { editor }), 763 React.createElement(Exceptions, { editor }), 764 conditionalPanelLocation 765 ? React.createElement(ConditionalPanel, { 766 editor, 767 selectedSource, 768 }) 769 : null, 770 (isPaused || isTraceSelected) && 771 inlinePreviewEnabled && 772 (!selectedSource.isOriginal || 773 selectedSource.isPrettyPrinted || 774 isOriginalSourceAndMapScopesEnabled) 775 ? React.createElement(InlinePreviews, { 776 editor, 777 }) 778 : null, 779 highlightedLineRange 780 ? React.createElement(HighlightLines, { 781 editor, 782 range: highlightedLineRange, 783 }) 784 : null, 785 React.createElement(ColumnBreakpoints, { 786 editor, 787 }) 788 ); 789 } 790 791 renderSearchInFileBar() { 792 if (!this.props.selectedSource) { 793 return null; 794 } 795 return React.createElement(SearchInFileBar, { 796 editor: this.state.editor, 797 }); 798 } 799 800 render() { 801 const { selectedSourceIsBlackBoxed, skipPausing } = this.props; 802 return div( 803 { 804 className: classnames("editor-wrapper", { 805 blackboxed: selectedSourceIsBlackBoxed, 806 "skip-pausing": skipPausing, 807 }), 808 ref: c => (this.$editorWrapper = c), 809 }, 810 div({ 811 className: "editor-mount devtools-monospace", 812 style: this.getInlineEditorStyles(), 813 }), 814 this.renderSearchInFileBar(), 815 this.renderItems() 816 ); 817 } 818 } 819 820 Editor.contextTypes = { 821 shortcuts: PropTypes.object, 822 }; 823 824 const mapStateToProps = state => { 825 const selectedSource = getSelectedSource(state); 826 const selectedLocation = getSelectedLocation(state); 827 828 const selectedSourceTextContent = getSelectedSourceTextContent(state); 829 830 return { 831 selectedLocation, 832 selectedSource, 833 // Settled means the content loaded succesfully (fulfilled) or the there was 834 // error (rejected) 835 selectedSourceTextContent: asSettled(selectedSourceTextContent), 836 selectedSourceIsBlackBoxed: selectedSource 837 ? isSourceBlackBoxed(state, selectedSource) 838 : null, 839 isSourceOnIgnoreList: 840 isSourceMapIgnoreListEnabled(state) && 841 isSourceOnSourceMapIgnoreList(state, selectedSource), 842 searchInFileEnabled: getActiveSearch(state) === "file", 843 conditionalPanelLocation: getConditionalPanelLocation(state), 844 isPaused: getIsCurrentThreadPaused(state), 845 isTraceSelected: getSelectedTraceIndex(state) != null, 846 skipPausing: getSkipPausing(state), 847 inlinePreviewEnabled: getInlinePreview(state), 848 blackboxedRanges: getBlackBoxRanges(state), 849 breakableLines: getSelectedBreakableLines(state), 850 highlightedLineRange: getHighlightedLineRangeForSelectedSource(state), 851 isOriginalSourceAndMapScopesEnabled: 852 selectedSource?.isOriginal && isMapScopesEnabled(state), 853 shouldScrollToSelectedLocation: getShouldScrollToSelectedLocation(state), 854 shouldHighlightSelectedLocation: getShouldHighlightSelectedLocation(state), 855 selectedTraceLocation: getSelectedTraceLocation(state), 856 }; 857 }; 858 859 const mapDispatchToProps = dispatch => ({ 860 ...bindActionCreators( 861 { 862 openConditionalPanel: actions.openConditionalPanel, 863 closeConditionalPanel: actions.closeConditionalPanel, 864 continueToHere: actions.continueToHere, 865 toggleBreakpointAtLine: actions.toggleBreakpointAtLine, 866 addBreakpointAtLine: actions.addBreakpointAtLine, 867 jumpToMappedLocation: actions.jumpToMappedLocation, 868 updateViewport: actions.updateViewport, 869 closeTabForSource: actions.closeTabForSource, 870 showEditorContextMenu: actions.showEditorContextMenu, 871 showEditorGutterContextMenu: actions.showEditorGutterContextMenu, 872 selectLocation: actions.selectLocation, 873 setInScopeLines: actions.setInScopeLines, 874 }, 875 dispatch 876 ), 877 }); 878 879 export default connect(mapStateToProps, mapDispatchToProps)(Editor);