| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'location.dart'; | |
| import 'span.dart'; | |
| import 'utils.dart'; | |
| /// A class that describes a segment of source text with additional context. | |
| class SourceSpanWithContext extends SourceSpanBase { | |
| // This is a getter so that subclasses can override it. | |
| /// Text around the span, which includes the line containing this span. | |
| String get context => _context; | |
| final String _context; | |
| /// Creates a new span from [start] to [end] (exclusive) containing [text], in | |
| /// the given [context]. | |
| /// | |
| /// [start] and [end] must have the same source URL and [start] must come | |
| /// before [end]. [text] must have a number of characters equal to the | |
| /// distance between [start] and [end]. [context] must contain [text], and | |
| /// [text] should start at `start.column` from the beginning of a line in | |
| /// [context]. | |
| SourceSpanWithContext( | |
| SourceLocation start, SourceLocation end, String text, this._context) | |
| : super(start, end, text) { | |
| if (!context.contains(text)) { | |
| throw ArgumentError('The context line "$context" must contain "$text".'); | |
| } | |
| if (findLineStart(context, text, start.column) == null) { | |
| throw ArgumentError('The span text "$text" must start at ' | |
| 'column ${start.column + 1} in a line within "$context".'); | |
| } | |
| } | |
| } | |
| // TODO(#52): Move these to instance methods in the next breaking release. | |
| /// Extension methods on the base [SourceSpan] API. | |
| extension SourceSpanWithContextExtension on SourceSpanWithContext { | |
| /// Returns a span from [start] code units (inclusive) to [end] code units | |
| /// (exclusive) after the beginning of this span. | |
| SourceSpanWithContext subspan(int start, [int? end]) { | |
| RangeError.checkValidRange(start, end, length); | |
| if (start == 0 && (end == null || end == length)) return this; | |
| final locations = subspanLocations(this, start, end); | |
| return SourceSpanWithContext( | |
| locations[0], locations[1], text.substring(start, end), context); | |
| } | |
| } |