Line data Source code
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 : import 'dart:math' as math;
6 :
7 : import 'package:charcode/charcode.dart';
8 : import 'package:path/path.dart' as p;
9 :
10 : import 'colors.dart' as colors;
11 : import 'span.dart';
12 : import 'span_with_context.dart';
13 : import 'utils.dart';
14 :
15 : /// A mixin for easily implementing [SourceSpan].
16 : ///
17 : /// This implements the [SourceSpan] methods in terms of [start], [end], and
18 : /// [text]. This assumes that [start] and [end] have the same source URL, that
19 : /// [start] comes before [end], and that [text] has a number of characters equal
20 : /// to the distance between [start] and [end].
21 : abstract class SourceSpanMixin implements SourceSpan {
22 0 : Uri get sourceUrl => start.sourceUrl;
23 0 : int get length => end.offset - start.offset;
24 :
25 : int compareTo(SourceSpan other) {
26 0 : var result = start.compareTo(other.start);
27 0 : return result == 0 ? end.compareTo(other.end) : result;
28 : }
29 :
30 : SourceSpan union(SourceSpan other) {
31 0 : if (sourceUrl != other.sourceUrl) {
32 0 : throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
33 0 : " \"${other.sourceUrl}\" don't match.");
34 : }
35 :
36 0 : var start = min(this.start, other.start);
37 0 : var end = max(this.end, other.end);
38 0 : var beginSpan = start == this.start ? this : other;
39 0 : var endSpan = end == this.end ? this : other;
40 :
41 0 : if (beginSpan.end.compareTo(endSpan.start) < 0) {
42 0 : throw new ArgumentError("Spans $this and $other are disjoint.");
43 : }
44 :
45 0 : var text = beginSpan.text +
46 0 : endSpan.text.substring(beginSpan.end.distance(endSpan.start));
47 0 : return new SourceSpan(start, end, text);
48 : }
49 :
50 : String message(String message, {color}) {
51 0 : var buffer = new StringBuffer();
52 0 : buffer.write('line ${start.line + 1}, column ${start.column + 1}');
53 0 : if (sourceUrl != null) buffer.write(' of ${p.prettyUri(sourceUrl)}');
54 0 : buffer.write(': $message');
55 :
56 0 : var highlight = this.highlight(color: color);
57 0 : if (!highlight.isEmpty) {
58 0 : buffer.writeln();
59 0 : buffer.write(highlight);
60 : }
61 :
62 0 : return buffer.toString();
63 : }
64 :
65 : String highlight({color}) {
66 0 : if (color == true) color = colors.RED;
67 0 : if (color == false) color = null;
68 :
69 0 : var column = start.column;
70 0 : var buffer = new StringBuffer();
71 : String textLine;
72 0 : if (this is SourceSpanWithContext) {
73 0 : var context = (this as SourceSpanWithContext).context;
74 0 : var lineStart = findLineStart(context, text, column);
75 0 : if (lineStart != null && lineStart > 0) {
76 0 : buffer.write(context.substring(0, lineStart));
77 0 : context = context.substring(lineStart);
78 : }
79 0 : var endIndex = context.indexOf('\n');
80 0 : textLine = endIndex == -1 ? context : context.substring(0, endIndex + 1);
81 5 : column = math.min(column, textLine.length);
82 0 : } else if (length == 0) {
83 : return "";
84 : } else {
85 0 : textLine = text.split("\n").first;
86 : column = 0;
87 : }
88 :
89 : var toColumn =
90 5 : math.min(column + end.offset - start.offset, textLine.length);
91 : if (color != null) {
92 0 : buffer.write(textLine.substring(0, column));
93 0 : buffer.write(color);
94 0 : buffer.write(textLine.substring(column, toColumn));
95 0 : buffer.write(colors.NONE);
96 0 : buffer.write(textLine.substring(toColumn));
97 : } else {
98 0 : buffer.write(textLine);
99 : }
100 0 : if (!textLine.endsWith('\n')) buffer.write('\n');
101 :
102 0 : for (var i = 0; i < column; i++) {
103 0 : if (textLine.codeUnitAt(i) == $tab) {
104 0 : buffer.writeCharCode($tab);
105 : } else {
106 0 : buffer.writeCharCode($space);
107 : }
108 : }
109 :
110 0 : if (color != null) buffer.write(color);
111 5 : buffer.write('^' * math.max(toColumn - column, 1));
112 0 : if (color != null) buffer.write(colors.NONE);
113 0 : return buffer.toString();
114 : }
115 :
116 0 : bool operator ==(other) => other is SourceSpan &&
117 0 : start == other.start && end == other.end;
118 :
119 0 : int get hashCode => start.hashCode + (31 * end.hashCode);
120 :
121 0 : String toString() => '<$runtimeType: from $start to $end "$text">';
122 : }
|