@@ -49,7 +49,7 @@ pub struct BytePos(pub u32);
4949/// A character offset. Because of multibyte utf8 characters, a byte offset
5050/// is not equivalent to a character offset. The CodeMap will convert BytePos
5151/// values to CharPos values as necessary.
52- #[ derive( Copy , Clone , PartialEq , Hash , PartialOrd , Debug ) ]
52+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , PartialOrd , Debug ) ]
5353pub struct CharPos ( pub usize ) ;
5454
5555// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
@@ -305,9 +305,21 @@ impl ExpnId {
305305
306306pub type FileName = String ;
307307
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+
308320pub struct FileLines {
309321 pub file : Rc < FileMap > ,
310- pub lines : Vec < usize >
322+ pub lines : Vec < LineInfo >
311323}
312324
313325/// Identifies an offset of a multi-byte character in a FileMap
@@ -479,9 +491,9 @@ impl FileMap {
479491 lines. push ( pos) ;
480492 }
481493
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 > {
485497 match self . src {
486498 Some ( ref src) => {
487499 let lines = self . lines . borrow ( ) ;
@@ -492,7 +504,7 @@ impl FileMap {
492504 match slice. find ( '\n' ) {
493505 Some ( e) => & slice[ ..e] ,
494506 None => slice
495- } . to_string ( )
507+ }
496508 } )
497509 }
498510 None => None
@@ -661,10 +673,29 @@ impl CodeMap {
661673 pub fn span_to_lines ( & self , sp : Span ) -> FileLines {
662674 let lo = self . lookup_char_pos ( sp. lo ) ;
663675 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+
668699 FileLines { file : lo. file , lines : lines}
669700 }
670701
@@ -919,17 +950,18 @@ pub struct MalformedCodemapPositions {
919950#[ cfg( test) ]
920951mod test {
921952 use super :: * ;
953+ use std:: rc:: Rc ;
922954
923955 #[ test]
924956 fn t1 ( ) {
925957 let cm = CodeMap :: new ( ) ;
926958 let fm = cm. new_filemap ( "blork.rs" . to_string ( ) ,
927959 "first line.\n second line" . to_string ( ) ) ;
928960 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." ) ) ;
930962 // TESTING BROKEN BEHAVIOR:
931963 fm. next_line ( BytePos ( 10 ) ) ;
932- assert_eq ! ( fm. get_line( 1 ) , Some ( "." . to_string ( ) ) ) ;
964+ assert_eq ! ( fm. get_line( 1 ) , Some ( "." ) ) ;
933965 }
934966
935967 #[ test]
@@ -1057,7 +1089,54 @@ mod test {
10571089
10581090 assert_eq ! ( file_lines. file. name, "blork.rs" ) ;
10591091 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) ;
10611140 }
10621141
10631142 #[ test]
0 commit comments