Skip to content

Commit 645fbc8

Browse files
Add CompilerOptions.onDiagnostic
Change-Id: I116eb4b3ce03ae50d8b314531e0b463d4fe58649 Reviewed-on: https://dart-review.googlesource.com/77362 Commit-Queue: Peter von der Ahé <[email protected]> Auto-Submit: Peter von der Ahé <[email protected]> Reviewed-by: Aske Simon Christensen <[email protected]>
1 parent 6361d9e commit 645fbc8

File tree

6 files changed

+201
-11
lines changed

6 files changed

+201
-11
lines changed

pkg/front_end/lib/src/api_prototype/compiler_options.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import 'byte_store.dart' show ByteStore, NullByteStore;
1616

1717
import 'compilation_message.dart' show CompilationMessage;
1818

19+
import 'diagnostic_message.dart' show DiagnosticMessageHandler;
20+
1921
import 'file_system.dart' show FileSystem;
2022

2123
import 'standard_file_system.dart' show StandardFileSystem;
@@ -24,6 +26,8 @@ export '../fasta/fasta_codes.dart' show FormattedMessage;
2426

2527
export '../fasta/severity.dart' show Severity;
2628

29+
export 'diagnostic_message.dart' show DiagnosticMessage;
30+
2731
/// Callback used to report errors encountered during compilation.
2832
typedef void ErrorHandler(CompilationMessage error);
2933

