Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(817)

Unified Diff: pkg/code_transformers/lib/messages/messages.dart

Issue 513023002: Step one towards stable error messages with details: (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/code_transformers/lib/messages/build_logger.dart ('k') | pkg/code_transformers/lib/src/messages.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/code_transformers/lib/messages/messages.dart
diff --git a/pkg/code_transformers/lib/messages/messages.dart b/pkg/code_transformers/lib/messages/messages.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b30c58ecfd32bdfc07a4e831d94b9983686f3ac4
--- /dev/null
+++ b/pkg/code_transformers/lib/messages/messages.dart
@@ -0,0 +1,230 @@
+// Copyright (c) 2014, 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.
+
+/// Defines messages templates and an adapter for TransformLogger to be able
+/// report error messages from transformers and refer to them in a consistent
+/// manner long term.
+library code_transformers.messages;
+
+// Note: this library purposely doesn't depend on dart:io, dart:html, or barback
+// so it can easily be used both in the transformers and in client-side apps
+// (for example in the log_injector).
+import 'dart:collection' show LinkedHashMap;
+import 'package:source_span/source_span.dart';
+
+/// A globally unique identifier for an error message. This identifier should be
+/// stable, that is, it should never change after it is asigned to a particular
+/// message. That allows us to document our error messages and make them
+/// searchable for prosperity.
+class MessageId implements Comparable {
+ /// Name of the package that declares this message.
+ final String package;
+
+ /// Message identifier number, unique within the package.
+ final int id;
+
+ const MessageId(this.package, this.id);
+
+ static const MessageId NOT_SPECIFIED = const MessageId('unknown', 0);
+
+ /// Serialize this message. We use a string and not a map to encode ids so
+ /// they can be used as keys in JSON maps.
+ String toJson() => toString();
+
+ toString() => '${package}#$id';
+
+ int compareTo(MessageId other) {
+ var res = package.compareTo(other.package);
+ if (res != 0) return res;
+ return id.compareTo(other.id);
+ }
+
+ /// Creates a new [MessageId] from an encoded value produced via [toJson].
+ factory MessageId.fromJson(data) {
+ var index = data.lastIndexOf('#');
+ if (index == -1) throw 'Invalid message id: $data';
+ return new MessageId(data.substring(0, index),
+ int.parse(data.substring(index + 1)));
+ }
+
+ operator ==(MessageId other) => package == other.package && id == other.id;
+ int get hashCode => 31 * package.hashCode + id;
+}
+
+/// An instance of an error message. These are typically produced from a
+/// [MessageTemplate].
+class Message {
+ /// A globally unique identifier for this message.
+ final MessageId id;
+
+ /// A snippet message that is presented to the user.
+ final String snippet;
+
+ const Message(this.id, this.snippet);
+
+ const Message.unknown(this.snippet) : id = MessageId.NOT_SPECIFIED;
+
+ /// Serializes this message to JSON.
+ Map toJson() => {'id': id.toJson(), 'snippet': snippet};
+ String toString() => 'id: $id, snippet: $snippet';
+
+ /// Creates a new [Message] from an encoded value produced via [toJson].
+ factory Message.fromJson(data) => new Message(
+ new MessageId.fromJson(data['id']), data['snippet']);
+}
+
+/// Template for a message. Templates can include placeholders to indicate
+/// values that are different for each instance of the error. Calling [create]
+/// will generate the actual message, with the placeholders replaced with
+/// values. If there are no placeholders, an instance of [MessageTemplate] is a
+/// valid instance of [Message] as well.
+class MessageTemplate implements Message {
+ /// Unique and stable id for the message.
+ final MessageId id;
+
+ /// Template message with placeholders of the form `%-name-%`.
+ final String snippetTemplate;
+
+ /// This returns the message snippet, only if it the template has no
+ /// placeholders, otherwise this throws an exception. Most messages have no
+ /// placeholder arguments, in those cases, the snippet can be computed
+ /// without specifying any arguments (exactly like calling `create()` with no
+ /// arguments).
+ String get snippet => _createSnippet();
+
+ /// Short description of the error message, typically used as a title of the
+ /// error message in autogenerated documentation. This should be a single
+ /// phrase, and cannot use placeholders.
+ final String description;
+
+ /// Additional details about this error message. These are used to
+ /// automatically generate documentation.
+ final String details;
+
+ const MessageTemplate(
+ this.id, this.snippetTemplate, this.description, this.details);
+
+ static final _placeholderPattern = new RegExp(r"%-(\w*)-%");
+
+ _createSnippet([Map args = const {}, bool fillUnknowns = false]) {
+ var snippet = snippetTemplate.replaceAllMapped(_placeholderPattern, (m) {
+ var arg = m.group(1);
+ var value = args[arg];
+ if (value != null) return '$value';
+ if (fillUnknowns) return '';
+ throw "missing argument $arg, for error message: $snippetTemplate";
+ });
+ return snippet;
+ }
+
+ create([Map args = const {}, bool fillUnknowns = false]) =>
+ new Message(id, _createSnippet(args, fillUnknowns));
+
+ /// Serializes this message to JSON.
+ Map toJson() => create().toJson();
+ String toString() => '${toJson()}';
+}
+
+/// Represents an actual log entry for a build error message. Including the
+/// actual message, its severity level (warning, error, etc), and a source span
+/// for a code location that is revelant to the message.
+class BuildLogEntry {
+ /// The actual message.
+ final Message message;
+
+ /// Severity level.
+ final String level;
+
+ /// Location associated with this message, if any.
+ final SourceSpan span;
+
+ BuildLogEntry(this.message, this.span, this.level);
+
+ /// Creates a new [BuildLogEntry] from an encoded value produced via [toJson].
+ factory BuildLogEntry.fromJson(Map data) {
+ var spanData = data['span'];
+ var span = null;
+ if (spanData != null) {
+ var locData = spanData['start'];
+ var start = new SourceLocation(locData['offset'],
+ sourceUrl: Uri.parse(locData['url']),
+ line: locData['line'],
+ column: locData['column']);
+ locData = spanData['end'];
+ var end = new SourceLocation(locData['offset'],
+ sourceUrl: Uri.parse(locData['url']),
+ line: locData['line'],
+ column: locData['column']);
+ span = new SourceSpan(start, end, spanData['text']);
+ }
+ return new BuildLogEntry(
+ new Message.fromJson(data['message']), span, data['level']);
+ }
+
+ /// Serializes this log entry to JSON.
+ Map toJson() {
+ var data = {
+ 'level': level,
+ 'message': message.toJson(),
+ };
+ if (span != null) {
+ data['span'] = {
+ 'start': {
+ 'url': span.start.sourceUrl.toString(),
+ 'offset': span.start.offset,
+ 'line': span.start.line,
+ 'column': span.start.column,
+ },
+ 'end': {
+ 'url': span.end.sourceUrl.toString(),
+ 'offset': span.end.offset,
+ 'line': span.end.line,
+ 'column': span.end.column,
+ },
+ 'text': span.text,
+ };
+ }
+ return data;
+ }
+ String toString() => '${toJson()}';
+}
+
+/// A table of entries, that clusters error messages by id.
+class LogEntryTable {
+ final Map<MessageId, List<BuildLogEntry>> entries;
+
+ LogEntryTable() : entries = new LinkedHashMap();
+
+ /// Creates a new [LogEntryTable] from an encoded value produced via [toJson].
+ factory LogEntryTable.fromJson(Map json) {
+ var res = new LogEntryTable();
+ for (String key in json.keys) {
+ var id = new MessageId.fromJson(key);
+ res.entries[id] = json[key]
+ .map((v) => new BuildLogEntry.fromJson(v)).toList();
+ }
+ return res;
+ }
+
+
+ /// Serializes this entire table as JSON.
+ Map toJson() {
+ var res = {};
+ entries.forEach((key, value) {
+ res['$key'] = value.map((e) => e.toJson()).toList();
+ });
+ return res;
+ }
+ String toString() => '${toJson()}';
+
+ void add(BuildLogEntry entry) {
+ entries.putIfAbsent(entry.message.id, () => []).add(entry);
+ }
+ void addAll(LogEntryTable other) {
+ for (var key in other.entries.keys) {
+ var values = entries.putIfAbsent(key, () => []);
+ values.addAll(other.entries[key]);
+ }
+ }
+}
« no previous file with comments | « pkg/code_transformers/lib/messages/build_logger.dart ('k') | pkg/code_transformers/lib/src/messages.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698