133 lines
4.4 KiB
Dart
133 lines
4.4 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
/// This library provides a single function called injectLogs which when called
|
|
/// will request a logs json file and build a small widget out of them which
|
|
/// groups the logs by level.
|
|
library polymer.build.log_injector;
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:html';
|
|
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:source_span/source_span.dart';
|
|
import 'package:dev_compiler/src/summary.dart';
|
|
|
|
main() async => displayMessages(await HttpRequest.getString('messages.json'));
|
|
|
|
void displayMessages(String data) {
|
|
var summary = GlobalSummary.parse(JSON.decode(data));
|
|
var messagesByLevel = _getMessagesByLevel(summary);
|
|
if (messagesByLevel.isEmpty) return;
|
|
|
|
// Build the wrapper, menu, and content divs.
|
|
var menuWrapper = new DivElement()..classes.add('menu');
|
|
var contentWrapper = new DivElement()..classes.add('content');
|
|
var wrapperDiv = new DivElement()
|
|
..classes.add('dev-compiler-messages')
|
|
..append(menuWrapper)
|
|
..append(contentWrapper);
|
|
|
|
var selectedMenu = new _Selection();
|
|
var selectedContent = new _Selection();
|
|
|
|
// Add a menu item, content section, and messages for each log level.
|
|
messagesByLevel.forEach((level, messages) {
|
|
var contentItem = new DivElement()..classes.add(level);
|
|
var menuItem = new Element.html('<div class="$level">'
|
|
'$level <span class="num">(${messages.length})</span>'
|
|
'</div>');
|
|
menuWrapper.append(menuItem);
|
|
contentWrapper.append(contentItem);
|
|
menuItem.onClick.listen((_) {
|
|
selectedMenu.select(menuItem);
|
|
selectedContent.select(contentItem);
|
|
});
|
|
|
|
// TODO(sigmund): add permanent links to error messages like in polymer.
|
|
for (var m in messages) {
|
|
var message = _hyperlinkUrls(_escape(m.message));
|
|
var span = m.span;
|
|
var sb = new StringBuffer();
|
|
sb.write('<div class="message"><div class="text $level">$message</div>');
|
|
if (span != null) {
|
|
sb.write('<div class="location">'
|
|
' <span class="location">${span.start.toolString}</span></div>'
|
|
' <span class="text">');
|
|
if (span is SourceSpanWithContext) {
|
|
var context = span.context;
|
|
var text = span.text;
|
|
sb.write(_escape(context.substring(0, span.start.column)));
|
|
sb.write('<span class="$level">');
|
|
sb.write(_escape(span.text));
|
|
sb.write('</span>');
|
|
sb.write(_escape(context.substring(span.end.column)));
|
|
} else {
|
|
sb.write(_escape(span.text));
|
|
}
|
|
sb.write('</span></div></div>');
|
|
}
|
|
sb.write('</div>');
|
|
|
|
var logElement = new Element.html('$sb',
|
|
validator: new NodeValidatorBuilder.common()
|
|
..allowNavigation(new _OpenUriPolicy()));
|
|
contentItem.append(logElement);
|
|
var messageElement = logElement.querySelector('div.text');
|
|
messageElement.onClick.listen((e) {
|
|
if (e.target == messageElement) {
|
|
messageElement.classes.toggle('expanded');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
document.body.append(wrapperDiv);
|
|
}
|
|
|
|
/// Toggles classes to match which item of a list of items is selected.
|
|
class _Selection {
|
|
Element _selected;
|
|
|
|
select(Element newItem) {
|
|
if (_selected == newItem) {
|
|
_selected = null;
|
|
} else {
|
|
if (_selected != null) {
|
|
_selected.classes.remove('active');
|
|
}
|
|
_selected = newItem;
|
|
}
|
|
newItem.classes.toggle('active');
|
|
}
|
|
}
|
|
|
|
final _urlRegex = new RegExp('http://[^ ]*');
|
|
final _escaper = new HtmlEscape();
|
|
String _hyperlinkUrls(String text) => text.replaceAllMapped(_urlRegex,
|
|
(m) => '<a href="${m.group(0)}" target="blank">${m.group(0)}</a>');
|
|
String _escape(String text) => _escaper.convert(text);
|
|
|
|
class _OpenUriPolicy implements UriPolicy {
|
|
bool allowsUri(String uri) => true;
|
|
}
|
|
|
|
Map<String, List<MessageSummary>> _getMessagesByLevel(GlobalSummary messages) {
|
|
var visitor = new _Visitor();
|
|
messages.accept(visitor);
|
|
return visitor.messagesByLevel;
|
|
}
|
|
|
|
class _Visitor extends RecursiveSummaryVisitor {
|
|
final Map<String, List<MessageSummary>> messagesByLevel = {};
|
|
|
|
@override
|
|
void visitMessage(MessageSummary message) {
|
|
var level = message.level.toLowerCase();
|
|
messagesByLevel.putIfAbsent(level, () => []);
|
|
messagesByLevel[level].add(message);
|
|
}
|
|
}
|