11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ciimport {App} from '../index.mjs' 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciimport {SelectionEvent, SelectRelatedEvent, ToolTipEvent} from './events.mjs'; 71cb0ef41Sopenharmony_ciimport {arrayEquals, CollapsableElement, CSSColor, defer, delay, DOM, formatBytes, gradientStopsFromGroups, groupBy, LazyTable} from './helper.mjs'; 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci// A source mapping proxy for source maps that don't have CORS headers. 101cb0ef41Sopenharmony_ci// TODO(leszeks): Make this configurable. 111cb0ef41Sopenharmony_ciconst sourceMapFetchPrefix = 'http://localhost:8080/'; 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ciDOM.defineCustomElement('view/script-panel', 141cb0ef41Sopenharmony_ci (templateText) => 151cb0ef41Sopenharmony_ci class SourcePanel extends CollapsableElement { 161cb0ef41Sopenharmony_ci _selectedSourcePositions = []; 171cb0ef41Sopenharmony_ci _sourcePositionsToMarkNodesPromise = defer(); 181cb0ef41Sopenharmony_ci _scripts = []; 191cb0ef41Sopenharmony_ci _script; 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci showToolTipEntriesHandler = this.handleShowToolTipEntries.bind(this); 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ci constructor() { 241cb0ef41Sopenharmony_ci super(templateText); 251cb0ef41Sopenharmony_ci this.scriptDropdown.addEventListener( 261cb0ef41Sopenharmony_ci 'change', e => this._handleSelectScript(e)); 271cb0ef41Sopenharmony_ci this.$('#selectedRelatedButton').onclick = 281cb0ef41Sopenharmony_ci this._handleSelectRelated.bind(this); 291cb0ef41Sopenharmony_ci } 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ci get script() { 321cb0ef41Sopenharmony_ci return this.$('#script'); 331cb0ef41Sopenharmony_ci } 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci get scriptNode() { 361cb0ef41Sopenharmony_ci return this.$('.scriptNode'); 371cb0ef41Sopenharmony_ci } 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci set script(script) { 401cb0ef41Sopenharmony_ci if (this._script === script) return; 411cb0ef41Sopenharmony_ci this._script = script; 421cb0ef41Sopenharmony_ci script.ensureSourceMapCalculated(sourceMapFetchPrefix); 431cb0ef41Sopenharmony_ci this._sourcePositionsToMarkNodesPromise = defer(); 441cb0ef41Sopenharmony_ci this._selectedSourcePositions = 451cb0ef41Sopenharmony_ci this._selectedSourcePositions.filter(each => each.script === script); 461cb0ef41Sopenharmony_ci this.requestUpdate(); 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci set focusedSourcePositions(sourcePositions) { 501cb0ef41Sopenharmony_ci this.selectedSourcePositions = sourcePositions; 511cb0ef41Sopenharmony_ci } 521cb0ef41Sopenharmony_ci 531cb0ef41Sopenharmony_ci set selectedSourcePositions(sourcePositions) { 541cb0ef41Sopenharmony_ci if (arrayEquals(this._selectedSourcePositions, sourcePositions)) { 551cb0ef41Sopenharmony_ci this._focusSelectedMarkers(0); 561cb0ef41Sopenharmony_ci } else { 571cb0ef41Sopenharmony_ci this._selectedSourcePositions = sourcePositions; 581cb0ef41Sopenharmony_ci // TODO: highlight multiple scripts 591cb0ef41Sopenharmony_ci this.script = sourcePositions[0]?.script; 601cb0ef41Sopenharmony_ci this._focusSelectedMarkers(100); 611cb0ef41Sopenharmony_ci } 621cb0ef41Sopenharmony_ci } 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci set scripts(scripts) { 651cb0ef41Sopenharmony_ci this._scripts = scripts; 661cb0ef41Sopenharmony_ci this._initializeScriptDropdown(); 671cb0ef41Sopenharmony_ci } 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci get scriptDropdown() { 701cb0ef41Sopenharmony_ci return this.$('#script-dropdown'); 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci _update() { 741cb0ef41Sopenharmony_ci this._renderSourcePanel(); 751cb0ef41Sopenharmony_ci this._updateScriptDropdownSelection(); 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci _initializeScriptDropdown() { 791cb0ef41Sopenharmony_ci this._scripts.sort((a, b) => a.name?.localeCompare(b.name) ?? 0); 801cb0ef41Sopenharmony_ci let select = this.scriptDropdown; 811cb0ef41Sopenharmony_ci select.options.length = 0; 821cb0ef41Sopenharmony_ci for (const script of this._scripts) { 831cb0ef41Sopenharmony_ci const option = document.createElement('option'); 841cb0ef41Sopenharmony_ci const size = formatBytes(script.source.length); 851cb0ef41Sopenharmony_ci option.text = `${script.name} (id=${script.id} size=${size})`; 861cb0ef41Sopenharmony_ci option.script = script; 871cb0ef41Sopenharmony_ci select.add(option); 881cb0ef41Sopenharmony_ci } 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci _updateScriptDropdownSelection() { 921cb0ef41Sopenharmony_ci this.scriptDropdown.selectedIndex = 931cb0ef41Sopenharmony_ci this._script ? this._scripts.indexOf(this._script) : -1; 941cb0ef41Sopenharmony_ci } 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci async _renderSourcePanel() { 971cb0ef41Sopenharmony_ci let scriptNode; 981cb0ef41Sopenharmony_ci const script = this._script; 991cb0ef41Sopenharmony_ci if (script) { 1001cb0ef41Sopenharmony_ci await delay(1); 1011cb0ef41Sopenharmony_ci if (script != this._script) return; 1021cb0ef41Sopenharmony_ci const builder = new LineBuilder(this, this._script); 1031cb0ef41Sopenharmony_ci scriptNode = await builder.createScriptNode(this._script.startLine); 1041cb0ef41Sopenharmony_ci if (script != this._script) return; 1051cb0ef41Sopenharmony_ci this._sourcePositionsToMarkNodesPromise.resolve( 1061cb0ef41Sopenharmony_ci builder.sourcePositionToMarkers); 1071cb0ef41Sopenharmony_ci } else { 1081cb0ef41Sopenharmony_ci scriptNode = DOM.div(); 1091cb0ef41Sopenharmony_ci this._selectedMarkNodes = undefined; 1101cb0ef41Sopenharmony_ci this._sourcePositionsToMarkNodesPromise.resolve(new Map()); 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci const oldScriptNode = this.script.childNodes[1]; 1131cb0ef41Sopenharmony_ci this.script.replaceChild(scriptNode, oldScriptNode); 1141cb0ef41Sopenharmony_ci } 1151cb0ef41Sopenharmony_ci 1161cb0ef41Sopenharmony_ci async _focusSelectedMarkers(delay_ms) { 1171cb0ef41Sopenharmony_ci if (delay_ms) await delay(delay_ms); 1181cb0ef41Sopenharmony_ci const sourcePositionsToMarkNodes = 1191cb0ef41Sopenharmony_ci await this._sourcePositionsToMarkNodesPromise; 1201cb0ef41Sopenharmony_ci // Remove all marked nodes. 1211cb0ef41Sopenharmony_ci for (let markNode of sourcePositionsToMarkNodes.values()) { 1221cb0ef41Sopenharmony_ci markNode.className = ''; 1231cb0ef41Sopenharmony_ci } 1241cb0ef41Sopenharmony_ci for (let sourcePosition of this._selectedSourcePositions) { 1251cb0ef41Sopenharmony_ci if (sourcePosition.script !== this._script) continue; 1261cb0ef41Sopenharmony_ci sourcePositionsToMarkNodes.get(sourcePosition).className = 'marked'; 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci this._scrollToFirstSourcePosition(sourcePositionsToMarkNodes) 1291cb0ef41Sopenharmony_ci } 1301cb0ef41Sopenharmony_ci 1311cb0ef41Sopenharmony_ci _scrollToFirstSourcePosition(sourcePositionsToMarkNodes) { 1321cb0ef41Sopenharmony_ci const sourcePosition = this._selectedSourcePositions.find( 1331cb0ef41Sopenharmony_ci each => each.script === this._script); 1341cb0ef41Sopenharmony_ci if (!sourcePosition) return; 1351cb0ef41Sopenharmony_ci const markNode = sourcePositionsToMarkNodes.get(sourcePosition); 1361cb0ef41Sopenharmony_ci markNode.scrollIntoView( 1371cb0ef41Sopenharmony_ci {behavior: 'smooth', block: 'center', inline: 'center'}); 1381cb0ef41Sopenharmony_ci } 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci _handleSelectScript(e) { 1411cb0ef41Sopenharmony_ci const option = 1421cb0ef41Sopenharmony_ci this.scriptDropdown.options[this.scriptDropdown.selectedIndex]; 1431cb0ef41Sopenharmony_ci this.script = option.script; 1441cb0ef41Sopenharmony_ci } 1451cb0ef41Sopenharmony_ci 1461cb0ef41Sopenharmony_ci _handleSelectRelated(e) { 1471cb0ef41Sopenharmony_ci if (!this._script) return; 1481cb0ef41Sopenharmony_ci this.dispatchEvent(new SelectRelatedEvent(this._script)); 1491cb0ef41Sopenharmony_ci } 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci setSelectedSourcePositionInternal(sourcePosition) { 1521cb0ef41Sopenharmony_ci this._selectedSourcePositions = [sourcePosition]; 1531cb0ef41Sopenharmony_ci console.assert(sourcePosition.script === this._script); 1541cb0ef41Sopenharmony_ci } 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci handleSourcePositionClick(e) { 1571cb0ef41Sopenharmony_ci const sourcePosition = e.target.sourcePosition; 1581cb0ef41Sopenharmony_ci this.setSelectedSourcePositionInternal(sourcePosition); 1591cb0ef41Sopenharmony_ci this.dispatchEvent(new SelectRelatedEvent(sourcePosition)); 1601cb0ef41Sopenharmony_ci } 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci handleSourcePositionMouseOver(e) { 1631cb0ef41Sopenharmony_ci const sourcePosition = e.target.sourcePosition; 1641cb0ef41Sopenharmony_ci const entries = sourcePosition.entries; 1651cb0ef41Sopenharmony_ci const toolTipContent = DOM.div(); 1661cb0ef41Sopenharmony_ci toolTipContent.appendChild( 1671cb0ef41Sopenharmony_ci new ToolTipTableBuilder(this, entries).tableNode); 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci let sourceMapContent; 1701cb0ef41Sopenharmony_ci switch (this._script.sourceMapState) { 1711cb0ef41Sopenharmony_ci case 'loaded': { 1721cb0ef41Sopenharmony_ci const originalPosition = sourcePosition.originalPosition; 1731cb0ef41Sopenharmony_ci if (originalPosition.source === null) { 1741cb0ef41Sopenharmony_ci sourceMapContent = 1751cb0ef41Sopenharmony_ci DOM.element('i', {textContent: 'no source mapping for location'}); 1761cb0ef41Sopenharmony_ci } else { 1771cb0ef41Sopenharmony_ci sourceMapContent = DOM.element('a', { 1781cb0ef41Sopenharmony_ci href: `${originalPosition.source}`, 1791cb0ef41Sopenharmony_ci target: '_blank', 1801cb0ef41Sopenharmony_ci textContent: `${originalPosition.source}:${originalPosition.line}:${ 1811cb0ef41Sopenharmony_ci originalPosition.column}` 1821cb0ef41Sopenharmony_ci }); 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci break; 1851cb0ef41Sopenharmony_ci } 1861cb0ef41Sopenharmony_ci case 'loading': 1871cb0ef41Sopenharmony_ci sourceMapContent = 1881cb0ef41Sopenharmony_ci DOM.element('i', {textContent: 'source map still loading...'}); 1891cb0ef41Sopenharmony_ci break; 1901cb0ef41Sopenharmony_ci case 'failed': 1911cb0ef41Sopenharmony_ci sourceMapContent = 1921cb0ef41Sopenharmony_ci DOM.element('i', {textContent: 'source map failed to load'}); 1931cb0ef41Sopenharmony_ci break; 1941cb0ef41Sopenharmony_ci case 'none': 1951cb0ef41Sopenharmony_ci sourceMapContent = DOM.element('i', {textContent: 'no source map'}); 1961cb0ef41Sopenharmony_ci break; 1971cb0ef41Sopenharmony_ci default: 1981cb0ef41Sopenharmony_ci break; 1991cb0ef41Sopenharmony_ci } 2001cb0ef41Sopenharmony_ci toolTipContent.appendChild(sourceMapContent); 2011cb0ef41Sopenharmony_ci this.dispatchEvent(new ToolTipEvent(toolTipContent, e.target)); 2021cb0ef41Sopenharmony_ci } 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci handleShowToolTipEntries(event) { 2051cb0ef41Sopenharmony_ci let entries = event.currentTarget.data; 2061cb0ef41Sopenharmony_ci const sourcePosition = entries[0].sourcePosition; 2071cb0ef41Sopenharmony_ci // Add a source position entry so the current position stays focused. 2081cb0ef41Sopenharmony_ci this.setSelectedSourcePositionInternal(sourcePosition); 2091cb0ef41Sopenharmony_ci entries = entries.concat(this._selectedSourcePositions); 2101cb0ef41Sopenharmony_ci this.dispatchEvent(new SelectionEvent(entries)); 2111cb0ef41Sopenharmony_ci } 2121cb0ef41Sopenharmony_ci}); 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ciclass ToolTipTableBuilder { 2151cb0ef41Sopenharmony_ci constructor(scriptPanel, entries) { 2161cb0ef41Sopenharmony_ci this._scriptPanel = scriptPanel; 2171cb0ef41Sopenharmony_ci this.tableNode = DOM.table(); 2181cb0ef41Sopenharmony_ci const tr = DOM.tr(); 2191cb0ef41Sopenharmony_ci tr.appendChild(DOM.td('Type')); 2201cb0ef41Sopenharmony_ci tr.appendChild(DOM.td('Subtype')); 2211cb0ef41Sopenharmony_ci tr.appendChild(DOM.td('Count')); 2221cb0ef41Sopenharmony_ci this.tableNode.appendChild(document.createElement('thead')).appendChild(tr); 2231cb0ef41Sopenharmony_ci groupBy(entries, each => each.constructor, true).forEach(group => { 2241cb0ef41Sopenharmony_ci this.addRow(group.key.name, 'all', entries, false) 2251cb0ef41Sopenharmony_ci groupBy(group.entries, each => each.type, true).forEach(group => { 2261cb0ef41Sopenharmony_ci this.addRow('', group.key, group.entries, false) 2271cb0ef41Sopenharmony_ci }) 2281cb0ef41Sopenharmony_ci }) 2291cb0ef41Sopenharmony_ci } 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci addRow(name, subtypeName, entries) { 2321cb0ef41Sopenharmony_ci const tr = DOM.tr(); 2331cb0ef41Sopenharmony_ci tr.appendChild(DOM.td(name)); 2341cb0ef41Sopenharmony_ci tr.appendChild(DOM.td(subtypeName)); 2351cb0ef41Sopenharmony_ci tr.appendChild(DOM.td(entries.length)); 2361cb0ef41Sopenharmony_ci const button = 2371cb0ef41Sopenharmony_ci DOM.button('Show', this._scriptPanel.showToolTipEntriesHandler); 2381cb0ef41Sopenharmony_ci button.data = entries; 2391cb0ef41Sopenharmony_ci tr.appendChild(DOM.td(button)); 2401cb0ef41Sopenharmony_ci this.tableNode.appendChild(tr); 2411cb0ef41Sopenharmony_ci } 2421cb0ef41Sopenharmony_ci} 2431cb0ef41Sopenharmony_ci 2441cb0ef41Sopenharmony_ciclass SourcePositionIterator { 2451cb0ef41Sopenharmony_ci _entries; 2461cb0ef41Sopenharmony_ci _index = 0; 2471cb0ef41Sopenharmony_ci constructor(sourcePositions) { 2481cb0ef41Sopenharmony_ci this._entries = sourcePositions; 2491cb0ef41Sopenharmony_ci } 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci * forLine(lineIndex) { 2521cb0ef41Sopenharmony_ci this._findStart(lineIndex); 2531cb0ef41Sopenharmony_ci while (!this._done() && this._current().line === lineIndex) { 2541cb0ef41Sopenharmony_ci yield this._current(); 2551cb0ef41Sopenharmony_ci this._next(); 2561cb0ef41Sopenharmony_ci } 2571cb0ef41Sopenharmony_ci } 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci _findStart(lineIndex) { 2601cb0ef41Sopenharmony_ci while (!this._done() && this._current().line < lineIndex) { 2611cb0ef41Sopenharmony_ci this._next(); 2621cb0ef41Sopenharmony_ci } 2631cb0ef41Sopenharmony_ci } 2641cb0ef41Sopenharmony_ci 2651cb0ef41Sopenharmony_ci _current() { 2661cb0ef41Sopenharmony_ci return this._entries[this._index]; 2671cb0ef41Sopenharmony_ci } 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci _done() { 2701cb0ef41Sopenharmony_ci return this._index >= this._entries.length; 2711cb0ef41Sopenharmony_ci } 2721cb0ef41Sopenharmony_ci 2731cb0ef41Sopenharmony_ci _next() { 2741cb0ef41Sopenharmony_ci this._index++; 2751cb0ef41Sopenharmony_ci } 2761cb0ef41Sopenharmony_ci} 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_cifunction* lineIterator(source, startLine) { 2791cb0ef41Sopenharmony_ci let current = 0; 2801cb0ef41Sopenharmony_ci let line = startLine; 2811cb0ef41Sopenharmony_ci while (current < source.length) { 2821cb0ef41Sopenharmony_ci const next = source.indexOf('\n', current); 2831cb0ef41Sopenharmony_ci if (next === -1) break; 2841cb0ef41Sopenharmony_ci yield [line, source.substring(current, next)]; 2851cb0ef41Sopenharmony_ci line++; 2861cb0ef41Sopenharmony_ci current = next + 1; 2871cb0ef41Sopenharmony_ci } 2881cb0ef41Sopenharmony_ci if (current < source.length) yield [line, source.substring(current)]; 2891cb0ef41Sopenharmony_ci} 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ciclass LineBuilder { 2921cb0ef41Sopenharmony_ci static _colorMap = (function() { 2931cb0ef41Sopenharmony_ci const map = new Map(); 2941cb0ef41Sopenharmony_ci let i = 0; 2951cb0ef41Sopenharmony_ci for (let type of App.allEventTypes) { 2961cb0ef41Sopenharmony_ci map.set(type, CSSColor.at(i++)); 2971cb0ef41Sopenharmony_ci } 2981cb0ef41Sopenharmony_ci return map; 2991cb0ef41Sopenharmony_ci })(); 3001cb0ef41Sopenharmony_ci static get colorMap() { 3011cb0ef41Sopenharmony_ci return this._colorMap; 3021cb0ef41Sopenharmony_ci } 3031cb0ef41Sopenharmony_ci 3041cb0ef41Sopenharmony_ci _script; 3051cb0ef41Sopenharmony_ci _clickHandler; 3061cb0ef41Sopenharmony_ci _mouseoverHandler; 3071cb0ef41Sopenharmony_ci _sourcePositionToMarkers = new Map(); 3081cb0ef41Sopenharmony_ci 3091cb0ef41Sopenharmony_ci constructor(panel, script) { 3101cb0ef41Sopenharmony_ci this._script = script; 3111cb0ef41Sopenharmony_ci this._clickHandler = panel.handleSourcePositionClick.bind(panel); 3121cb0ef41Sopenharmony_ci this._mouseoverHandler = panel.handleSourcePositionMouseOver.bind(panel); 3131cb0ef41Sopenharmony_ci } 3141cb0ef41Sopenharmony_ci 3151cb0ef41Sopenharmony_ci get sourcePositionToMarkers() { 3161cb0ef41Sopenharmony_ci return this._sourcePositionToMarkers; 3171cb0ef41Sopenharmony_ci } 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci async createScriptNode(startLine) { 3201cb0ef41Sopenharmony_ci const scriptNode = DOM.div('scriptNode'); 3211cb0ef41Sopenharmony_ci 3221cb0ef41Sopenharmony_ci // TODO: sort on script finalization. 3231cb0ef41Sopenharmony_ci this._script.sourcePositions.sort((a, b) => { 3241cb0ef41Sopenharmony_ci if (a.line === b.line) return a.column - b.column; 3251cb0ef41Sopenharmony_ci return a.line - b.line; 3261cb0ef41Sopenharmony_ci }); 3271cb0ef41Sopenharmony_ci 3281cb0ef41Sopenharmony_ci const sourcePositionsIterator = 3291cb0ef41Sopenharmony_ci new SourcePositionIterator(this._script.sourcePositions); 3301cb0ef41Sopenharmony_ci scriptNode.style.counterReset = `sourceLineCounter ${startLine - 1}`; 3311cb0ef41Sopenharmony_ci for (let [lineIndex, line] of lineIterator( 3321cb0ef41Sopenharmony_ci this._script.source, startLine)) { 3331cb0ef41Sopenharmony_ci scriptNode.appendChild( 3341cb0ef41Sopenharmony_ci this._createLineNode(sourcePositionsIterator, lineIndex, line)); 3351cb0ef41Sopenharmony_ci } 3361cb0ef41Sopenharmony_ci if (this._script.sourcePositions.length != 3371cb0ef41Sopenharmony_ci this._sourcePositionToMarkers.size) { 3381cb0ef41Sopenharmony_ci console.error('Not all SourcePositions were processed.'); 3391cb0ef41Sopenharmony_ci } 3401cb0ef41Sopenharmony_ci return scriptNode; 3411cb0ef41Sopenharmony_ci } 3421cb0ef41Sopenharmony_ci 3431cb0ef41Sopenharmony_ci _createLineNode(sourcePositionsIterator, lineIndex, line) { 3441cb0ef41Sopenharmony_ci const lineNode = DOM.span(); 3451cb0ef41Sopenharmony_ci let columnIndex = 0; 3461cb0ef41Sopenharmony_ci for (const sourcePosition of sourcePositionsIterator.forLine(lineIndex)) { 3471cb0ef41Sopenharmony_ci const nextColumnIndex = sourcePosition.column - 1; 3481cb0ef41Sopenharmony_ci lineNode.appendChild(document.createTextNode( 3491cb0ef41Sopenharmony_ci line.substring(columnIndex, nextColumnIndex))); 3501cb0ef41Sopenharmony_ci columnIndex = nextColumnIndex; 3511cb0ef41Sopenharmony_ci 3521cb0ef41Sopenharmony_ci lineNode.appendChild( 3531cb0ef41Sopenharmony_ci this._createMarkerNode(line[columnIndex], sourcePosition)); 3541cb0ef41Sopenharmony_ci columnIndex++; 3551cb0ef41Sopenharmony_ci } 3561cb0ef41Sopenharmony_ci lineNode.appendChild( 3571cb0ef41Sopenharmony_ci document.createTextNode(line.substring(columnIndex) + '\n')); 3581cb0ef41Sopenharmony_ci return lineNode; 3591cb0ef41Sopenharmony_ci } 3601cb0ef41Sopenharmony_ci 3611cb0ef41Sopenharmony_ci _createMarkerNode(text, sourcePosition) { 3621cb0ef41Sopenharmony_ci const marker = document.createElement('mark'); 3631cb0ef41Sopenharmony_ci this._sourcePositionToMarkers.set(sourcePosition, marker); 3641cb0ef41Sopenharmony_ci marker.textContent = text; 3651cb0ef41Sopenharmony_ci marker.sourcePosition = sourcePosition; 3661cb0ef41Sopenharmony_ci marker.onclick = this._clickHandler; 3671cb0ef41Sopenharmony_ci marker.onmouseover = this._mouseoverHandler; 3681cb0ef41Sopenharmony_ci 3691cb0ef41Sopenharmony_ci const entries = sourcePosition.entries; 3701cb0ef41Sopenharmony_ci const groups = groupBy(entries, entry => entry.constructor); 3711cb0ef41Sopenharmony_ci if (groups.length > 1) { 3721cb0ef41Sopenharmony_ci const stops = gradientStopsFromGroups( 3731cb0ef41Sopenharmony_ci entries.length, '%', groups, type => LineBuilder.colorMap.get(type)); 3741cb0ef41Sopenharmony_ci marker.style.backgroundImage = `linear-gradient(0deg,${stops.join(',')})` 3751cb0ef41Sopenharmony_ci } else { 3761cb0ef41Sopenharmony_ci marker.style.backgroundColor = LineBuilder.colorMap.get(groups[0].key) 3771cb0ef41Sopenharmony_ci } 3781cb0ef41Sopenharmony_ci 3791cb0ef41Sopenharmony_ci return marker; 3801cb0ef41Sopenharmony_ci } 3811cb0ef41Sopenharmony_ci} 382