@@ -49,7 +49,7 @@ pub struct BytePos(pub u32);
49
49
/// A character offset. Because of multibyte utf8 characters, a byte offset
50
50
/// is not equivalent to a character offset. The CodeMap will convert BytePos
51
51
/// values to CharPos values as necessary.
52
- #[ derive( Copy , Clone , PartialEq , Hash , PartialOrd , Debug ) ]
52
+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , PartialOrd , Debug ) ]
53
53
pub struct CharPos ( pub usize ) ;
54
54
55
55
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
@@ -305,9 +305,21 @@ impl ExpnId {
305
305
306
306
pub type FileName = String ;
307
307
308
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
309
+ pub struct LineInfo {
310
+ /// Index of line, starting from 0.
311
+ pub line_index : usize ,
312
+
313
+ /// Column in line where span begins, starting from 0.
314
+ pub start_col : CharPos ,
315
+
316
+ /// Column in line where span ends, starting from 0, exclusive.
317
+ pub end_col : CharPos ,
318
+ }
319
+
308
320
pub struct FileLines {
309
321
pub file : Rc < FileMap > ,
310
- pub lines : Vec < usize >
322
+ pub lines : Vec < LineInfo >
311
323
}
312
324
313
325
/// Identifies an offset of a multi-byte character in a FileMap
@@ -479,9 +491,9 @@ impl FileMap {
479
491
lines. push ( pos) ;
480
492
}
481
493
482
- /// get a line from the list of pre-computed line-beginnings
483
- ///
484
- pub fn get_line ( & self , line_number : usize ) -> Option < String > {
494
+ /// get a line from the list of pre-computed line-beginnings.
495
+ /// line-number here is 0-based.
496
+ pub fn get_line ( & self , line_number : usize ) -> Option < & str > {
485
497
match self . src {
486
498
Some ( ref src) => {
487
499
let lines = self . lines . borrow ( ) ;
@@ -492,7 +504,7 @@ impl FileMap {
492
504
match slice. find ( '\n' ) {
493
505
Some ( e) => & slice[ ..e] ,
494
506
None => slice
495
- } . to_string ( )
507
+ }
496
508
} )
497
509
}
498
510
None => None
@@ -661,10 +673,29 @@ impl CodeMap {
661
673
pub fn span_to_lines ( & self , sp : Span ) -> FileLines {
662
674
let lo = self . lookup_char_pos ( sp. lo ) ;
663
675
let hi = self . lookup_char_pos ( sp. hi ) ;
664
- let mut lines = Vec :: new ( ) ;
665
- for i in lo. line - 1 ..hi. line {
666
- lines. push ( i) ;
667
- } ;
676
+ let mut lines = Vec :: with_capacity ( hi. line - lo. line + 1 ) ;
677
+
678
+ // The span starts partway through the first line,
679
+ // but after that it starts from offset 0.
680
+ let mut start_col = lo. col ;
681
+
682
+ // For every line but the last, it extends from `start_col`
683
+ // and to the end of the line. Be careful because the line
684
+ // numbers in Loc are 1-based, so we subtract 1 to get 0-based
685
+ // lines.
686
+ for line_index in lo. line -1 .. hi. line -1 {
687
+ let line_len = lo. file . get_line ( line_index) . map ( |s| s. len ( ) ) . unwrap_or ( 0 ) ;
688
+ lines. push ( LineInfo { line_index : line_index,
689
+ start_col : start_col,
690
+ end_col : CharPos :: from_usize ( line_len) } ) ;
691
+ start_col = CharPos :: from_usize ( 0 ) ;
692
+ }
693
+
694
+ // For the last line, it extends from `start_col` to `hi.col`:
695
+ lines. push ( LineInfo { line_index : hi. line - 1 ,
696
+ start_col : start_col,
697
+ end_col : hi. col } ) ;
698
+
668
699
FileLines { file : lo. file , lines : lines}
669
700
}
670
701
@@ -919,17 +950,18 @@ pub struct MalformedCodemapPositions {
919
950
#[ cfg( test) ]
920
951
mod test {
921
952
use super :: * ;
953
+ use std:: rc:: Rc ;
922
954
923
955
#[ test]
924
956
fn t1 ( ) {
925
957
let cm = CodeMap :: new ( ) ;
926
958
let fm = cm. new_filemap ( "blork.rs" . to_string ( ) ,
927
959
"first line.\n second line" . to_string ( ) ) ;
928
960
fm. next_line ( BytePos ( 0 ) ) ;
929
- assert_eq ! ( fm. get_line( 0 ) , Some ( "first line." . to_string ( ) ) ) ;
961
+ assert_eq ! ( fm. get_line( 0 ) , Some ( "first line." ) ) ;
930
962
// TESTING BROKEN BEHAVIOR:
931
963
fm. next_line ( BytePos ( 10 ) ) ;
932
- assert_eq ! ( fm. get_line( 1 ) , Some ( "." . to_string ( ) ) ) ;
964
+ assert_eq ! ( fm. get_line( 1 ) , Some ( "." ) ) ;
933
965
}
934
966
935
967
#[ test]
@@ -1057,7 +1089,54 @@ mod test {
1057
1089
1058
1090
assert_eq ! ( file_lines. file. name, "blork.rs" ) ;
1059
1091
assert_eq ! ( file_lines. lines. len( ) , 1 ) ;
1060
- assert_eq ! ( file_lines. lines[ 0 ] , 1 ) ;
1092
+ assert_eq ! ( file_lines. lines[ 0 ] . line_index, 1 ) ;
1093
+ }
1094
+
1095
+ /// Given a string like " ^~~~~~~~~~~~ ", produces a span
1096
+ /// coverting that range. The idea is that the string has the same
1097
+ /// length as the input, and we uncover the byte positions. Note
1098
+ /// that this can span lines and so on.
1099
+ fn span_from_selection ( input : & str , selection : & str ) -> Span {
1100
+ assert_eq ! ( input. len( ) , selection. len( ) ) ;
1101
+ let left_index = selection. find ( '^' ) . unwrap ( ) as u32 ;
1102
+ let right_index = selection. rfind ( '~' ) . unwrap ( ) as u32 ;
1103
+ Span { lo : BytePos ( left_index) , hi : BytePos ( right_index + 1 ) , expn_id : NO_EXPANSION }
1104
+ }
1105
+
1106
+ fn new_filemap_and_lines ( cm : & CodeMap , filename : & str , input : & str ) -> Rc < FileMap > {
1107
+ let fm = cm. new_filemap ( filename. to_string ( ) , input. to_string ( ) ) ;
1108
+ let mut byte_pos: u32 = 0 ;
1109
+ for line in input. lines ( ) {
1110
+ // register the start of this line
1111
+ fm. next_line ( BytePos ( byte_pos) ) ;
1112
+
1113
+ // update byte_pos to include this line and the \n at the end
1114
+ byte_pos += line. len ( ) as u32 + 1 ;
1115
+ }
1116
+ fm
1117
+ }
1118
+
1119
+ /// Test span_to_snippet and span_to_lines for a span coverting 3
1120
+ /// lines in the middle of a file.
1121
+ #[ test]
1122
+ fn span_to_snippet_and_lines_spanning_multiple_lines ( ) {
1123
+ let cm = CodeMap :: new ( ) ;
1124
+ let inputtext = "aaaaa\n bbbbBB\n CCC\n DDDDDddddd\n eee\n " ;
1125
+ let selection = " \n ^~\n ~~~\n ~~~~~ \n \n " ;
1126
+ new_filemap_and_lines ( & cm, "blork.rs" , inputtext) ;
1127
+ let span = span_from_selection ( inputtext, selection) ;
1128
+
1129
+ // check that we are extracting the text we thought we were extracting
1130
+ assert_eq ! ( & cm. span_to_snippet( span) . unwrap( ) , "BB\n CCC\n DDDDD" ) ;
1131
+
1132
+ // check that span_to_lines gives us the complete result with the lines/cols we expected
1133
+ let lines = cm. span_to_lines ( span) ;
1134
+ let expected = vec ! [
1135
+ LineInfo { line_index: 1 , start_col: CharPos ( 4 ) , end_col: CharPos ( 6 ) } ,
1136
+ LineInfo { line_index: 2 , start_col: CharPos ( 0 ) , end_col: CharPos ( 3 ) } ,
1137
+ LineInfo { line_index: 3 , start_col: CharPos ( 0 ) , end_col: CharPos ( 5 ) }
1138
+ ] ;
1139
+ assert_eq ! ( lines. lines, expected) ;
1061
1140
}
1062
1141
1063
1142
#[ test]
0 commit comments