Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly indent multi-line labels for mutli-span highlights #86

Merged
merged 3 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# 1.9.1-dev
# 1.9.1

* Properly handle multi-line labels for multi-span highlights.

* Populate the pubspec `repository` field.

Expand Down
1 change: 0 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ linter:
- file_names
- hash_and_equals
- implementation_imports
- invariant_booleans
- iterable_contains_unrelated_type
- library_names
- library_prefixes
Expand Down
66 changes: 53 additions & 13 deletions lib/src/highlighter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,13 @@ class Highlighter {
_writeMultilineHighlights(line, highlightsByColumn, current: highlight);
if (highlightsByColumn.isNotEmpty) _buffer.write(' ');

_colorize(() {
final underlineLength = _colorize(() {
final start = _buffer.length;
_writeUnderline(line, highlight.span,
highlight.isPrimary ? '^' : glyph.horizontalLineBold);
_writeLabel(highlight.label);
return _buffer.length - start;
}, color: color);
_buffer.writeln();
_writeLabel(highlight, highlightsByColumn, underlineLength);
} else if (highlight.span.start.line == line.number) {
if (highlightsByColumn.contains(highlight)) return;
replaceFirstNull(highlightsByColumn, highlight);
Expand All @@ -394,16 +395,17 @@ class Highlighter {
_buffer.write(' ');
_writeMultilineHighlights(line, highlightsByColumn, current: highlight);

_colorize(() {
final underlineLength = _colorize(() {
final start = _buffer.length;
if (coversWholeLine) {
_buffer.write(glyph.horizontalLine * 3);
} else {
_writeArrow(line, math.max(highlight.span.end.column - 1, 0),
beginning: false);
}
_writeLabel(highlight.label);
return _buffer.length - start;
}, color: color);
_buffer.writeln();
_writeLabel(highlight, highlightsByColumn, underlineLength);
replaceWithNull(highlightsByColumn, highlight);
}
}
Expand Down Expand Up @@ -442,9 +444,45 @@ class Highlighter {
..write('^');
}

/// Writes a space followed by [label] if [label] isn't `null`.
void _writeLabel(String? label) {
if (label != null) _buffer.write(' $label');
/// Writes [highlight]'s label.
///
/// The `_buffer` is assumed to be written to the point where the first line
/// of `highlight.label` can be written after a space, but this takes care of
/// writing indentation and highlight columns for later lines.
///
/// The [highlightsByColumn] are used to write ongoing highlight lines if the
/// label is more than one line long.
///
/// The [underlineLength] is the length of the line written between the
/// highlights and the beginning of the first label.
void _writeLabel(_Highlight highlight, List<_Highlight?> highlightsByColumn,
nex3 marked this conversation as resolved.
Show resolved Hide resolved
int underlineLength) {
final label = highlight.label;
if (label == null) {
_buffer.writeln();
return;
}

final lines = label.split('\n');
jakemac53 marked this conversation as resolved.
Show resolved Hide resolved
final color = highlight.isPrimary ? _primaryColor : _secondaryColor;
_colorize(() => _buffer.write(' ${lines.first}'), color: color);
_buffer.writeln();

for (var text in lines.skip(1)) {
_writeSidebar();
_buffer.write(' ');
for (var columnHighlight in highlightsByColumn) {
jakemac53 marked this conversation as resolved.
Show resolved Hide resolved
if (columnHighlight == null || columnHighlight == highlight) {
_buffer.write(' ');
} else {
_buffer.write(glyph.verticalLine);
}
}

_buffer.write(' ' * underlineLength);
_colorize(() => _buffer.write(' $text'), color: color);
_buffer.writeln();
}
}

/// Writes a snippet from the source text, converting hard tab characters into
Expand Down Expand Up @@ -496,10 +534,11 @@ class Highlighter {

/// Colors all text written to [_buffer] during [callback], if colorization is
/// enabled and [color] is not `null`.
void _colorize(void Function() callback, {required String? color}) {
T _colorize<T>(T Function() callback, {required String? color}) {
if (_primaryColor != null && color != null) _buffer.write(color);
callback();
final result = callback();
if (_primaryColor != null && color != null) _buffer.write(colors.none);
return result;
}
}

Expand All @@ -522,14 +561,15 @@ class _Highlight {
/// used in the same message.
final String? label;

_Highlight(SourceSpan span, {this.label, bool primary = false})
_Highlight(SourceSpan span, {String? label, bool primary = false})
: span = (() {
var newSpan = _normalizeContext(span);
newSpan = _normalizeNewlines(newSpan);
newSpan = _normalizeTrailingNewline(newSpan);
return _normalizeEndOfLine(newSpan);
})(),
isPrimary = primary;
isPrimary = primary,
label = label?.replaceAll('\r\n', '\n');

/// Normalizes [span] to ensure that it's a [SourceSpanWithContext] whose
/// context actually contains its text at the expected column.
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: source_span
version: 1.9.1-dev
version: 1.9.1
description: A library for identifying source spans and locations.
repository: https://github.com/dart-lang/source_span

Expand Down
96 changes: 96 additions & 0 deletions test/multiple_highlight_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,100 @@ quibble bibble boop
| === two
'"""));
});

group('indents mutli-line labels', () {
test('for the primary label', () {
expect(file.span(17, 21).highlightMultiple('line 1\nline 2\nline 3', {}),
equals("""
,
2 | whiz bang boom
| ^^^^ line 1
| line 2
| line 3
'"""));
});

group('for a secondary label', () {
test('on the same line', () {
expect(
file.span(17, 21).highlightMultiple(
'primary', {file.span(22, 26): 'line 1\nline 2\nline 3'}),
equals("""
,
2 | whiz bang boom
| ^^^^ primary
| ==== line 1
| line 2
| line 3
'"""));
});

test('on a different line', () {
expect(
file.span(17, 21).highlightMultiple(
'primary', {file.span(31, 34): 'line 1\nline 2\nline 3'}),
equals("""
,
2 | whiz bang boom
| ^^^^ primary
3 | zip zap zop
| === line 1
| line 2
| line 3
'"""));
});
});

group('for a multiline span', () {
test('that covers the whole last line', () {
expect(
file.span(12, 70).highlightMultiple('line 1\nline 2\nline 3', {}),
equals("""
,
2 | / whiz bang boom
3 | | zip zap zop
4 | | fwee fwoo fwip
5 | | argle bargle boo
| '--- line 1
| line 2
| line 3
'"""));
});

test('that covers part of the last line', () {
expect(
file.span(12, 66).highlightMultiple('line 1\nline 2\nline 3', {}),
equals("""
,
2 | / whiz bang boom
3 | | zip zap zop
4 | | fwee fwoo fwip
5 | | argle bargle boo
| '------------^ line 1
| line 2
| line 3
'"""));
});
});

test('with an overlapping span', () {
expect(
file.span(12, 70).highlightMultiple('line 1\nline 2\nline 3',
{file.span(54, 89): 'two', file.span(0, 27): 'three'}),
equals("""
,
1 | /- foo bar baz
2 | |/ whiz bang boom
| '+--- three
3 | | zip zap zop
4 | | fwee fwoo fwip
5 | /+ argle bargle boo
| |'--- line 1
| | line 2
| | line 3
6 | | gibble bibble bop
| '---- two
'"""));
});
});
}