161 lines
4.7 KiB
Dart
161 lines
4.7 KiB
Dart
// Copyright (c) 2016, 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.
|
|
|
|
const int _LF = 0x0A;
|
|
const int _CR = 0x0D;
|
|
const int _LBRACE = 0x7B;
|
|
|
|
class Annotation {
|
|
/// 1-based line number of the annotation.
|
|
final int lineNo;
|
|
|
|
/// 1-based column number of the annotation.
|
|
final int columnNo;
|
|
|
|
/// 0-based character offset of the annotation within the source text.
|
|
final int offset;
|
|
|
|
/// The text in the annotation.
|
|
final String text;
|
|
|
|
Annotation(this.lineNo, this.columnNo, this.offset, this.text);
|
|
}
|
|
|
|
/// A source code text with annotated positions.
|
|
///
|
|
/// An [AnnotatedCode] can be created from a [String] of source code where
|
|
/// annotated positions are embedded using the syntax `@{text}`. For instance
|
|
///
|
|
/// main() {
|
|
/// @{foo-call}foo();
|
|
/// bar@{bar-args}();
|
|
/// }
|
|
///
|
|
/// the position of `foo` call will hold an annotation with text 'foo-call' and
|
|
/// the position of `bar` arguments will hold an annotation with text
|
|
/// 'bar-args'.
|
|
///
|
|
/// Annotation text cannot span multiple lines and cannot contain '}'.
|
|
class AnnotatedCode {
|
|
/// The source code without annotations.
|
|
final String sourceCode;
|
|
|
|
/// The annotations for the source code.
|
|
final List<Annotation> annotations;
|
|
|
|
List<int> _lineStarts;
|
|
|
|
AnnotatedCode(this.sourceCode, this.annotations);
|
|
|
|
AnnotatedCode.internal(this.sourceCode, this.annotations, this._lineStarts);
|
|
|
|
/// Creates an [AnnotatedCode] by processing [annotatedCode]. Annotation of
|
|
/// the form `@{...}` are converted into [Annotation]s and removed from the
|
|
/// [annotatedCode] to process the source code.
|
|
factory AnnotatedCode.fromText(String annotatedCode) {
|
|
StringBuffer codeBuffer = new StringBuffer();
|
|
List<Annotation> annotations = <Annotation>[];
|
|
int index = 0;
|
|
int offset = 0;
|
|
int lineNo = 1;
|
|
int columnNo = 1;
|
|
List<int> lineStarts = <int>[];
|
|
lineStarts.add(offset);
|
|
while (index < annotatedCode.length) {
|
|
int charCode = annotatedCode.codeUnitAt(index);
|
|
switch (charCode) {
|
|
case _LF:
|
|
codeBuffer.write('\n');
|
|
offset++;
|
|
lineStarts.add(offset);
|
|
lineNo++;
|
|
columnNo = 1;
|
|
break;
|
|
case _CR:
|
|
if (index + 1 < annotatedCode.length &&
|
|
annotatedCode.codeUnitAt(index + 1) == _LF) {
|
|
index++;
|
|
}
|
|
codeBuffer.write('\n');
|
|
offset++;
|
|
lineStarts.add(offset);
|
|
lineNo++;
|
|
columnNo = 1;
|
|
break;
|
|
case 0x40:
|
|
if (index + 1 < annotatedCode.length &&
|
|
annotatedCode.codeUnitAt(index + 1) == _LBRACE) {
|
|
int endIndex = annotatedCode.indexOf('}', index);
|
|
String text = annotatedCode.substring(index + 2, endIndex);
|
|
annotations.add(new Annotation(lineNo, columnNo, offset, text));
|
|
index = endIndex;
|
|
} else {
|
|
codeBuffer.writeCharCode(charCode);
|
|
offset++;
|
|
columnNo++;
|
|
}
|
|
break;
|
|
default:
|
|
codeBuffer.writeCharCode(charCode);
|
|
offset++;
|
|
columnNo++;
|
|
}
|
|
index++;
|
|
}
|
|
lineStarts.add(offset);
|
|
return new AnnotatedCode.internal(
|
|
codeBuffer.toString(), annotations, lineStarts);
|
|
}
|
|
|
|
void _ensureLineStarts() {
|
|
if (_lineStarts == null) {
|
|
int index = 0;
|
|
int offset = 0;
|
|
_lineStarts = <int>[];
|
|
_lineStarts.add(offset);
|
|
while (index < sourceCode.length) {
|
|
int charCode = sourceCode.codeUnitAt(index);
|
|
switch (charCode) {
|
|
case _LF:
|
|
offset++;
|
|
_lineStarts.add(offset);
|
|
break;
|
|
case _CR:
|
|
if (index + 1 < sourceCode.length &&
|
|
sourceCode.codeUnitAt(index + 1) == _LF) {
|
|
index++;
|
|
}
|
|
offset++;
|
|
_lineStarts.add(offset);
|
|
break;
|
|
default:
|
|
offset++;
|
|
}
|
|
index++;
|
|
}
|
|
_lineStarts.add(offset);
|
|
}
|
|
}
|
|
|
|
void addAnnotation(int lineNo, int columnNo, String text) {
|
|
_ensureLineStarts();
|
|
int offset = _lineStarts[lineNo - 1] + (columnNo - 1);
|
|
annotations.add(new Annotation(lineNo, columnNo, offset, text));
|
|
}
|
|
|
|
String toText() {
|
|
StringBuffer sb = new StringBuffer();
|
|
List<Annotation> list = annotations.toList()
|
|
..sort((a, b) => a.offset.compareTo(b.offset));
|
|
int offset = 0;
|
|
for (Annotation annotation in list) {
|
|
sb.write(sourceCode.substring(offset, annotation.offset));
|
|
sb.write('@{${annotation.text}}');
|
|
offset = annotation.offset;
|
|
}
|
|
sb.write(sourceCode.substring(offset));
|
|
return sb.toString();
|
|
}
|
|
}
|