Chromium Code Reviews| 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 uiSourceCode of Workspace.workspace.uiSourceCodes()) { | |
| 43 uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._ decoratorType); | |
| 44 uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._deco ratorType, this); | |
| 45 } | |
| 46 } | |
| 47 | |
| 48 /** | |
| 49 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 50 * @return {!Promise<!Array<boolean>>} | |
| 51 */ | |
| 52 async usageByLine(uiSourceCode) { | |
| 53 var result = []; | |
| 54 var sourceText = new TextUtils.Text(uiSourceCode.content() || ''); | |
| 55 await this._updateTexts(uiSourceCode, sourceText); | |
| 56 var lineEndings = sourceText.lineEndings(); | |
| 57 for (var line = 0; line < sourceText.lineCount(); ++line) { | |
| 58 var lineLength = lineEndings[line] - (line ? lineEndings[line - 1] : 0); | |
| 59 var startLocations = this._rawLocationsForSourceLocation(uiSourceCode, lin e, 0); | |
| 60 var endLocations = this._rawLocationsForSourceLocation(uiSourceCode, line, lineLength - 1); | |
| 61 var used = false; | |
| 62 for (var startIndex = 0, endIndex = 0; startIndex < startLocations.length; ++startIndex) { | |
| 63 var start = startLocations[startIndex]; | |
| 64 while (endIndex < endLocations.length && | |
| 65 Coverage.CoverageDecorationManager._compareLocations(start, endLo cations[endIndex]) >= 0) | |
|
alph
2017/05/05 22:50:36
Shouldn't be ">" instead?
start === endLocations[e
| |
| 66 ++endIndex; | |
| 67 if (endIndex >= endLocations.length || endLocations[endIndex].id !== sta rt.id) | |
|
alph
2017/05/05 22:50:36
hmm. How come "endLocations[endIndex].id !== start
| |
| 68 continue; | |
| 69 var end = endLocations[endIndex]; | |
| 70 var text = this._textByProvider.get(end.contentProvider); | |
| 71 if (!text) | |
| 72 continue; | |
| 73 var textValue = text.value(); | |
| 74 var startOffset = text.offsetFromPosition(start.line, start.column); | |
| 75 var endOffset = text.offsetFromPosition(end.line, end.column); | |
| 76 while (startOffset <= endOffset && /\s/.test(textValue[startOffset])) | |
| 77 ++startOffset; | |
| 78 while (startOffset <= endOffset && /\s/.test(textValue[endOffset])) | |
| 79 --endOffset; | |
| 80 used = | |
| 81 startOffset <= endOffset && this._coverageModel.usageForRange(end.co ntentProvider, startOffset, endOffset); | |
| 82 if (used) | |
| 83 break; | |
| 84 } | |
| 85 result.push(used); | |
| 86 } | |
| 87 return result; | |
| 88 } | |
| 89 | |
| 90 /** | |
| 91 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 92 * @param {!TextUtils.Text} text | |
| 93 * @return {!Promise} | |
| 94 */ | |
| 95 _updateTexts(uiSourceCode, text) { | |
| 96 var promises = []; | |
| 97 for (var line = 0; line < text.lineCount(); ++line) { | |
| 98 for (var entry of this._rawLocationsForSourceLocation(uiSourceCode, line, 0)) { | |
| 99 if (this._textByProvider.has(entry.contentProvider)) | |
| 100 continue; | |
| 101 this._textByProvider.set(entry.contentProvider, null); | |
| 102 this._uiSourceCodeByContentProvider.set(entry.contentProvider, uiSourceC ode); | |
| 103 promises.push(this._updateTextForProvider(entry.contentProvider)); | |
| 104 } | |
| 105 } | |
| 106 return Promise.all(promises); | |
| 107 } | |
| 108 | |
| 109 /** | |
| 110 * @param {!Common.ContentProvider} contentProvider | |
| 111 * @return {!Promise} | |
| 112 */ | |
| 113 async _updateTextForProvider(contentProvider) { | |
| 114 var content = await contentProvider.requestContent(); | |
| 115 this._textByProvider.set(contentProvider, new TextUtils.Text(content)); | |
| 116 } | |
| 117 | |
| 118 /** | |
| 119 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 120 * @param {number} line | |
| 121 * @param {number} column | |
| 122 * @return {!Array<!Coverage.RawLocation>} | |
| 123 */ | |
| 124 _rawLocationsForSourceLocation(uiSourceCode, line, column) { | |
| 125 var result = []; | |
| 126 var contentType = uiSourceCode.contentType(); | |
| 127 if (contentType.hasScripts()) { | |
| 128 var location = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(u iSourceCode, line, column); | |
| 129 if (location && location.script()) { | |
| 130 result.push({ | |
| 131 id: `js:${location.scriptId}`, | |
| 132 contentProvider: location.script(), | |
| 133 line: location.lineNumber, | |
| 134 column: location.columnNumber | |
| 135 }); | |
| 136 } | |
| 137 } | |
| 138 if (contentType.isStyleSheet() || contentType.isDocument()) { | |
| 139 var rawStyleLocations = | |
| 140 Bindings.cssWorkspaceBinding.uiLocationToRawLocations(new Workspace.UI Location(uiSourceCode, line, column)); | |
| 141 for (var location of rawStyleLocations) { | |
| 142 if (!location.header()) | |
| 143 continue; | |
| 144 result.push({ | |
| 145 id: `css:${location.styleSheetId}`, | |
| 146 contentProvider: location.header(), | |
| 147 line: location.lineNumber, | |
| 148 column: location.columnNumber | |
| 149 }); | |
| 150 } | |
| 151 } | |
| 152 result.sort(Coverage.CoverageDecorationManager._compareLocations); | |
| 153 return result; | |
| 154 } | |
| 155 | |
| 156 /** | |
| 157 * @param {!Coverage.RawLocation} a | |
| 158 * @param {!Coverage.RawLocation} b | |
|
alph
2017/05/05 22:50:36
@return
| |
| 159 */ | |
| 160 static _compareLocations(a, b) { | |
| 161 return a.id.localeCompare(b.id) || a.line - b.line || a.column - b.column; | |
| 162 } | |
| 163 | |
| 164 /** | |
| 165 * @param {!Common.Event} event | |
| 166 */ | |
| 167 _onUISourceCodeAdded(event) { | |
| 168 var uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data); | |
| 169 uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decora torType, this); | |
| 170 } | |
| 171 }; | |
| 172 | |
| 173 Coverage.CoverageDecorationManager._decoratorType = 'coverage'; | |
| 174 | |
| 175 /** | |
| 176 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator} | |
| 177 */ | |
| 178 Coverage.CoverageView.LineDecorator = class { | |
| 179 /** | |
| 180 * @override | |
| 181 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 182 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | |
| 183 */ | |
| 184 decorate(uiSourceCode, textEditor) { | |
| 185 var decorations = uiSourceCode.decorationsForType(Coverage.CoverageDecoratio nManager._decoratorType); | |
| 186 if (!decorations.size) { | |
| 187 textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType ); | |
| 188 return; | |
| 189 } | |
| 190 var decorationManager = | |
| 191 /** @type {!Coverage.CoverageDecorationManager} */ (decorations.values() .next().value.data()); | |
| 192 decorationManager.usageByLine(uiSourceCode).then(lineUsage => { | |
| 193 textEditor.operation(() => this._innerDecorate(textEditor, lineUsage)); | |
| 194 }); | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | |
| 199 * @param {!Array<boolean>} lineUsage | |
| 200 */ | |
| 201 _innerDecorate(textEditor, lineUsage) { | |
| 202 var gutterType = Coverage.CoverageView.LineDecorator._gutterType; | |
| 203 textEditor.uninstallGutter(gutterType); | |
| 204 textEditor.installGutter(gutterType, false); | |
| 205 for (var line = 0; line < lineUsage.length; ++line) { | |
| 206 var className = lineUsage[line] ? 'text-editor-coverage-used-marker' : 'te xt-editor-coverage-unused-marker'; | |
| 207 textEditor.setGutterDecoration(line, gutterType, createElementWithClass('d iv', className)); | |
| 208 } | |
| 209 } | |
| 210 }; | |
| 211 | |
| 212 Coverage.CoverageView.LineDecorator._gutterType = 'CodeMirror-gutter-coverage'; | |
| OLD | NEW |