OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @typedef {!{ |
| 7 * id: string, |
| 8 * contentProvider: !Common.ContentProvider, |
| 9 * line: number, |
| 10 * column: number |
| 11 * }} |
| 12 */ |
| 13 Coverage.RawLocation; |
| 14 |
| 15 Coverage.CoverageDecorationManager = class { |
| 16 /** |
| 17 * @param {!Coverage.CoverageModel} coverageModel |
| 18 */ |
| 19 constructor(coverageModel) { |
| 20 this._coverageModel = coverageModel; |
| 21 /** @type {!Map<!Common.ContentProvider, ?TextUtils.Text>} */ |
| 22 this._textByProvider = new Map(); |
| 23 /** @type {!Multimap<!Common.ContentProvider, !Workspace.UISourceCode>} */ |
| 24 this._uiSourceCodeByContentProvider = new Multimap(); |
| 25 |
| 26 for (var uiSourceCode of Workspace.workspace.uiSourceCodes()) |
| 27 uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._deco
ratorType, this); |
| 28 Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCode
Added, this._onUISourceCodeAdded, this); |
| 29 } |
| 30 |
| 31 dispose() { |
| 32 for (var uiSourceCode of Workspace.workspace.uiSourceCodes()) |
| 33 uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._
decoratorType); |
| 34 Workspace.workspace.removeEventListener( |
| 35 Workspace.Workspace.Events.UISourceCodeAdded, this._onUISourceCodeAdded,
this); |
| 36 } |
| 37 |
| 38 /** |
| 39 * @param {!Array<!Coverage.CoverageInfo>} updatedEntries |
| 40 */ |
| 41 update(updatedEntries) { |
| 42 for (var entry of updatedEntries) { |
| 43 for (var uiSourceCode of this._uiSourceCodeByContentProvider.get(entry.con
tentProvider())) { |
| 44 uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager
._decoratorType); |
| 45 uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._de
coratorType, this); |
| 46 } |
| 47 } |
| 48 } |
| 49 |
| 50 /** |
| 51 * @param {!Workspace.UISourceCode} uiSourceCode |
| 52 * @return {!Promise<!Array<boolean>>} |
| 53 */ |
| 54 async usageByLine(uiSourceCode) { |
| 55 var result = []; |
| 56 var sourceText = new TextUtils.Text(uiSourceCode.content() || ''); |
| 57 await this._updateTexts(uiSourceCode, sourceText); |
| 58 var lineEndings = sourceText.lineEndings(); |
| 59 for (var line = 0; line < sourceText.lineCount(); ++line) { |
| 60 var lineLength = lineEndings[line] - (line ? lineEndings[line - 1] : 0) -
1; |
| 61 if (!lineLength) { |
| 62 result.push(false); |
| 63 continue; |
| 64 } |
| 65 var startLocations = this._rawLocationsForSourceLocation(uiSourceCode, lin
e, 0); |
| 66 var endLocations = this._rawLocationsForSourceLocation(uiSourceCode, line,
lineLength); |
| 67 var used = false; |
| 68 for (var startIndex = 0, endIndex = 0; startIndex < startLocations.length;
++startIndex) { |
| 69 var start = startLocations[startIndex]; |
| 70 while (endIndex < endLocations.length && |
| 71 Coverage.CoverageDecorationManager._compareLocations(start, endLo
cations[endIndex]) >= 0) |
| 72 ++endIndex; |
| 73 if (endIndex >= endLocations.length || endLocations[endIndex].id !== sta
rt.id) |
| 74 continue; |
| 75 var end = endLocations[endIndex++]; |
| 76 var text = this._textByProvider.get(end.contentProvider); |
| 77 if (!text) |
| 78 continue; |
| 79 var textValue = text.value(); |
| 80 var startOffset = text.offsetFromPosition(start.line, start.column); |
| 81 var endOffset = text.offsetFromPosition(end.line, end.column); |
| 82 while (startOffset <= endOffset && /\s/.test(textValue[startOffset])) |
| 83 ++startOffset; |
| 84 while (startOffset <= endOffset && /\s/.test(textValue[endOffset])) |
| 85 --endOffset; |
| 86 used = |
| 87 startOffset <= endOffset && this._coverageModel.usageForRange(end.co
ntentProvider, startOffset, endOffset); |
| 88 if (used) |
| 89 break; |
| 90 } |
| 91 result.push(used); |
| 92 } |
| 93 return result; |
| 94 } |
| 95 |
| 96 /** |
| 97 * @param {!Workspace.UISourceCode} uiSourceCode |
| 98 * @param {!TextUtils.Text} text |
| 99 * @return {!Promise} |
| 100 */ |
| 101 _updateTexts(uiSourceCode, text) { |
| 102 var promises = []; |
| 103 for (var line = 0; line < text.lineCount(); ++line) { |
| 104 for (var entry of this._rawLocationsForSourceLocation(uiSourceCode, line,
0)) { |
| 105 if (this._textByProvider.has(entry.contentProvider)) |
| 106 continue; |
| 107 this._textByProvider.set(entry.contentProvider, null); |
| 108 this._uiSourceCodeByContentProvider.set(entry.contentProvider, uiSourceC
ode); |
| 109 promises.push(this._updateTextForProvider(entry.contentProvider)); |
| 110 } |
| 111 } |
| 112 return Promise.all(promises); |
| 113 } |
| 114 |
| 115 /** |
| 116 * @param {!Common.ContentProvider} contentProvider |
| 117 * @return {!Promise} |
| 118 */ |
| 119 async _updateTextForProvider(contentProvider) { |
| 120 var content = await contentProvider.requestContent(); |
| 121 this._textByProvider.set(contentProvider, new TextUtils.Text(content)); |
| 122 } |
| 123 |
| 124 /** |
| 125 * @param {!Workspace.UISourceCode} uiSourceCode |
| 126 * @param {number} line |
| 127 * @param {number} column |
| 128 * @return {!Array<!Coverage.RawLocation>} |
| 129 */ |
| 130 _rawLocationsForSourceLocation(uiSourceCode, line, column) { |
| 131 var result = []; |
| 132 var contentType = uiSourceCode.contentType(); |
| 133 if (contentType.hasScripts()) { |
| 134 var location = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(u
iSourceCode, line, column); |
| 135 if (location && location.script()) { |
| 136 result.push({ |
| 137 id: `js:${location.scriptId}`, |
| 138 contentProvider: location.script(), |
| 139 line: location.lineNumber, |
| 140 column: location.columnNumber |
| 141 }); |
| 142 } |
| 143 } |
| 144 if (contentType.isStyleSheet() || contentType.isDocument()) { |
| 145 var rawStyleLocations = |
| 146 Bindings.cssWorkspaceBinding.uiLocationToRawLocations(new Workspace.UI
Location(uiSourceCode, line, column)); |
| 147 for (var location of rawStyleLocations) { |
| 148 if (!location.header()) |
| 149 continue; |
| 150 result.push({ |
| 151 id: `css:${location.styleSheetId}`, |
| 152 contentProvider: location.header(), |
| 153 line: location.lineNumber, |
| 154 column: location.columnNumber |
| 155 }); |
| 156 } |
| 157 } |
| 158 result.sort(Coverage.CoverageDecorationManager._compareLocations); |
| 159 return result; |
| 160 } |
| 161 |
| 162 /** |
| 163 * @param {!Coverage.RawLocation} a |
| 164 * @param {!Coverage.RawLocation} b |
| 165 */ |
| 166 static _compareLocations(a, b) { |
| 167 return a.id.localeCompare(b.id) || a.line - b.line || a.column - b.column; |
| 168 } |
| 169 |
| 170 /** |
| 171 * @param {!Common.Event} event |
| 172 */ |
| 173 _onUISourceCodeAdded(event) { |
| 174 var uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data); |
| 175 uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decora
torType, this); |
| 176 } |
| 177 }; |
| 178 |
| 179 Coverage.CoverageDecorationManager._decoratorType = 'coverage'; |
| 180 |
| 181 /** |
| 182 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator} |
| 183 */ |
| 184 Coverage.CoverageView.LineDecorator = class { |
| 185 /** |
| 186 * @override |
| 187 * @param {!Workspace.UISourceCode} uiSourceCode |
| 188 * @param {!TextEditor.CodeMirrorTextEditor} textEditor |
| 189 */ |
| 190 decorate(uiSourceCode, textEditor) { |
| 191 var decorations = uiSourceCode.decorationsForType(Coverage.CoverageDecoratio
nManager._decoratorType); |
| 192 if (!decorations.size) { |
| 193 textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType
); |
| 194 return; |
| 195 } |
| 196 var decorationManager = |
| 197 /** @type {!Coverage.CoverageDecorationManager} */ (decorations.values()
.next().value.data()); |
| 198 decorationManager.usageByLine(uiSourceCode).then(lineUsage => { |
| 199 textEditor.operation(() => this._innerDecorate(textEditor, lineUsage)); |
| 200 }); |
| 201 } |
| 202 |
| 203 /** |
| 204 * @param {!TextEditor.CodeMirrorTextEditor} textEditor |
| 205 * @param {!Array<boolean>} lineUsage |
| 206 */ |
| 207 _innerDecorate(textEditor, lineUsage) { |
| 208 var gutterType = Coverage.CoverageView.LineDecorator._gutterType; |
| 209 textEditor.uninstallGutter(gutterType); |
| 210 textEditor.installGutter(gutterType, false); |
| 211 for (var line = 0; line < lineUsage.length; ++line) { |
| 212 var className = lineUsage[line] ? 'text-editor-coverage-used-marker' : 'te
xt-editor-coverage-unused-marker'; |
| 213 textEditor.setGutterDecoration(line, gutterType, createElementWithClass('d
iv', className)); |
| 214 } |
| 215 } |
| 216 }; |
| 217 |
| 218 Coverage.CoverageView.LineDecorator._gutterType = 'CodeMirror-gutter-coverage'; |
OLD | NEW |