@@ -524,6 +524,7 @@ pub(crate) enum DisplaySourceLine<'a> {
524
524
Content {
525
525
text : & ' a str ,
526
526
range : ( usize , usize ) , // meta information for annotation placement.
527
+ end_line : EndLine ,
527
528
} ,
528
529
/// An empty source line.
529
530
Empty ,
@@ -658,7 +659,8 @@ impl<'a> CursorLines<'a> {
658
659
}
659
660
}
660
661
661
- enum EndLine {
662
+ #[ derive( Copy , Clone , Debug , PartialEq ) ]
663
+ pub ( crate ) enum EndLine {
662
664
Eof = 0 ,
663
665
Crlf = 1 ,
664
666
Lf = 2 ,
@@ -847,13 +849,20 @@ fn format_header<'a>(
847
849
848
850
for item in body {
849
851
if let DisplayLine :: Source {
850
- line : DisplaySourceLine :: Content { text, range } ,
852
+ line :
853
+ DisplaySourceLine :: Content {
854
+ text,
855
+ range,
856
+ end_line,
857
+ } ,
851
858
lineno,
852
859
..
853
860
} = item
854
861
{
855
- if main_range >= range. 0 && main_range <= range. 1 {
856
- let char_column = text[ 0 ..( main_range - range. 0 ) ] . chars ( ) . count ( ) ;
862
+ if main_range >= range. 0 && main_range <= range. 1 + * end_line as usize {
863
+ let char_column = text[ 0 ..( main_range - range. 0 ) . min ( text. len ( ) ) ]
864
+ . chars ( )
865
+ . count ( ) ;
857
866
col = char_column + 1 ;
858
867
line_offset = lineno. unwrap_or ( 1 ) ;
859
868
break ;
@@ -927,8 +936,18 @@ fn fold_body(body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
927
936
let mut unhighlighed_lines = vec ! [ ] ;
928
937
for line in body {
929
938
match & line {
930
- DisplayLine :: Source { annotations, .. } => {
931
- if annotations. is_empty ( ) {
939
+ DisplayLine :: Source {
940
+ annotations,
941
+ inline_marks,
942
+ ..
943
+ } => {
944
+ if annotations. is_empty ( )
945
+ // A multiline start mark (`/`) needs be treated as an
946
+ // annotation or the line could get folded.
947
+ && inline_marks
948
+ . iter ( )
949
+ . all ( |m| m. mark_type != DisplayMarkType :: AnnotationStart )
950
+ {
932
951
unhighlighed_lines. push ( line) ;
933
952
} else {
934
953
if lines. is_empty ( ) {
@@ -1016,12 +1035,14 @@ fn format_body(
1016
1035
for ( idx, ( line, end_line) ) in CursorLines :: new ( snippet. source ) . enumerate ( ) {
1017
1036
let line_length: usize = line. len ( ) ;
1018
1037
let line_range = ( current_index, current_index + line_length) ;
1038
+ let end_line_size = end_line as usize ;
1019
1039
body. push ( DisplayLine :: Source {
1020
1040
lineno : Some ( current_line) ,
1021
1041
inline_marks : vec ! [ ] ,
1022
1042
line : DisplaySourceLine :: Content {
1023
1043
text : line,
1024
1044
range : line_range,
1045
+ end_line,
1025
1046
} ,
1026
1047
annotations : vec ! [ ] ,
1027
1048
} ) ;
@@ -1045,7 +1066,7 @@ fn format_body(
1045
1066
let line_start_index = line_range. 0 ;
1046
1067
let line_end_index = line_range. 1 ;
1047
1068
current_line += 1 ;
1048
- current_index += line_length + end_line as usize ;
1069
+ current_index += line_length + end_line_size ;
1049
1070
1050
1071
// It would be nice to use filter_drain here once it's stable.
1051
1072
annotations. retain ( |annotation| {
@@ -1057,18 +1078,24 @@ fn format_body(
1057
1078
} ;
1058
1079
let label_right = annotation. label . map_or ( 0 , |label| label. len ( ) + 1 ) ;
1059
1080
match annotation. range {
1060
- Range { start, .. } if start > line_end_index => true ,
1081
+ // This handles if the annotation is on the next line. We add
1082
+ // the `end_line_size` to account for annotating the line end.
1083
+ Range { start, .. } if start > line_end_index + end_line_size => true ,
1084
+ // This handles the case where an annotation is contained
1085
+ // within the current line including any line-end characters.
1061
1086
Range { start, end }
1062
- if start >= line_start_index && end <= line_end_index
1063
- // Allow annotating eof or stripped eol
1064
- || start == line_end_index && end - start <= 1 =>
1087
+ if start >= line_start_index
1088
+ // We add at least one to `line_end_index` to allow
1089
+ // highlighting the end of a file
1090
+ && end <= line_end_index + max ( end_line_size, 1 ) =>
1065
1091
{
1066
1092
if let DisplayLine :: Source {
1067
1093
ref mut annotations,
1068
1094
..
1069
1095
} = body[ body_idx]
1070
1096
{
1071
- let annotation_start_col = line[ 0 ..( start - line_start_index) ]
1097
+ let annotation_start_col = line
1098
+ [ 0 ..( start - line_start_index) . min ( line_length) ]
1072
1099
. chars ( )
1073
1100
. map ( |c| unicode_width:: UnicodeWidthChar :: width ( c) . unwrap_or ( 0 ) )
1074
1101
. sum :: < usize > ( ) ;
@@ -1101,11 +1128,16 @@ fn format_body(
1101
1128
}
1102
1129
false
1103
1130
}
1131
+ // This handles the case where a multiline annotation starts
1132
+ // somewhere on the current line, including any line-end chars
1104
1133
Range { start, end }
1105
1134
if start >= line_start_index
1106
- && start <= line_end_index
1135
+ // The annotation can start on a line ending
1136
+ && start <= line_end_index + end_line_size. saturating_sub ( 1 )
1107
1137
&& end > line_end_index =>
1108
1138
{
1139
+ // Special case for multiline annotations that start at the
1140
+ // beginning of a line, which requires a special mark (`/`)
1109
1141
if start - line_start_index == 0 {
1110
1142
if let DisplayLine :: Source {
1111
1143
ref mut inline_marks,
@@ -1122,7 +1154,8 @@ fn format_body(
1122
1154
..
1123
1155
} = body[ body_idx]
1124
1156
{
1125
- let annotation_start_col = line[ 0 ..( start - line_start_index) ]
1157
+ let annotation_start_col = line
1158
+ [ 0 ..( start - line_start_index) . min ( line_length) ]
1126
1159
. chars ( )
1127
1160
. map ( |c| unicode_width:: UnicodeWidthChar :: width ( c) . unwrap_or ( 0 ) )
1128
1161
. sum :: < usize > ( ) ;
@@ -1147,7 +1180,11 @@ fn format_body(
1147
1180
}
1148
1181
true
1149
1182
}
1150
- Range { start, end } if start < line_start_index && end > line_end_index => {
1183
+ // This handles the case where a multiline annotation starts
1184
+ // somewhere before this line and ends after it as well
1185
+ Range { start, end }
1186
+ if start < line_start_index && end > line_end_index + max ( end_line_size, 1 ) =>
1187
+ {
1151
1188
if let DisplayLine :: Source {
1152
1189
ref mut inline_marks,
1153
1190
..
@@ -1160,10 +1197,14 @@ fn format_body(
1160
1197
}
1161
1198
true
1162
1199
}
1200
+ // This handles the case where a multiline annotation ends
1201
+ // somewhere on the current line, including any line-end chars
1163
1202
Range { start, end }
1164
1203
if start < line_start_index
1165
1204
&& end >= line_start_index
1166
- && end <= line_end_index =>
1205
+ // We add at least one to `line_end_index` to allow
1206
+ // highlighting the end of a file
1207
+ && end <= line_end_index + max ( end_line_size, 1 ) =>
1167
1208
{
1168
1209
if let DisplayLine :: Source {
1169
1210
ref mut inline_marks,
@@ -1175,13 +1216,21 @@ fn format_body(
1175
1216
mark_type : DisplayMarkType :: AnnotationThrough ,
1176
1217
annotation_type : DisplayAnnotationType :: from ( annotation. level ) ,
1177
1218
} ) ;
1178
- let end_mark = line[ 0 ..( end - line_start_index) ]
1219
+ let end_mark = line[ 0 ..( end - line_start_index) . min ( line_length ) ]
1179
1220
. chars ( )
1180
1221
. map ( |c| unicode_width:: UnicodeWidthChar :: width ( c) . unwrap_or ( 0 ) )
1181
1222
. sum :: < usize > ( )
1182
1223
. saturating_sub ( 1 ) ;
1183
-
1184
- let end_plus_one = end_mark + 1 ;
1224
+ // If the annotation ends on a line-end character, we
1225
+ // need to annotate one past the end of the line
1226
+ let ( end_mark, end_plus_one) = if end > line_end_index
1227
+ // Special case for highlighting the end of a file
1228
+ || ( end == line_end_index + 1 && end_line_size == 0 )
1229
+ {
1230
+ ( end_mark + 1 , end_mark + 2 )
1231
+ } else {
1232
+ ( end_mark, end_mark + 1 )
1233
+ } ;
1185
1234
1186
1235
span_left_margin = min ( span_left_margin, end_mark) ;
1187
1236
span_right_margin = max ( span_right_margin, end_plus_one) ;
0 commit comments