@@ -60,6 +64,8 @@ class CompilerOptions {
6064

6165
ProblemHandler onProblem;
6266

67+
DiagnosticMessageHandler onDiagnostic;
68+
6369
/// Whether messages should be reported using the compiler's internal
6470
/// reporting mechanism.
6571
///
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2018, 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+
library front_end.diagnostic_message;
6+
7+
import '../fasta/severity.dart' show Severity;
8+
9+
/// The type of a diagnostic message callback. For example:
10+
///
11+
/// void handler(DiagnosticMessage message) {
12+
/// if (enableTerminalColors) { // See [terminal_color_support.dart].
13+
/// message.ansiFormatted.forEach(stderr.writeln);
14+
/// } else {
15+
/// message.plainTextFormatted.forEach(stderr.writeln);
16+
/// }
17+
/// }
18+
typedef DiagnosticMessageHandler = void Function(DiagnosticMessage);
19+
20+
/// Represents a diagnostic message that can be reported from a tool, for
21+
/// example, a compiler.
22+
///
23+
/// The word *diagnostic* is used loosely here, as a tool may also use this for
24+
/// reporting any kind of message, including non-diagnostic messages such as
25+
/// licensing, informal, or logging information. This allows a well-behaved
26+
/// tool to never directly write to stdout or stderr.
27+
abstract class DiagnosticMessage {
28+
DiagnosticMessage._(); // Prevent subclassing.
29+
30+
Iterable<String> get ansiFormatted;
31+
32+
Iterable<String> get plainTextFormatted;
33+
34+
Severity get severity;
35+
36+
// TODO(ahe): Rename this, it's actually an error code.
37+
int get index;
38+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) 2018, 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+
library front_end.terminal_color_support;
6+
7+
import 'dart:convert' show jsonEncode;
8+
9+
import 'dart:io' show Platform, Process, ProcessResult, stderr, stdout;
10+
11+
import '../fasta/colors.dart' show ALL_CODES, TERMINAL_CAPABILITIES;
12+
13+
/// True if we should enable colors in output.
14+
///
15+
/// We enable colors only when both [stdout] and [stderr] support ANSI escapes.
16+
final bool enableTerminalColors = _computeEnableColors();
17+
18+
/// On Windows, colors are enabled if both stdout and stderr supports ANSI
19+
/// escapes. On other platforms, we rely on the external programs `tty` and
20+
/// `tput` to compute if ANSI colors are supported.
21+
bool _computeEnableColors() {
22+
const bool debug =
23+
const bool.fromEnvironment("front_end.debug_compute_enable_colors");
24+
25+
if (Platform.isWindows) {
26+
if (!stdout.supportsAnsiEscapes || !stderr.supportsAnsiEscapes) {
27+
// In this case, either [stdout] or [stderr] did not support the property
28+
// `supportsAnsiEscapes`. Since we do not have another way to determine
29+
// support for colors, we disable them.
30+
if (debug) {
31+
print("Not enabling colors as ANSI is not supported.");
32+
}
33+
return false;
34+
}
35+
if (debug) {
36+
print("Enabling colors as OS is Windows.");
37+
}
38+
return true;
39+
}
40+
41+
// We have to check if the terminal actually supports colors. Currently, to
42+
// avoid linking the Dart VM with ncurses, ANSI escape support is reduced to
43+
// `Platform.environment['TERM'].contains("xterm")`.
44+
45+
// Check if stdin is a terminal (TTY).
46+
ProcessResult result =
47+
Process.runSync("/bin/sh", ["-c", "tty > /dev/null 2> /dev/null"]);
48+
49+
if (result.exitCode != 0) {
50+
if (debug) {
51+
print("Not enabling colors, stdin isn't a terminal.");
52+
}
53+
return false;
54+
}
55+
56+
// The `-S` option of `tput` allows us to query multiple capabilities at
57+
// once.
58+
result = Process.runSync(
59+
"/bin/sh", ["-c", "printf '%s' '$TERMINAL_CAPABILITIES' | tput -S"]);
60+
61+
if (result.exitCode != 0) {
62+
if (debug) {
63+
print("Not enabling colors, running tput failed.");
64+
}
65+
return false;
66+
}
67+
68+
List<String> lines = result.stdout.split("\n");
69+
70+
if (lines.length != 2) {
71+
if (debug) {
72+
print("Not enabling colors, unexpected output from tput: "
73+
"${jsonEncode(result.stdout)}.");
74+
}
75+
return false;
76+
}
77+
78+
String numberOfColors = lines[0];
79+
if (int.parse(numberOfColors, onError: (_) => -1) < 8) {
80+
if (debug) {
81+
print("Not enabling colors, less than 8 colors supported: "
82+
"${jsonEncode(numberOfColors)}.");
83+
}
84+
return false;
85+
}
86+
87+
String allCodes = lines[1].trim();
88+
if (ALL_CODES != allCodes) {
89+
if (debug) {
90+
print("Not enabling colors, color codes don't match: "
91+
"${jsonEncode(ALL_CODES)} != ${jsonEncode(allCodes)}.");
92+
}
93+
return false;
94+
}
95+
96+
if (debug) {
97+
print("Enabling colors.");
98+
}
99+
return true;
100+
}

pkg/front_end/lib/src/base/processed_options.dart

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,26 +198,38 @@ class ProcessedOptions {
198198
(_raw.reportMessages ?? (_raw.onError == null));
199199
}
200200

201-
FormattedMessage format(LocatedMessage message, Severity severity) {
201+
FormattedMessage format(
202+
LocatedMessage message, Severity severity, List<LocatedMessage> context) {
202203
int offset = message.charOffset;
203204
Uri uri = message.uri;
204205
Location location = offset == -1 ? null : getLocation(uri, offset);
205206
String formatted =
206207
command_line_reporting.format(message, severity, location: location);
207-
return message.withFormatting(
208-
formatted, location?.line ?? -1, location?.column ?? -1);
208+
List<FormattedMessage> formattedContext;
209+
if (context != null && context.isNotEmpty) {
210+
formattedContext = new List<FormattedMessage>(context.length);
211+
for (int i = 0; i < context.length; i++) {
212+
formattedContext[i] = format(context[i], Severity.context, null);
213+
}
214+
}
215+
return message.withFormatting(formatted, location?.line ?? -1,
216+
location?.column ?? -1, severity, formattedContext);
209217
}
210218

211219
void report(LocatedMessage message, Severity severity,
212220
{List<LocatedMessage> context}) {
213221
context ??= const <LocatedMessage>[];
214-
if (_raw.onProblem != null) {
222+
if (_raw.onDiagnostic != null) {
223+
_raw.onDiagnostic(format(message, severity, context));
224+
return;
225+
} else if (_raw.onProblem != null) {
215226
List<FormattedMessage> formattedContext =
216227
new List<FormattedMessage>(context.length);
217228
for (int i = 0; i < context.length; i++) {
218-
formattedContext[i] = format(context[i], severity);
229+
formattedContext[i] = format(context[i], severity, null);
219230
}
220-
_raw.onProblem(format(message, severity), severity, formattedContext);
231+
_raw.onProblem(
232+
format(message, severity, null), severity, formattedContext);
221233
if (command_line_reporting.shouldThrowOn(severity)) {
222234
throw new DebugAbort(
223235
message.uri, message.charOffset, severity, StackTrace.current);

pkg/front_end/lib/src/fasta/colors.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ bool _supportsAnsiEscapes(sink) {
113113
/// Note: do not call this method directly, as it is expensive to
114114
/// compute. Instead, use [CompilerContext.enableColors].
115115
bool computeEnableColors(CompilerContext context) {
116+
// TODO(ahe): Remove this method.
117+
116118
bool stderrSupportsColors = _supportsAnsiEscapes(stdout);
117119
bool stdoutSupportsColors = _supportsAnsiEscapes(stderr);
118120

pkg/front_end/lib/src/fasta/fasta_codes.dart

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'package:kernel/ast.dart' show Constant, DartType;
88

99
import 'package:kernel/text/ast_to_text.dart' show NameSystem, Printer;
1010

11+
import '../api_prototype/diagnostic_message.dart' show DiagnosticMessage;
12+
1113
import '../scanner/token.dart' show Token;
1214

1315
import 'severity.dart' show Severity;
@@ -125,12 +127,14 @@ class LocatedMessage implements Comparable<LocatedMessage> {
125127
return message.compareTo(message);
126128
}
127129

128-
FormattedMessage withFormatting(String formatted, int line, int column) {
129-
return new FormattedMessage(this, formatted, line, column);
130+
FormattedMessage withFormatting(String formatted, int line, int column,
131+
Severity severity, List<FormattedMessage> relatedInformation) {
132+
return new FormattedMessage(
133+
this, formatted, line, column, severity, relatedInformation);
130134
}
131135
}
132136

133-
class FormattedMessage {
137+
class FormattedMessage implements DiagnosticMessage {
134138
final LocatedMessage locatedMessage;
135139

136140
final String formatted;
@@ -139,8 +143,13 @@ class FormattedMessage {
139143

140144
final int column;
141145

142-
const FormattedMessage(
143-
this.locatedMessage, this.formatted, this.line, this.column);
146+
@override
147+
final Severity severity;
148+
149+
final List<FormattedMessage> relatedInformation;
150+
151+
const FormattedMessage(this.locatedMessage, this.formatted, this.line,
152+
this.column, this.severity, this.relatedInformation);
144153

145154
Code get code => locatedMessage.code;
146155

@@ -149,6 +158,29 @@ class FormattedMessage {
149158
String get tip => locatedMessage.tip;
150159

151160
Map<String, dynamic> get arguments => locatedMessage.arguments;
161+
162+
Uri get uri => locatedMessage.uri;
163+
164+
int get charOffset => locatedMessage.charOffset;
165+
166+
int get length => locatedMessage.length;
167+
168+
@override
169+
Iterable<String> get ansiFormatted sync* {
170+
yield formatted;
171+
for (FormattedMessage m in relatedInformation) {
172+
yield m.formatted;
173+
}
174+
}
175+
176+
@override
177+
Iterable<String> get plainTextFormatted {
178+
// TODO(ahe): Implement this correctly.
179+
return ansiFormatted;
180+
}
181+
182+
@override
183+
int get index => code.index;
152184
}
153185

154186
String relativizeUri(Uri uri) {

0 commit comments

Comments
 (0)