Skip to content
This repository was archived by the owner on May 15, 2023. It is now read-only.

Commit 831e5e9

Browse files
author
Awjin Ahn
authored
Formatted alerts (#35)
1 parent b068083 commit 831e5e9

File tree

5 files changed

+187
-42
lines changed

5 files changed

+187
-42
lines changed

bin/dart_sass_embedded.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ void main(List<String> args) {
3939
request.style == InboundMessage_CompileRequest_OutputStyle.COMPRESSED
4040
? sass.OutputStyle.compressed
4141
: sass.OutputStyle.expanded;
42-
var logger = Logger(dispatcher, request.id);
42+
var color = request.alertColor ?? false;
43+
var ascii = request.alertAscii ?? false;
44+
var logger = Logger(dispatcher, request.id, color: color, ascii: ascii);
4345

4446
try {
4547
String result;
@@ -59,6 +61,7 @@ void main(List<String> args) {
5961
case InboundMessage_CompileRequest_Input.string:
6062
var input = request.string;
6163
result = sass.compileString(input.source,
64+
color: color,
6265
logger: logger,
6366
importers: importers,
6467
importer: _decodeImporter(dispatcher, request, input.importer),
@@ -72,6 +75,7 @@ void main(List<String> args) {
7275
case InboundMessage_CompileRequest_Input.path:
7376
try {
7477
result = sass.compile(request.path,
78+
color: color,
7579
logger: logger,
7680
importers: importers,
7781
functions: globalFunctions,
@@ -97,11 +101,14 @@ void main(List<String> args) {
97101
}
98102
return OutboundMessage_CompileResponse()..success = success;
99103
} on sass.SassException catch (error) {
104+
var formatted = withGlyphs(() => error.toString(color: color),
105+
ascii: request.alertAscii);
100106
return OutboundMessage_CompileResponse()
101107
..failure = (OutboundMessage_CompileResponse_CompileFailure()
102108
..message = error.message
103109
..span = protofySpan(error.span)
104-
..stackTrace = error.trace.toString());
110+
..stackTrace = error.trace.toString()
111+
..formatted = formatted);
105112
}
106113
});
107114
}

lib/src/logger.dart

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// MIT-style license that can be found in the LICENSE file or at
33
// https://opensource.org/licenses/MIT.
44

5+
import 'package:path/path.dart' as p;
56
import 'package:sass/sass.dart' as sass;
67
import 'package:source_span/source_span.dart';
78
import 'package:stack_trace/stack_trace.dart';
@@ -18,24 +19,65 @@ class Logger implements sass.Logger {
1819
/// The ID of the compilation to which this logger is passed.
1920
final int _compilationId;
2021

21-
Logger(this._dispatcher, this._compilationId);
22+
/// Whether the formatted message should contain terminal colors.
23+
final bool _color;
24+
25+
/// Whether the formatted message should use ASCII encoding.
26+
final bool _ascii;
27+
28+
Logger(this._dispatcher, this._compilationId,
29+
{bool color = false, bool ascii = false})
30+
: _color = color,
31+
_ascii = ascii;
2232

2333
void debug(String message, SourceSpan span) {
34+
var url =
35+
span.start.sourceUrl == null ? '-' : p.prettyUri(span.start.sourceUrl);
36+
var buffer = StringBuffer()
37+
..write('$url:${span.start.line + 1} ')
38+
..write(_color ? '\u001b[1mDebug\u001b[0m' : 'DEBUG')
39+
..writeln(': $message');
40+
2441
_dispatcher.sendLog(OutboundMessage_LogEvent()
2542
..compilationId = _compilationId
2643
..type = OutboundMessage_LogEvent_Type.DEBUG
2744
..message = message
28-
..span = protofySpan(span));
45+
..span = protofySpan(span)
46+
..formatted = buffer.toString());
2947
}
3048

3149
void warn(String message,
3250
{FileSpan span, Trace trace, bool deprecation = false}) {
51+
var formatted = withGlyphs(() {
52+
var buffer = new StringBuffer();
53+
if (_color) {
54+
buffer.write('\u001b[33m\u001b[1m');
55+
if (deprecation) buffer.write('Deprecation ');
56+
buffer.write('Warning\u001b[0m');
57+
} else {
58+
if (deprecation) buffer.write('DEPRECATION ');
59+
buffer.write('WARNING');
60+
}
61+
if (span == null) {
62+
buffer.writeln(': $message');
63+
} else if (trace != null) {
64+
buffer.writeln(': $message\n\n${span.highlight(color: _color)}');
65+
} else {
66+
buffer.writeln(' on ${span.message("\n" + message, color: _color)}');
67+
}
68+
if (trace != null) {
69+
buffer.writeln(indent(trace.toString().trimRight(), 4));
70+
}
71+
return buffer.toString();
72+
}, ascii: _ascii);
73+
3374
var event = OutboundMessage_LogEvent()
3475
..compilationId = _compilationId
3576
..type = deprecation
3677
? OutboundMessage_LogEvent_Type.DEPRECATION_WARNING
3778
: OutboundMessage_LogEvent_Type.WARNING
38-
..message = message;
79+
..message = message
80+
..formatted = formatted;
3981
if (span != null) event.span = protofySpan(span);
4082
if (trace != null) event.stackTrace = trace.toString();
4183
_dispatcher.sendLog(event);

lib/src/utils.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// MIT-style license that can be found in the LICENSE file or at
33
// https://opensource.org/licenses/MIT.
44

5+
import 'package:meta/meta.dart';
56
import 'package:sass/sass.dart' as sass;
67
import 'package:source_span/source_span.dart';
8+
import 'package:term_glyph/term_glyph.dart' as term_glyph;
79

810
import 'embedded_sass.pb.dart' as proto;
911
import 'embedded_sass.pb.dart' hide SourceSpan;
@@ -57,3 +59,17 @@ sass.Syntax syntaxToSyntax(InboundMessage_Syntax syntax) {
5759
throw "Unknown syntax $syntax.";
5860
}
5961
}
62+
63+
/// Returns [string] with every line indented [indentation] spaces.
64+
String indent(String string, int indentation) =>
65+
string.split("\n").map((line) => (" " * indentation) + line).join("\n");
66+
67+
/// Returns the result of running [callback] with the global ASCII config set
68+
/// to [ascii].
69+
T withGlyphs<T>(T callback(), {@required bool ascii}) {
70+
var currentConfig = term_glyph.ascii;
71+
term_glyph.ascii = ascii;
72+
var result = callback();
73+
term_glyph.ascii = currentConfig;
74+
return result;
75+
}

test/protocol_test.dart

Lines changed: 113 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,6 @@ void main() {
9797
await expectLater(process.outbound, emits(isSuccess(equals("a{b:3px}"))));
9898
await process.kill();
9999
});
100-
101-
test("expanded mode when nested mode is passed", () async {
102-
process.inbound.add(compileString("a {b: 1px + 2px}",
103-
style: InboundMessage_CompileRequest_OutputStyle.NESTED));
104-
await expectLater(
105-
process.outbound, emits(isSuccess(equals("a {\n b: 3px;\n}"))));
106-
await process.kill();
107-
});
108-
109-
test("expanded mode when compact mode is passed", () async {
110-
process.inbound.add(compileString("a {b: 1px + 2px}",
111-
style: InboundMessage_CompileRequest_OutputStyle.COMPACT));
112-
await expectLater(
113-
process.outbound, emits(isSuccess(equals("a {\n b: 3px;\n}"))));
114-
await process.kill();
115-
});
116100
});
117101

