@@ -212,6 +212,13 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
212
212
/// // ^^^ first line
213
213
/// // | second line
214
214
///
215
+ /// Trailing whitespace is sometimes desired but usually stripped by the editor
216
+ /// if at the end of a line, or incorrectly sized if followed by another
217
+ /// annotation. In those cases the annotation can be explicitly ended with the
218
+ /// `$` character.
219
+ ///
220
+ /// // ^^^ trailing-ws-wanted $
221
+ ///
215
222
/// Annotations point to the last line that actually was long enough for the
216
223
/// range, not counting annotations themselves. So overlapping annotations are
217
224
/// possible:
@@ -229,9 +236,10 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
229
236
let mut prev_line_annotations: Vec < ( TextSize , usize ) > = Vec :: new ( ) ;
230
237
for line in text. split_inclusive ( '\n' ) {
231
238
let mut this_line_annotations = Vec :: new ( ) ;
232
- let line_length = if let Some ( idx) = line. find ( "//" ) {
233
- let annotation_offset = TextSize :: of ( & line[ ..idx + "//" . len ( ) ] ) ;
234
- for annotation in extract_line_annotations ( & line[ idx + "//" . len ( ) ..] ) {
239
+ let line_length = if let Some ( ( prefix, suffix) ) = line. split_once ( "//" ) {
240
+ let ss_len = TextSize :: of ( "//" ) ;
241
+ let annotation_offset = TextSize :: of ( prefix) + ss_len;
242
+ for annotation in extract_line_annotations ( suffix. trim_end_matches ( '\n' ) ) {
235
243
match annotation {
236
244
LineAnnotation :: Annotation { mut range, content, file } => {
237
245
range += annotation_offset;
@@ -257,7 +265,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
257
265
}
258
266
}
259
267
}
260
- idx . try_into ( ) . unwrap ( )
268
+ annotation_offset
261
269
} else {
262
270
TextSize :: of ( line)
263
271
} ;
@@ -294,16 +302,29 @@ fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
294
302
len = 1 ;
295
303
}
296
304
let range = TextRange :: at ( offset, len. try_into ( ) . unwrap ( ) ) ;
297
- let next = line[ len..] . find ( marker) . map_or ( line. len ( ) , |it| it + len) ;
298
- let mut content = & line[ len..] [ ..next - len] ;
305
+ let line_no_caret = & line[ len..] ;
306
+ let end_marker = line_no_caret. find ( |c| c == '$' ) ;
307
+ let next = line_no_caret. find ( marker) . map_or ( line. len ( ) , |it| it + len) ;
308
+
309
+ let mut content = match end_marker {
310
+ Some ( end_marker)
311
+ if end_marker < next
312
+ && line_no_caret[ end_marker..]
313
+ . strip_prefix ( |c : char | c. is_whitespace ( ) || c == '^' )
314
+ . is_some ( ) =>
315
+ {
316
+ & line_no_caret[ ..end_marker]
317
+ }
318
+ _ => line_no_caret[ ..next - len] . trim_end ( ) ,
319
+ } ;
299
320
300
321
let mut file = false ;
301
322
if !continuation && content. starts_with ( "file" ) {
302
323
file = true ;
303
324
content = & content[ "file" . len ( ) ..] ;
304
325
}
305
326
306
- let content = content. trim ( ) . to_string ( ) ;
327
+ let content = content. trim_start ( ) . to_string ( ) ;
307
328
308
329
let annotation = if continuation {
309
330
LineAnnotation :: Continuation { offset : range. end ( ) , content }
0 commit comments