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 'package:path/path.dart' as p; 6 : 7 : import 'highlighter.dart'; 8 : import 'span.dart'; 9 : import 'span_with_context.dart'; 10 : import 'utils.dart'; 11 : 12 : /// A mixin for easily implementing [SourceSpan]. 13 : /// 14 : /// This implements the [SourceSpan] methods in terms of [start], [end], and 15 : /// [text]. This assumes that [start] and [end] have the same source URL, that 16 : /// [start] comes before [end], and that [text] has a number of characters equal 17 : /// to the distance between [start] and [end]. 18 : abstract class SourceSpanMixin implements SourceSpan { 19 0 : @override 20 0 : Uri? get sourceUrl => start.sourceUrl; 21 : 22 0 : @override 23 0 : int get length => end.offset - start.offset; 24 : 25 0 : @override 26 : int compareTo(SourceSpan other) { 27 0 : final result = start.compareTo(other.start); 28 0 : return result == 0 ? end.compareTo(other.end) : result; 29 : } 30 : 31 0 : @override 32 : SourceSpan union(SourceSpan other) { 33 0 : if (sourceUrl != other.sourceUrl) { 34 0 : throw ArgumentError('Source URLs \"$sourceUrl\" and ' 35 0 : " \"${other.sourceUrl}\" don't match."); 36 : } 37 : 38 0 : final start = min(this.start, other.start); 39 0 : final end = max(this.end, other.end); 40 0 : final beginSpan = start == this.start ? this : other; 41 0 : final endSpan = end == this.end ? this : other; 42 : 43 0 : if (beginSpan.end.compareTo(endSpan.start) < 0) { 44 0 : throw ArgumentError('Spans $this and $other are disjoint.'); 45 : } 46 : 47 0 : final text = beginSpan.text + 48 0 : endSpan.text.substring(beginSpan.end.distance(endSpan.start)); 49 0 : return SourceSpan(start, end, text); 50 : } 51 : 52 0 : @override 53 : String message(String message, {color}) { 54 0 : final buffer = StringBuffer() 55 0 : ..write('line ${start.line + 1}, column ${start.column + 1}'); 56 0 : if (sourceUrl != null) buffer.write(' of ${p.prettyUri(sourceUrl)}'); 57 0 : buffer.write(': $message'); 58 : 59 0 : final highlight = this.highlight(color: color); 60 0 : if (highlight.isNotEmpty) { 61 : buffer 62 0 : ..writeln() 63 0 : ..write(highlight); 64 : } 65 : 66 0 : return buffer.toString(); 67 : } 68 : 69 0 : @override 70 : String highlight({color}) { 71 0 : if (this is! SourceSpanWithContext && length == 0) return ''; 72 0 : return Highlighter(this, color: color).highlight(); 73 : } 74 : 75 0 : @override 76 : bool operator ==(other) => 77 0 : other is SourceSpan && start == other.start && end == other.end; 78 : 79 0 : @override 80 0 : int get hashCode => start.hashCode + (31 * end.hashCode); 81 : 82 0 : @override 83 0 : String toString() => '<$runtimeType: from $start to $end "$text">'; 84 : }