118102
test("doesn't include a source map by default", () async {
@@ -145,31 +129,74 @@ void main() {
145129
});
146130

147131
group("emits a log event", () {
148-
test("for a @debug rule", () async {
149-
process.inbound.add(compileString("a {@debug hello}"));
132+
group("for a @debug rule", () {
133+
test("with correct fields", () async {
134+
process.inbound.add(compileString("a {@debug hello}"));
135+
136+
var logEvent = getLogEvent(await process.outbound.next);
137+
expect(logEvent.compilationId, equals(0));
138+
expect(logEvent.type, equals(OutboundMessage_LogEvent_Type.DEBUG));
139+
expect(logEvent.message, equals("hello"));
140+
expect(logEvent.span.text, equals("@debug hello"));
141+
expect(logEvent.span.start, equals(location(3, 0, 3)));
142+
expect(logEvent.span.end, equals(location(15, 0, 15)));
143+
expect(logEvent.span.context, equals("a {@debug hello}"));
144+
expect(logEvent.stackTrace, isEmpty);
145+
expect(logEvent.formatted, equals('-:1 DEBUG: hello\n'));
146+
await process.kill();
147+
});
150148

151-
var logEvent = getLogEvent(await process.outbound.next);
152-
expect(logEvent.compilationId, equals(0));
153-
expect(logEvent.type, equals(OutboundMessage_LogEvent_Type.DEBUG));
154-
expect(logEvent.message, equals("hello"));
155-
expect(logEvent.span.text, equals("@debug hello"));
156-
expect(logEvent.span.start, equals(location(3, 0, 3)));
157-
expect(logEvent.span.end, equals(location(15, 0, 15)));
158-
expect(logEvent.span.context, equals("a {@debug hello}"));
159-
expect(logEvent.stackTrace, isEmpty);
160-
await process.kill();
149+
test("formatted with terminal colors", () async {
150+
process.inbound
151+
.add(compileString("a {@debug hello}", alertColor: true));
152+
var logEvent = getLogEvent(await process.outbound.next);
153+
expect(
154+
logEvent.formatted, equals('-:1 \u001b[1mDebug\u001b[0m: hello\n'));
155+
await process.kill();
156+
});
161157
});
162158

163-
test("for a @warn rule", () async {
164-
process.inbound.add(compileString("a {@warn hello}"));
159+
group("for a @warn rule", () {
160+
test("with correct fields", () async {
161+
process.inbound.add(compileString("a {@warn hello}"));
165162

166-
var logEvent = getLogEvent(await process.outbound.next);
167-
expect(logEvent.compilationId, equals(0));
168-
expect(logEvent.type, equals(OutboundMessage_LogEvent_Type.WARNING));
169-
expect(logEvent.message, equals("hello"));
170-
expect(logEvent.span, equals(SourceSpan()));
171-
expect(logEvent.stackTrace, equals("- 1:4 root stylesheet\n"));
172-
await process.kill();
163+
var logEvent = getLogEvent(await process.outbound.next);
164+
expect(logEvent.compilationId, equals(0));
165+
expect(logEvent.type, equals(OutboundMessage_LogEvent_Type.WARNING));
166+
expect(logEvent.message, equals("hello"));
167+
expect(logEvent.span, equals(SourceSpan()));
168+
expect(logEvent.stackTrace, equals("- 1:4 root stylesheet\n"));
169+
expect(
170+
logEvent.formatted,
171+
equals('WARNING: hello\n'
172+
' - 1:4 root stylesheet\n'));
173+
await process.kill();
174+
});
175+
176+
test("formatted with terminal colors", () async {
177+
process.inbound.add(compileString("a {@warn hello}", alertColor: true));
178+
var logEvent = getLogEvent(await process.outbound.next);
179+
expect(
180+
logEvent.formatted,
181+
equals('\x1B[33m\x1B[1mWarning\x1B[0m: hello\n'
182+
' - 1:4 root stylesheet\n'));
183+
await process.kill();
184+
});
185+
186+
test("encoded in ASCII", () async {
187+
process.inbound
188+
.add(compileString("a {@debug a && b}", alertAscii: true));
189+
var logEvent = getLogEvent(await process.outbound.next);
190+
expect(
191+
logEvent.formatted,
192+
equals('WARNING on line 1, column 13: \n'
193+
'In Sass, "&&" means two copies of the parent selector. You probably want to use "and" instead.\n'
194+
' ,\n'
195+
'1 | a {@debug a && b}\n'
196+
' | ^^\n'
197+
' \'\n'));
198+
await process.kill();
199+
});
173200
});
174201

175202
test("for a parse-time deprecation warning", () async {
@@ -343,5 +370,54 @@ a {
343370
expect(failure.stackTrace, equals("- 1:11 root stylesheet\n"));
344371
await process.kill();
345372
});
373+
374+
group("and provides a formatted", () {
375+
test("message", () async {
376+
process.inbound.add(compileString("a {b: 1px + 1em}"));
377+
378+
var failure = getCompileFailure(await process.outbound.next);
379+
expect(
380+
failure.formatted,
381+
equals('Error: 1px and 1em have incompatible units.\n'
382+
' ╷\n'
383+
'1 │ a {b: 1px + 1em}\n'
384+
' │ ^^^^^^^^^\n'
385+
' ╵\n'
386+
' - 1:7 root stylesheet'));
387+
await process.kill();
388+
});
389+
390+
test("message with terminal colors", () async {
391+
process.inbound
392+
.add(compileString("a {b: 1px + 1em}", alertColor: true));
393+
394+
var failure = getCompileFailure(await process.outbound.next);
395+
expect(
396+
failure.formatted,
397+
equals('Error: 1px and 1em have incompatible units.\n'
398+
'\x1B[34m ╷\x1B[0m\n'
399+
'\x1B[34m1 │\x1B[0m a {b: \x1B[31m1px + 1em\x1B[0m}\n'
400+
'\x1B[34m │\x1B[0m \x1B[31m ^^^^^^^^^\x1B[0m\n'
401+
'\x1B[34m ╵\x1B[0m\n'
402+
' - 1:7 root stylesheet'));
403+
await process.kill();
404+
});
405+
406+
test("message with ASCII encoding", () async {
407+
process.inbound
408+
.add(compileString("a {b: 1px + 1em}", alertAscii: true));
409+
410+
var failure = getCompileFailure(await process.outbound.next);
411+
expect(
412+
failure.formatted,
413+
equals('Error: 1px and 1em have incompatible units.\n'
414+
' ,\n'
415+
'1 | a {b: 1px + 1em}\n'
416+
' | ^^^^^^^^^\n'
417+
' \'\n'
418+
' - 1:7 root stylesheet'));
419+
await process.kill();
420+
});
421+
});
346422
});
347423
}

test/utils.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'embedded_process.dart';
1414
/// string.
1515
InboundMessage compileString(String css,
1616
{int id,
17+
bool alertColor,
18+
bool alertAscii,
1719
InboundMessage_Syntax syntax,
1820
InboundMessage_CompileRequest_OutputStyle style,
1921
String url,
@@ -32,6 +34,8 @@ InboundMessage compileString(String css,
3234
if (style != null) request.style = style;
3335
if (sourceMap != null) request.sourceMap = sourceMap;
3436
if (functions != null) request.globalFunctions.addAll(functions);
37+
if (alertColor != null) request.alertColor = alertColor;
38+
if (alertAscii != null) request.alertAscii = alertAscii;
3539

3640
return InboundMessage()..compileRequest = request;
3741
}

0 commit comments

Comments
 (0)