OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * | |
11 * 2. Redistributions in binary form must reproduce the above | |
12 * copyright notice, this list of conditions and the following disclaimer | |
13 * in the documentation and/or other materials provided with the | |
14 * distribution. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS | |
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. | |
20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 */ | |
28 | |
29 /** | |
30 * @unrestricted | |
31 */ | |
32 Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { | |
33 /** | |
34 * @param {!Workspace.UISourceCode} uiSourceCode | |
35 */ | |
36 constructor(uiSourceCode) { | |
37 super(uiSourceCode.contentURL(), workingCopy); | |
38 this._uiSourceCode = uiSourceCode; | |
39 this.setEditable(this._canEditSource()); | |
40 | |
41 if (Runtime.experiments.isEnabled('sourceDiff')) | |
42 this._diff = new Sources.SourceCodeDiff(uiSourceCode.requestOriginalConten
t(), this.textEditor); | |
43 | |
44 /** @type {?UI.AutocompleteConfig} */ | |
45 this._autocompleteConfig = {isWordChar: Common.TextUtils.isWordChar}; | |
46 Common.moduleSetting('textEditorAutocompletion').addChangeListener(this._upd
ateAutocomplete, this); | |
47 this._updateAutocomplete(); | |
48 | |
49 /** @type {?Persistence.PersistenceBinding} */ | |
50 this._persistenceBinding = Persistence.persistence.binding(uiSourceCode); | |
51 | |
52 /** @type {!Map<number, !Sources.UISourceCodeFrame.RowMessageBucket>} */ | |
53 this._rowMessageBuckets = new Map(); | |
54 /** @type {!Set<string>} */ | |
55 this._typeDecorationsPending = new Set(); | |
56 this._uiSourceCode.addEventListener( | |
57 Workspace.UISourceCode.Events.WorkingCopyChanged, this._onWorkingCopyCha
nged, this); | |
58 this._uiSourceCode.addEventListener( | |
59 Workspace.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCopyC
ommitted, this); | |
60 | |
61 this._messageAndDecorationListeners = []; | |
62 this._installMessageAndDecorationListeners(); | |
63 | |
64 Persistence.persistence.subscribeForBindingEvent(this._uiSourceCode, this._o
nBindingChanged.bind(this)); | |
65 | |
66 this.textEditor.addEventListener( | |
67 SourceFrame.SourcesTextEditor.Events.EditorBlurred, | |
68 () => UI.context.setFlavor(Sources.UISourceCodeFrame, null)); | |
69 this.textEditor.addEventListener( | |
70 SourceFrame.SourcesTextEditor.Events.EditorFocused, | |
71 () => UI.context.setFlavor(Sources.UISourceCodeFrame, this)); | |
72 | |
73 this._updateStyle(); | |
74 | |
75 this._errorPopoverHelper = new UI.PopoverHelper(this.element); | |
76 this._errorPopoverHelper.initializeCallbacks(this._getErrorAnchor.bind(this)
, this._showErrorPopover.bind(this)); | |
77 | |
78 this._errorPopoverHelper.setTimeout(100, 100); | |
79 | |
80 /** | |
81 * @return {!Promise<?string>} | |
82 */ | |
83 function workingCopy() { | |
84 if (uiSourceCode.isDirty()) | |
85 return /** @type {!Promise<?string>} */ (Promise.resolve(uiSourceCode.wo
rkingCopy())); | |
86 return uiSourceCode.requestContent(); | |
87 } | |
88 } | |
89 | |
90 _installMessageAndDecorationListeners() { | |
91 if (this._persistenceBinding) { | |
92 var networkSourceCode = this._persistenceBinding.network; | |
93 var fileSystemSourceCode = this._persistenceBinding.fileSystem; | |
94 this._messageAndDecorationListeners = [ | |
95 networkSourceCode.addEventListener(Workspace.UISourceCode.Events.Message
Added, this._onMessageAdded, this), | |
96 networkSourceCode.addEventListener(Workspace.UISourceCode.Events.Message
Removed, this._onMessageRemoved, this), | |
97 networkSourceCode.addEventListener( | |
98 Workspace.UISourceCode.Events.LineDecorationAdded, this._onLineDecor
ationAdded, this), | |
99 networkSourceCode.addEventListener( | |
100 Workspace.UISourceCode.Events.LineDecorationRemoved, this._onLineDec
orationRemoved, this), | |
101 | |
102 fileSystemSourceCode.addEventListener(Workspace.UISourceCode.Events.Mess
ageAdded, this._onMessageAdded, this), | |
103 fileSystemSourceCode.addEventListener( | |
104 Workspace.UISourceCode.Events.MessageRemoved, this._onMessageRemoved
, this), | |
105 ]; | |
106 } else { | |
107 this._messageAndDecorationListeners = [ | |
108 this._uiSourceCode.addEventListener(Workspace.UISourceCode.Events.Messag
eAdded, this._onMessageAdded, this), | |
109 this._uiSourceCode.addEventListener(Workspace.UISourceCode.Events.Messag
eRemoved, this._onMessageRemoved, this), | |
110 this._uiSourceCode.addEventListener( | |
111 Workspace.UISourceCode.Events.LineDecorationAdded, this._onLineDecor
ationAdded, this), | |
112 this._uiSourceCode.addEventListener( | |
113 Workspace.UISourceCode.Events.LineDecorationRemoved, this._onLineDec
orationRemoved, this) | |
114 ]; | |
115 } | |
116 } | |
117 | |
118 /** | |
119 * @return {!Workspace.UISourceCode} | |
120 */ | |
121 uiSourceCode() { | |
122 return this._uiSourceCode; | |
123 } | |
124 | |
125 /** | |
126 * @override | |
127 */ | |
128 wasShown() { | |
129 super.wasShown(); | |
130 // We need CodeMirrorTextEditor to be initialized prior to this call as it c
alls |cursorPositionToCoordinates| internally. @see crbug.com/506566 | |
131 setImmediate(this._updateBucketDecorations.bind(this)); | |
132 } | |
133 | |
134 /** | |
135 * @override | |
136 */ | |
137 willHide() { | |
138 super.willHide(); | |
139 UI.context.setFlavor(Sources.UISourceCodeFrame, null); | |
140 this._uiSourceCode.removeWorkingCopyGetter(); | |
141 } | |
142 | |
143 /** | |
144 * @return {boolean} | |
145 */ | |
146 _canEditSource() { | |
147 if (Persistence.persistence.binding(this._uiSourceCode)) | |
148 return true; | |
149 if (this._uiSourceCode.project().canSetFileContent()) | |
150 return true; | |
151 if (this._uiSourceCode.project().isServiceProject()) | |
152 return false; | |
153 return this._uiSourceCode.contentType() !== Common.resourceTypes.Document; | |
154 } | |
155 | |
156 commitEditing() { | |
157 if (!this._uiSourceCode.isDirty()) | |
158 return; | |
159 | |
160 this._muteSourceCodeEvents = true; | |
161 this._uiSourceCode.commitWorkingCopy(); | |
162 delete this._muteSourceCodeEvents; | |
163 } | |
164 | |
165 /** | |
166 * @override | |
167 */ | |
168 onTextEditorContentSet() { | |
169 if (this._diff) | |
170 this._diff.updateDiffMarkersImmediately(); | |
171 super.onTextEditorContentSet(); | |
172 for (var message of this._allMessages()) | |
173 this._addMessageToSource(message); | |
174 this._decorateAllTypes(); | |
175 } | |
176 | |
177 /** | |
178 * @return {!Array<!Workspace.UISourceCode.Message>} | |
179 */ | |
180 _allMessages() { | |
181 return this._persistenceBinding ? | |
182 this._persistenceBinding.network.messages().concat(this._persistenceBind
ing.fileSystem.messages()) : | |
183 this._uiSourceCode.messages(); | |
184 } | |
185 | |
186 /** | |
187 * @override | |
188 * @param {!Common.TextRange} oldRange | |
189 * @param {!Common.TextRange} newRange | |
190 */ | |
191 onTextChanged(oldRange, newRange) { | |
192 if (this._diff) | |
193 this._diff.updateDiffMarkersWhenPossible(); | |
194 super.onTextChanged(oldRange, newRange); | |
195 this._errorPopoverHelper.hidePopover(); | |
196 if (this._isSettingContent) | |
197 return; | |
198 this._muteSourceCodeEvents = true; | |
199 if (this.textEditor.isClean()) | |
200 this._uiSourceCode.resetWorkingCopy(); | |
201 else | |
202 this._uiSourceCode.setWorkingCopyGetter(this.textEditor.text.bind(this.tex
tEditor)); | |
203 delete this._muteSourceCodeEvents; | |
204 } | |
205 | |
206 /** | |
207 * @param {!Common.Event} event | |
208 */ | |
209 _onWorkingCopyChanged(event) { | |
210 if (this._muteSourceCodeEvents) | |
211 return; | |
212 this._innerSetContent(this._uiSourceCode.workingCopy()); | |
213 this.onUISourceCodeContentChanged(); | |
214 } | |
215 | |
216 /** | |
217 * @param {!Common.Event} event | |
218 */ | |
219 _onWorkingCopyCommitted(event) { | |
220 if (!this._muteSourceCodeEvents) { | |
221 this._innerSetContent(this._uiSourceCode.workingCopy()); | |
222 this.onUISourceCodeContentChanged(); | |
223 } | |
224 this.textEditor.markClean(); | |
225 this._updateStyle(); | |
226 } | |
227 | |
228 _onBindingChanged() { | |
229 var binding = Persistence.persistence.binding(this._uiSourceCode); | |
230 if (binding === this._persistenceBinding) | |
231 return; | |
232 for (var message of this._allMessages()) | |
233 this._removeMessageFromSource(message); | |
234 Common.EventTarget.removeEventListeners(this._messageAndDecorationListeners)
; | |
235 | |
236 this._persistenceBinding = binding; | |
237 | |
238 for (var message of this._allMessages()) | |
239 this._addMessageToSource(message); | |
240 this._installMessageAndDecorationListeners(); | |
241 this._updateStyle(); | |
242 this._decorateAllTypes(); | |
243 this.onBindingChanged(); | |
244 } | |
245 | |
246 /** | |
247 * @protected | |
248 */ | |
249 onBindingChanged() { | |
250 // Overriden in subclasses. | |
251 } | |
252 | |
253 _updateStyle() { | |
254 this.element.classList.toggle( | |
255 'source-frame-unsaved-committed-changes', | |
256 Persistence.persistence.hasUnsavedCommittedChanges(this._uiSourceCode)); | |
257 this.setEditable(this._canEditSource()); | |
258 } | |
259 | |
260 onUISourceCodeContentChanged() { | |
261 } | |
262 | |
263 _updateAutocomplete() { | |
264 this.textEditor.configureAutocomplete( | |
265 Common.moduleSetting('textEditorAutocompletion').get() ? this._autocompl
eteConfig : null); | |
266 } | |
267 | |
268 /** | |
269 * @param {?UI.AutocompleteConfig} config | |
270 */ | |
271 configureAutocomplete(config) { | |
272 this._autocompleteConfig = config; | |
273 this._updateAutocomplete(); | |
274 } | |
275 | |
276 /** | |
277 * @param {string} content | |
278 */ | |
279 _innerSetContent(content) { | |
280 this._isSettingContent = true; | |
281 if (this._diff) { | |
282 var oldContent = this.textEditor.text(); | |
283 this.setContent(content); | |
284 this._diff.highlightModifiedLines(oldContent, content); | |
285 } else { | |
286 this.setContent(content); | |
287 } | |
288 delete this._isSettingContent; | |
289 } | |
290 | |
291 /** | |
292 * @override | |
293 * @return {!Promise} | |
294 */ | |
295 populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber) { | |
296 /** | |
297 * @this {Sources.UISourceCodeFrame} | |
298 */ | |
299 function appendItems() { | |
300 contextMenu.appendApplicableItems(this._uiSourceCode); | |
301 contextMenu.appendApplicableItems(new Workspace.UILocation(this._uiSourceC
ode, lineNumber, columnNumber)); | |
302 contextMenu.appendApplicableItems(this); | |
303 } | |
304 | |
305 return super.populateTextAreaContextMenu(contextMenu, lineNumber, columnNumb
er).then(appendItems.bind(this)); | |
306 } | |
307 | |
308 /** | |
309 * @param {!Array.<!UI.Infobar|undefined>} infobars | |
310 */ | |
311 attachInfobars(infobars) { | |
312 for (var i = infobars.length - 1; i >= 0; --i) { | |
313 var infobar = infobars[i]; | |
314 if (!infobar) | |
315 continue; | |
316 this.element.insertBefore(infobar.element, this.element.children[0]); | |
317 infobar.setParentView(this); | |
318 } | |
319 this.doResize(); | |
320 } | |
321 | |
322 dispose() { | |
323 this.textEditor.dispose(); | |
324 Common.moduleSetting('textEditorAutocompletion').removeChangeListener(this._
updateAutocomplete, this); | |
325 this.detach(); | |
326 } | |
327 | |
328 /** | |
329 * @param {!Common.Event} event | |
330 */ | |
331 _onMessageAdded(event) { | |
332 var message = /** @type {!Workspace.UISourceCode.Message} */ (event.data); | |
333 this._addMessageToSource(message); | |
334 } | |
335 | |
336 /** | |
337 * @param {!Workspace.UISourceCode.Message} message | |
338 */ | |
339 _addMessageToSource(message) { | |
340 if (!this.loaded) | |
341 return; | |
342 var lineNumber = message.lineNumber(); | |
343 if (lineNumber >= this.textEditor.linesCount) | |
344 lineNumber = this.textEditor.linesCount - 1; | |
345 if (lineNumber < 0) | |
346 lineNumber = 0; | |
347 | |
348 var messageBucket = this._rowMessageBuckets.get(lineNumber); | |
349 if (!messageBucket) { | |
350 messageBucket = new Sources.UISourceCodeFrame.RowMessageBucket(this, this.
textEditor, lineNumber); | |
351 this._rowMessageBuckets.set(lineNumber, messageBucket); | |
352 } | |
353 messageBucket.addMessage(message); | |
354 } | |
355 | |
356 /** | |
357 * @param {!Common.Event} event | |
358 */ | |
359 _onMessageRemoved(event) { | |
360 var message = /** @type {!Workspace.UISourceCode.Message} */ (event.data); | |
361 this._removeMessageFromSource(message); | |
362 } | |
363 | |
364 /** | |
365 * @param {!Workspace.UISourceCode.Message} message | |
366 */ | |
367 _removeMessageFromSource(message) { | |
368 if (!this.loaded) | |
369 return; | |
370 | |
371 var lineNumber = message.lineNumber(); | |
372 if (lineNumber >= this.textEditor.linesCount) | |
373 lineNumber = this.textEditor.linesCount - 1; | |
374 if (lineNumber < 0) | |
375 lineNumber = 0; | |
376 | |
377 var messageBucket = this._rowMessageBuckets.get(lineNumber); | |
378 if (!messageBucket) | |
379 return; | |
380 messageBucket.removeMessage(message); | |
381 if (!messageBucket.uniqueMessagesCount()) { | |
382 messageBucket.detachFromEditor(); | |
383 this._rowMessageBuckets.delete(lineNumber); | |
384 } | |
385 } | |
386 | |
387 /** | |
388 * @param {!Element} target | |
389 * @param {!Event} event | |
390 * @return {(!Element|undefined)} | |
391 */ | |
392 _getErrorAnchor(target, event) { | |
393 var element = target.enclosingNodeOrSelfWithClass('text-editor-line-decorati
on-icon') || | |
394 target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-wave'); | |
395 if (!element) | |
396 return; | |
397 this._errorWavePopoverAnchor = new AnchorBox(event.clientX, event.clientY, 1
, 1); | |
398 return element; | |
399 } | |
400 | |
401 /** | |
402 * @param {!Element} anchor | |
403 * @param {!UI.Popover} popover | |
404 */ | |
405 _showErrorPopover(anchor, popover) { | |
406 var messageBucket = anchor.enclosingNodeOrSelfWithClass('text-editor-line-de
coration')._messageBucket; | |
407 var messagesOutline = messageBucket.messagesDescription(); | |
408 var popoverAnchor = | |
409 anchor.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon')
? anchor : this._errorWavePopoverAnchor; | |
410 popover.showForAnchor(messagesOutline, popoverAnchor); | |
411 } | |
412 | |
413 _updateBucketDecorations() { | |
414 for (var bucket of this._rowMessageBuckets.values()) | |
415 bucket._updateDecoration(); | |
416 } | |
417 | |
418 /** | |
419 * @param {!Common.Event} event | |
420 */ | |
421 _onLineDecorationAdded(event) { | |
422 var marker = /** @type {!Workspace.UISourceCode.LineMarker} */ (event.data); | |
423 this._decorateTypeThrottled(marker.type()); | |
424 } | |
425 | |
426 /** | |
427 * @param {!Common.Event} event | |
428 */ | |
429 _onLineDecorationRemoved(event) { | |
430 var marker = /** @type {!Workspace.UISourceCode.LineMarker} */ (event.data); | |
431 this._decorateTypeThrottled(marker.type()); | |
432 } | |
433 | |
434 /** | |
435 * @param {string} type | |
436 */ | |
437 _decorateTypeThrottled(type) { | |
438 if (this._typeDecorationsPending.has(type)) | |
439 return; | |
440 this._typeDecorationsPending.add(type); | |
441 self.runtime.extensions(Sources.UISourceCodeFrame.LineDecorator) | |
442 .find(extension => extension.descriptor()['decoratorType'] === type) | |
443 .instance() | |
444 .then(decorator => { | |
445 this._typeDecorationsPending.delete(type); | |
446 decorator.decorate( | |
447 this._persistenceBinding ? this._persistenceBinding.network : this
.uiSourceCode(), this.textEditor); | |
448 }); | |
449 } | |
450 | |
451 _decorateAllTypes() { | |
452 var extensions = self.runtime.extensions(Sources.UISourceCodeFrame.LineDecor
ator); | |
453 extensions.forEach(extension => this._decorateTypeThrottled(extension.descri
ptor()['decoratorType'])); | |
454 } | |
455 }; | |
456 | |
457 Sources.UISourceCodeFrame._iconClassPerLevel = {}; | |
458 Sources.UISourceCodeFrame._iconClassPerLevel[Workspace.UISourceCode.Message.Leve
l.Error] = 'smallicon-error'; | |
459 Sources.UISourceCodeFrame._iconClassPerLevel[Workspace.UISourceCode.Message.Leve
l.Warning] = 'smallicon-warning'; | |
460 | |
461 Sources.UISourceCodeFrame._bubbleTypePerLevel = {}; | |
462 Sources.UISourceCodeFrame._bubbleTypePerLevel[Workspace.UISourceCode.Message.Lev
el.Error] = 'error'; | |
463 Sources.UISourceCodeFrame._bubbleTypePerLevel[Workspace.UISourceCode.Message.Lev
el.Warning] = 'warning'; | |
464 | |
465 Sources.UISourceCodeFrame._lineClassPerLevel = {}; | |
466 Sources.UISourceCodeFrame._lineClassPerLevel[Workspace.UISourceCode.Message.Leve
l.Error] = | |
467 'text-editor-line-with-error'; | |
468 Sources.UISourceCodeFrame._lineClassPerLevel[Workspace.UISourceCode.Message.Leve
l.Warning] = | |
469 'text-editor-line-with-warning'; | |
470 | |
471 /** | |
472 * @interface | |
473 */ | |
474 Sources.UISourceCodeFrame.LineDecorator = function() {}; | |
475 | |
476 Sources.UISourceCodeFrame.LineDecorator.prototype = { | |
477 /** | |
478 * @param {!Workspace.UISourceCode} uiSourceCode | |
479 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | |
480 */ | |
481 decorate(uiSourceCode, textEditor) {} | |
482 }; | |
483 | |
484 /** | |
485 * @unrestricted | |
486 */ | |
487 Sources.UISourceCodeFrame.RowMessage = class { | |
488 /** | |
489 * @param {!Workspace.UISourceCode.Message} message | |
490 */ | |
491 constructor(message) { | |
492 this._message = message; | |
493 this._repeatCount = 1; | |
494 this.element = createElementWithClass('div', 'text-editor-row-message'); | |
495 this._icon = this.element.createChild('label', '', 'dt-icon-label'); | |
496 this._icon.type = Sources.UISourceCodeFrame._iconClassPerLevel[message.level
()]; | |
497 this._repeatCountElement = this.element.createChild('label', 'message-repeat
-count hidden', 'dt-small-bubble'); | |
498 this._repeatCountElement.type = Sources.UISourceCodeFrame._bubbleTypePerLeve
l[message.level()]; | |
499 var linesContainer = this.element.createChild('div', 'text-editor-row-messag
e-lines'); | |
500 var lines = this._message.text().split('\n'); | |
501 for (var i = 0; i < lines.length; ++i) { | |
502 var messageLine = linesContainer.createChild('div'); | |
503 messageLine.textContent = lines[i]; | |
504 } | |
505 } | |
506 | |
507 /** | |
508 * @return {!Workspace.UISourceCode.Message} | |
509 */ | |
510 message() { | |
511 return this._message; | |
512 } | |
513 | |
514 /** | |
515 * @return {number} | |
516 */ | |
517 repeatCount() { | |
518 return this._repeatCount; | |
519 } | |
520 | |
521 setRepeatCount(repeatCount) { | |
522 if (this._repeatCount === repeatCount) | |
523 return; | |
524 this._repeatCount = repeatCount; | |
525 this._updateMessageRepeatCount(); | |
526 } | |
527 | |
528 _updateMessageRepeatCount() { | |
529 this._repeatCountElement.textContent = this._repeatCount; | |
530 var showRepeatCount = this._repeatCount > 1; | |
531 this._repeatCountElement.classList.toggle('hidden', !showRepeatCount); | |
532 this._icon.classList.toggle('hidden', showRepeatCount); | |
533 } | |
534 }; | |
535 | |
536 /** | |
537 * @unrestricted | |
538 */ | |
539 Sources.UISourceCodeFrame.RowMessageBucket = class { | |
540 /** | |
541 * @param {!Sources.UISourceCodeFrame} sourceFrame | |
542 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | |
543 * @param {number} lineNumber | |
544 */ | |
545 constructor(sourceFrame, textEditor, lineNumber) { | |
546 this._sourceFrame = sourceFrame; | |
547 this.textEditor = textEditor; | |
548 this._lineHandle = textEditor.textEditorPositionHandle(lineNumber, 0); | |
549 this._decoration = createElementWithClass('div', 'text-editor-line-decoratio
n'); | |
550 this._decoration._messageBucket = this; | |
551 this._wave = this._decoration.createChild('div', 'text-editor-line-decoratio
n-wave'); | |
552 this._icon = this._wave.createChild('label', 'text-editor-line-decoration-ic
on', 'dt-icon-label'); | |
553 this._hasDecoration = false; | |
554 | |
555 this._messagesDescriptionElement = createElementWithClass('div', 'text-edito
r-messages-description-container'); | |
556 /** @type {!Array.<!Sources.UISourceCodeFrame.RowMessage>} */ | |
557 this._messages = []; | |
558 | |
559 this._level = null; | |
560 } | |
561 | |
562 /** | |
563 * @param {number} lineNumber | |
564 * @param {number} columnNumber | |
565 */ | |
566 _updateWavePosition(lineNumber, columnNumber) { | |
567 lineNumber = Math.min(lineNumber, this.textEditor.linesCount - 1); | |
568 var lineText = this.textEditor.line(lineNumber); | |
569 columnNumber = Math.min(columnNumber, lineText.length); | |
570 var lineIndent = Common.TextUtils.lineIndent(lineText).length; | |
571 if (this._hasDecoration) | |
572 this.textEditor.removeDecoration(this._decoration, lineNumber); | |
573 this._hasDecoration = true; | |
574 this.textEditor.addDecoration(this._decoration, lineNumber, Math.max(columnN
umber - 1, lineIndent)); | |
575 } | |
576 | |
577 /** | |
578 * @return {!Element} | |
579 */ | |
580 messagesDescription() { | |
581 this._messagesDescriptionElement.removeChildren(); | |
582 for (var i = 0; i < this._messages.length; ++i) | |
583 this._messagesDescriptionElement.appendChild(this._messages[i].element); | |
584 | |
585 return this._messagesDescriptionElement; | |
586 } | |
587 | |
588 detachFromEditor() { | |
589 var position = this._lineHandle.resolve(); | |
590 if (!position) | |
591 return; | |
592 var lineNumber = position.lineNumber; | |
593 if (this._level) | |
594 this.textEditor.toggleLineClass(lineNumber, Sources.UISourceCodeFrame._lin
eClassPerLevel[this._level], false); | |
595 if (this._hasDecoration) | |
596 this.textEditor.removeDecoration(this._decoration, lineNumber); | |
597 this._hasDecoration = false; | |
598 } | |
599 | |
600 /** | |
601 * @return {number} | |
602 */ | |
603 uniqueMessagesCount() { | |
604 return this._messages.length; | |
605 } | |
606 | |
607 /** | |
608 * @param {!Workspace.UISourceCode.Message} message | |
609 */ | |
610 addMessage(message) { | |
611 for (var i = 0; i < this._messages.length; ++i) { | |
612 var rowMessage = this._messages[i]; | |
613 if (rowMessage.message().isEqual(message)) { | |
614 rowMessage.setRepeatCount(rowMessage.repeatCount() + 1); | |
615 return; | |
616 } | |
617 } | |
618 | |
619 var rowMessage = new Sources.UISourceCodeFrame.RowMessage(message); | |
620 this._messages.push(rowMessage); | |
621 this._updateDecoration(); | |
622 } | |
623 | |
624 /** | |
625 * @param {!Workspace.UISourceCode.Message} message | |
626 */ | |
627 removeMessage(message) { | |
628 for (var i = 0; i < this._messages.length; ++i) { | |
629 var rowMessage = this._messages[i]; | |
630 if (!rowMessage.message().isEqual(message)) | |
631 continue; | |
632 rowMessage.setRepeatCount(rowMessage.repeatCount() - 1); | |
633 if (!rowMessage.repeatCount()) | |
634 this._messages.splice(i, 1); | |
635 this._updateDecoration(); | |
636 return; | |
637 } | |
638 } | |
639 | |
640 _updateDecoration() { | |
641 if (!this._sourceFrame.isEditorShowing()) | |
642 return; | |
643 if (!this._messages.length) | |
644 return; | |
645 var position = this._lineHandle.resolve(); | |
646 if (!position) | |
647 return; | |
648 | |
649 var lineNumber = position.lineNumber; | |
650 var columnNumber = Number.MAX_VALUE; | |
651 var maxMessage = null; | |
652 for (var i = 0; i < this._messages.length; ++i) { | |
653 var message = this._messages[i].message(); | |
654 columnNumber = Math.min(columnNumber, message.columnNumber()); | |
655 if (!maxMessage || Workspace.UISourceCode.Message.messageLevelComparator(m
axMessage, message) < 0) | |
656 maxMessage = message; | |
657 } | |
658 this._updateWavePosition(lineNumber, columnNumber); | |
659 | |
660 if (this._level) { | |
661 this.textEditor.toggleLineClass(lineNumber, Sources.UISourceCodeFrame._lin
eClassPerLevel[this._level], false); | |
662 this._icon.type = ''; | |
663 } | |
664 this._level = maxMessage.level(); | |
665 if (!this._level) | |
666 return; | |
667 this.textEditor.toggleLineClass(lineNumber, Sources.UISourceCodeFrame._lineC
lassPerLevel[this._level], true); | |
668 this._icon.type = Sources.UISourceCodeFrame._iconClassPerLevel[this._level]; | |
669 } | |
670 }; | |
671 | |
672 Workspace.UISourceCode.Message._messageLevelPriority = { | |
673 'Warning': 3, | |
674 'Error': 4 | |
675 }; | |
676 | |
677 /** | |
678 * @param {!Workspace.UISourceCode.Message} a | |
679 * @param {!Workspace.UISourceCode.Message} b | |
680 * @return {number} | |
681 */ | |
682 Workspace.UISourceCode.Message.messageLevelComparator = function(a, b) { | |
683 return Workspace.UISourceCode.Message._messageLevelPriority[a.level()] - | |
684 Workspace.UISourceCode.Message._messageLevelPriority[b.level()]; | |
685 }; | |
OLD | NEW |