@@ -47,7 +47,7 @@ pub struct BytePos(pub u32);
47
47
/// A character offset. Because of multibyte utf8 characters, a byte offset
48
48
/// is not equivalent to a character offset. The CodeMap will convert BytePos
49
49
/// values to CharPos values as necessary.
50
- #[ derive( Copy , Clone , PartialEq , Hash , PartialOrd , Debug ) ]
50
+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , PartialOrd , Debug ) ]
51
51
pub struct CharPos ( pub usize ) ;
52
52
53
53
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
@@ -299,9 +299,21 @@ impl ExpnId {
299
299
300
300
pub type FileName = String ;
301
301
302
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
303
+ pub struct LineInfo {
304
+ /// Index of line, starting from 0.
305
+ pub line_index : usize ,
306
+
307
+ /// Column in line where span begins, starting from 0.
308
+ pub start_col : CharPos ,
309
+
310
+ /// Column in line where span ends, starting from 0, exclusive.
311
+ pub end_col : CharPos ,
312
+ }
313
+
302
314
pub struct FileLines {
303
315
pub file : Rc < FileMap > ,
304
- pub lines : Vec < usize >
316
+ pub lines : Vec < LineInfo >
305
317
}
306
318
307
319
/// Identifies an offset of a multi-byte character in a FileMap
@@ -467,9 +479,9 @@ impl FileMap {
467
479
lines. push ( pos) ;
468
480
}
469
481
470
- /// get a line from the list of pre-computed line-beginnings
471
- ///
472
- pub fn get_line ( & self , line_number : usize ) -> Option < String > {
482
+ /// get a line from the list of pre-computed line-beginnings.
483
+ /// line-number here is 0-based.
484
+ pub fn get_line ( & self , line_number : usize ) -> Option < & str > {
473
485
match self . src {
474
486
Some ( ref src) => {
475
487
let lines = self . lines . borrow ( ) ;
@@ -480,7 +492,7 @@ impl FileMap {
480
492
match slice. find ( '\n' ) {
481
493
Some ( e) => & slice[ ..e] ,
482
494
None => slice
483
- } . to_string ( )
495
+ }
484
496
} )
485
497
}
486
498
None => None
@@ -649,10 +661,29 @@ impl CodeMap {
649
661
pub fn span_to_lines ( & self , sp : Span ) -> FileLines {
650
662
let lo = self . lookup_char_pos ( sp. lo ) ;
651
663
let hi = self . lookup_char_pos ( sp. hi ) ;
652
- let mut lines = Vec :: new ( ) ;
653
- for i in lo. line - 1 ..hi. line {
654
- lines. push ( i) ;
655
- } ;
664
+ let mut lines = Vec :: with_capacity ( hi. line - lo. line + 1 ) ;
665
+
666
+ // The span starts partway through the first line,
667
+ // but after that it starts from offset 0.
668
+ let mut start_col = lo. col ;
669
+
670
+ // For every line but the last, it extends from `start_col`
671
+ // and to the end of the line. Be careful because the line
672
+ // numbers in Loc are 1-based, so we subtract 1 to get 0-based
673
+ // lines.
674
+ for line_index in lo. line -1 .. hi. line -1 {
675
+ let line_len = lo. file . get_line ( line_index) . map ( |s| s. len ( ) ) . unwrap_or ( 0 ) ;
676
+ lines. push ( LineInfo { line_index : line_index,
677
+ start_col : start_col,
678
+ end_col : CharPos :: from_usize ( line_len) } ) ;
679
+ start_col = CharPos :: from_usize ( 0 ) ;
680
+ }
681
+
682
+ // For the last line, it extends from `start_col` to `hi.col`:
683
+ lines. push ( LineInfo { line_index : hi. line - 1 ,
684
+ start_col : start_col,
685
+ end_col : hi. col } ) ;
686
+
656
687
FileLines { file : lo. file , lines : lines}
657
688
}
658
689
@@ -907,17 +938,18 @@ pub struct MalformedCodemapPositions {
907
938
#[ cfg( test) ]
908
939
mod test {
909
940
use super :: * ;
941
+ use std:: rc:: Rc ;
910
942
911
943
#[ test]
912
944
fn t1 ( ) {
913
945
let cm = CodeMap :: new ( ) ;
914
946
let fm = cm. new_filemap ( "blork.rs" . to_string ( ) ,
915
947
"first line.\n second line" . to_string ( ) ) ;
916
948
fm. next_line ( BytePos ( 0 ) ) ;
917
- assert_eq ! ( fm. get_line( 0 ) , Some ( "first line." . to_string ( ) ) ) ;
949
+ assert_eq ! ( fm. get_line( 0 ) , Some ( "first line." ) ) ;
918
950
// TESTING BROKEN BEHAVIOR:
919
951
fm. next_line ( BytePos ( 10 ) ) ;
920
- assert_eq ! ( fm. get_line( 1 ) , Some ( "." . to_string ( ) ) ) ;
952
+ assert_eq ! ( fm. get_line( 1 ) , Some ( "." ) ) ;
921
953
}
922
954
923
955
#[ test]
@@ -1045,7 +1077,54 @@ mod test {
1045
1077
1046
1078
assert_eq ! ( file_lines. file. name, "blork.rs" ) ;
1047
1079
assert_eq ! ( file_lines. lines. len( ) , 1 ) ;
1048
- assert_eq ! ( file_lines. lines[ 0 ] , 1 ) ;
1080
+ assert_eq ! ( file_lines. lines[ 0 ] . line_index, 1 ) ;
1081
+ }
1082
+
1083
+ /// Given a string like " ^~~~~~~~~~~~ ", produces a span
1084
+ /// coverting that range. The idea is that the string has the same
1085
+ /// length as the input, and we uncover the byte positions. Note
1086
+ /// that this can span lines and so on.
1087
+ fn span_from_selection ( input : & str , selection : & str ) -> Span {
1088
+ assert_eq ! ( input. len( ) , selection. len( ) ) ;
1089
+ let left_index = selection. find ( '^' ) . unwrap ( ) as u32 ;
1090
+ let right_index = selection. rfind ( '~' ) . unwrap ( ) as u32 ;
1091
+ Span { lo : BytePos ( left_index) , hi : BytePos ( right_index + 1 ) , expn_id : NO_EXPANSION }
1092
+ }
1093
+
1094
+ fn new_filemap_and_lines ( cm : & CodeMap , filename : & str , input : & str ) -> Rc < FileMap > {
1095
+ let fm = cm. new_filemap ( filename. to_string ( ) , input. to_string ( ) ) ;
1096
+ let mut byte_pos: u32 = 0 ;
1097
+ for line in input. lines ( ) {
1098
+ // register the start of this line
1099
+ fm. next_line ( BytePos ( byte_pos) ) ;
1100
+
1101
+ // update byte_pos to include this line and the \n at the end
1102
+ byte_pos += line. len ( ) as u32 + 1 ;
1103
+ }
1104
+ fm
1105
+ }
1106
+
1107
+ /// Test span_to_snippet and span_to_lines for a span coverting 3
1108
+ /// lines in the middle of a file.
1109
+ #[ test]
1110
+ fn span_to_snippet_and_lines_spanning_multiple_lines ( ) {
1111
+ let cm = CodeMap :: new ( ) ;
1112
+ let inputtext = "aaaaa\n bbbbBB\n CCC\n DDDDDddddd\n eee\n " ;
1113
+ let selection = " \n ^~\n ~~~\n ~~~~~ \n \n " ;
1114
+ new_filemap_and_lines ( & cm, "blork.rs" , inputtext) ;
1115
+ let span = span_from_selection ( inputtext, selection) ;
1116
+
1117
+ // check that we are extracting the text we thought we were extracting
1118
+ assert_eq ! ( & cm. span_to_snippet( span) . unwrap( ) , "BB\n CCC\n DDDDD" ) ;
1119
+
1120
+ // check that span_to_lines gives us the complete result with the lines/cols we expected
1121
+ let lines = cm. span_to_lines ( span) ;
1122
+ let expected = vec ! [
1123
+ LineInfo { line_index: 1 , start_col: CharPos ( 4 ) , end_col: CharPos ( 6 ) } ,
1124
+ LineInfo { line_index: 2 , start_col: CharPos ( 0 ) , end_col: CharPos ( 3 ) } ,
1125
+ LineInfo { line_index: 3 , start_col: CharPos ( 0 ) , end_col: CharPos ( 5 ) }
1126
+ ] ;
1127
+ assert_eq ! ( lines. lines, expected) ;
1049
1128
}
1050
1129
1051
1130
#[ test]
0 commit comments