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 |