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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// Defines messages templates and an adapter for TransformLogger to be able
6 /// report error messages from transformers and refer to them in a consistent
7 /// manner long term.
8 library code_transformers.messages;
9
10 // Note: this library purposely doesn't depend on dart:io, dart:html, or barback
11 // so it can easily be used both in the transformers and in client-side apps
12 // (for example in the log_injector).
13 import 'dart:collection' show LinkedHashMap;
14 import 'package:source_span/source_span.dart';
15
16 /// A globally unique identifier for an error message. This identifier should be
17 /// stable, that is, it should never change after it is asigned to a particular
18 /// message. That allows us to document our error messages and make them
19 /// searchable for prosperity.
20 class MessageId implements Comparable {
21 /// Name of the package that declares this message.
22 final String package;
23
24 /// Message identifier number, unique within the package.
25 final int id;
26
27 const MessageId(this.package, this.id);
28
29 static const MessageId NOT_SPECIFIED = const MessageId('unknown', 0);
30
31 /// Serialize this message as a String
32 String toJson() => toString();
33
34 toString() => '${package}_$id';
jakemac 2014/09/03 17:20:41 imo, a ':' would be better than an '_'
Siggi Cherem (dart-lang) 2014/09/04 02:32:13 yeah - just trying to use something that doesn't r
Siggi Cherem (dart-lang) 2014/09/04 19:55:11 All set - I went with # here, but do a separate se
35
36 int compareTo(MessageId other) {
37 var res = package.compareTo(other.package);
38 if (res != 0) return res;
39 return id.compareTo(other.id);
40 }
41
42 /// Creates a new [MessageId] from an encoded value produced via [toJson].
43 factory MessageId.fromJson(data) {
44 var index = data.lastIndexOf('_');
45 if (index == -1) throw 'Invalid message id: $data';
46 return new MessageId(data.substring(0, index),
47 int.parse(data.substring(index + 1)));
48 }
49
50 operator ==(MessageId other) => package == other.package && id == other.id;
51 int get hashCode => 31 * package.hashCode + id;
52 }
53
54 /// An instance of an error message. These are typically produced from a
55 /// [MessageTemplate].
56 class Message {
57 /// A globally unique identifier for this message.
58 final MessageId id;
59
60 /// A snippet message that is presented to the user.
61 final String snippet;
62
63 const Message(this.id, this.snippet);
jakemac 2014/09/03 17:20:41 A named constructor or factory that takes no ID an
Siggi Cherem (dart-lang) 2014/09/04 02:32:13 Done.
64
65 /// Serializes this message to JSON.
66 Map toJson() => {'id': id.toJson(), 'snippet': snippet};
67 String toString() => '${toJson()}';
jakemac 2014/09/03 17:20:41 I think something like '$id: $snippet' might be be
Siggi Cherem (dart-lang) 2014/09/04 02:32:13 Done.
68
69 /// Creates a new [Message] from an encoded value produced via [toJson].
70 factory Message.fromJson(data) => new Message(
71 new MessageId.fromJson(data['id']), data['snippet']);
72 }
73
74 /// Template for a message. Templates can include placeholders to indicate
75 /// values that are different for each instance of the error. Calling [create]
76 /// will generate the actual message, with the placeholders replaced with
77 /// values. If there are no placeholders, an instance of [MessageTemplate] is a
78 /// valid instance of [Message] as well.
79 class MessageTemplate implements Message {
80 /// Unique and stable id for the message.
81 final MessageId id;
82
83 /// Template message with placeholders of the form `%-name-%`.
84 final String snippetTemplate;
85
86 /// This returns the message snippet, only if it the template has no
87 /// placeholders, otherwise this throws an exception. Most messages have no
88 /// placeholder arguments, in those cases, the snippet can be computed
89 /// without specifying any arguments (exactly like calling `create()` with no
90 /// arguments).
91 String get snippet => _createSnippet();
92
93 /// Short description of the error message
jakemac 2014/09/03 17:20:41 How does this differ from snippetTemplate and deta
Siggi Cherem (dart-lang) 2014/09/04 02:32:13 added details - basically it should be the title w
94 final String description;
95
96 /// Additional details about this error message. These are used to
97 /// automatically generate documentation.
98 final String details;
99
100 const MessageTemplate(
101 this.id, this.snippetTemplate, this.description, this.details);
102
103 static final _placeholderPattern = new RegExp(r"%-(\w*)-%");
104
105 _createSnippet([Map args = const {}, bool fillUnknowns = false]) {
106 var snippet = snippetTemplate.replaceAllMapped(_placeholderPattern, (m) {
107 var arg = m.group(1);
108 var value = args[arg];
109 if (value != null) return '$value';
110 if (fillUnknowns) return '';
111 throw "missing argument $arg, for error message: $snippetTemplate";
112 });
113 return snippet;
114 }
115
116 create([Map args = const {}, bool fillUnknowns = false]) =>
117 new Message(id, _createSnippet(args, fillUnknowns));
118
119 /// Serializes this message to JSON.
120 Map toJson() => create().toJson();
121 String toString() => '${toJson()}';
122 }
123
124 /// Represents an actual log entry for a build error message. Including the
125 /// actual message, its severity level (warning, error, etc), and a source span
126 /// for a code location that is revelant to the message.
127 class BuildLogEntry {
128 /// The actual message.
129 final Message message;
130
131 /// Severity level.
132 final String level;
133
134 /// Location associated with this message, if any.
135 final SourceSpan span;
136
137 BuildLogEntry(this.message, this.span, this.level);
138
139 /// Creates a new [BuildLogEntry] from an encoded value produced via [toJson].
140 factory BuildLogEntry.fromJson(Map data) {
141 var spanData = data['span'];
142 var span = null;
143 if (spanData != null) {
144 var locData = spanData['start'];
145 var start = new SourceLocation(locData['offset'],
146 sourceUrl: Uri.parse(locData['url']),
147 line: locData['line'],
148 column: locData['column']);
149 locData = spanData['end'];
150 var end = new SourceLocation(locData['offset'],
151 sourceUrl: Uri.parse(locData['url']),
152 line: locData['line'],
153 column: locData['column']);
154 span = new SourceSpan(start, end, spanData['text']);
155 }
156 return new BuildLogEntry(
157 new Message.fromJson(data['message']), span, data['level']);
158 }
159
160 /// Serializes this log entry to JSON.
161 Map toJson() {
162 var data = {
163 'level': level,
164 'message': message.toJson(),
165 };
166 if (span != null) {
167 data['span'] = {
168 'start': {
169 'url': span.start.sourceUrl.toString(),
170 'offset': span.start.offset,
171 'line': span.start.line,
172 'column': span.start.column,
173 },
174 'end': {
175 'url': span.end.sourceUrl.toString(),
176 'offset': span.end.offset,
177 'line': span.end.line,
178 'column': span.end.column,
179 },
180 'text': span.text,
181 };
182 }
183 return data;
184 }
185 String toString() => '${toJson()}';
186 }
187
188 /// A table of entries, that clusters error messages by id.
189 class LogEntryTable {
190 final Map<MessageId, List<BuildLogEntry>> entries;
191
192 LogEntryTable() : entries = new LinkedHashMap();
193
194 /// Creates a new [LogEntryTable] from an encoded value produced via [toJson].
195 factory LogEntryTable.fromJson(Map json) {
196 var res = new LogEntryTable();
197 for (String key in json.keys) {
198 var id = new MessageId.fromJson(key);
199 res.entries[id] = json[key]
200 .map((v) => new BuildLogEntry.fromJson(v)).toList();
201 }
202 return res;
203 }
204
205
206 /// Serializes this entire table as JSON.
207 Map toJson() {
208 var res = {};
209 entries.forEach((key, value) {
210 res['$key'] = value.map((e) => e.toJson()).toList();
211 });
212 return res;
213 }
214 String toString() => '${toJson()}';
215
216 void add(BuildLogEntry entry) {
217 entries.putIfAbsent(entry.message.id, () => []).add(entry);
218 }
219 void addAll(LogEntryTable other) {
220 for (var key in other.entries.keys) {
221 var values = entries.putIfAbsent(key, () => []);
222 values.addAll(other.entries[key]);
223 }
224 }
225 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698