@@ -157,12 +157,12 @@ pub enum Token<'a> {
157157 /// A `<bad-url-token>`
158158 ///
159159 /// This token always indicates a parse error.
160- BadUrl ,
160+ BadUrl ( CompactCowStr < ' a > ) ,
161161
162162 /// A `<bad-string-token>`
163163 ///
164164 /// This token always indicates a parse error.
165- BadString ,
165+ BadString ( CompactCowStr < ' a > ) ,
166166
167167 /// A `<)-token>`
168168 ///
@@ -194,7 +194,7 @@ impl<'a> Token<'a> {
194194 pub fn is_parse_error ( & self ) -> bool {
195195 matches ! (
196196 * self ,
197- BadUrl | BadString | CloseParenthesis | CloseSquareBracket | CloseCurlyBracket
197+ BadUrl ( _ ) | BadString ( _ ) | CloseParenthesis | CloseSquareBracket | CloseCurlyBracket
198198 )
199199 }
200200}
@@ -226,7 +226,7 @@ impl<'a> Tokenizer<'a> {
226226 input : input,
227227 position : 0 ,
228228 last_known_source_location : Cell :: new ( ( SourcePosition ( 0 ) ,
229- SourceLocation { line : 1 , column : 1 } ) ) ,
229+ SourceLocation { line : 0 , column : 0 } ) ) ,
230230 var_functions : SeenStatus :: DontCare ,
231231 viewport_percentages : SeenStatus :: DontCare ,
232232 }
@@ -287,6 +287,17 @@ impl<'a> Tokenizer<'a> {
287287 self . source_location ( position)
288288 }
289289
290+ pub fn current_source_line ( & self ) -> & ' a str {
291+ let current = self . position ;
292+ let start = self . input [ 0 ..current]
293+ . rfind ( |c| matches ! ( c, '\r' | '\n' | '\x0C' ) )
294+ . map_or ( 0 , |start| start + 1 ) ;
295+ let end = self . input [ current..]
296+ . find ( |c| matches ! ( c, '\r' | '\n' | '\x0C' ) )
297+ . map_or ( self . input . len ( ) , |end| current + end) ;
298+ & self . input [ start..end]
299+ }
300+
290301 pub fn source_location ( & self , position : SourcePosition ) -> SourceLocation {
291302 let target = position. 0 ;
292303 let mut location;
@@ -301,7 +312,7 @@ impl<'a> Tokenizer<'a> {
301312 // So if the requested position is before the last known one,
302313 // start over from the beginning.
303314 position = 0 ;
304- location = SourceLocation { line : 1 , column : 1 } ;
315+ location = SourceLocation { line : 0 , column : 0 } ;
305316 }
306317 let mut source = & self . input [ position..target] ;
307318 while let Some ( newline_position) = source. find ( |c| matches ! ( c, '\n' | '\r' | '\x0C' ) ) {
@@ -310,7 +321,7 @@ impl<'a> Tokenizer<'a> {
310321 source = & source[ offset..] ;
311322 position += offset;
312323 location. line += 1 ;
313- location. column = 1 ;
324+ location. column = 0 ;
314325 }
315326 debug_assert ! ( position <= target) ;
316327 location. column += ( target - position) as u32 ;
@@ -386,10 +397,10 @@ pub struct SourcePosition(usize);
386397/// The line and column number for a given position within the input.
387398#[ derive( PartialEq , Eq , Debug , Clone , Copy ) ]
388399pub struct SourceLocation {
389- /// The line number, starting at 1 for the first line.
400+ /// The line number, starting at 0 for the first line.
390401 pub line : u32 ,
391402
392- /// The column number within a line, starting at 1 for first the character of the line.
403+ /// The column number within a line, starting at 0 for first the character of the line.
393404 pub column : u32 ,
394405}
395406
@@ -556,14 +567,14 @@ fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result<Token<'a>, ()> {
556567fn consume_string < ' a > ( tokenizer : & mut Tokenizer < ' a > , single_quote : bool ) -> Token < ' a > {
557568 match consume_quoted_string ( tokenizer, single_quote) {
558569 Ok ( value) => QuotedString ( value) ,
559- Err ( ( ) ) => BadString
570+ Err ( value ) => BadString ( value )
560571 }
561572}
562573
563574
564575/// Return `Err(())` on syntax error (ie. unescaped newline)
565576fn consume_quoted_string < ' a > ( tokenizer : & mut Tokenizer < ' a > , single_quote : bool )
566- -> Result < CompactCowStr < ' a > , ( ) > {
577+ -> Result < CompactCowStr < ' a > , CompactCowStr < ' a > > {
567578 tokenizer. advance ( 1 ) ; // Skip the initial quote
568579 // start_pos is at code point boundary, after " or '
569580 let start_pos = tokenizer. position ( ) ;
@@ -596,15 +607,22 @@ fn consume_quoted_string<'a>(tokenizer: &mut Tokenizer<'a>, single_quote: bool)
596607 string_bytes = tokenizer. slice_from( start_pos) . as_bytes( ) . to_owned( ) ;
597608 break
598609 }
599- b'\n' | b'\r' | b'\x0C' => { return Err ( ( ) ) } ,
610+ b'\n' | b'\r' | b'\x0C' => {
611+ return Err ( tokenizer. slice_from( start_pos) . into( ) )
612+ } ,
600613 _ => { }
601614 }
602615 tokenizer. consume_byte ( ) ;
603616 }
604617
605618 while !tokenizer. is_eof ( ) {
606619 if matches ! ( tokenizer. next_byte_unchecked( ) , b'\n' | b'\r' | b'\x0C' ) {
607- return Err ( ( ) ) ;
620+ return Err (
621+ // string_bytes is well-formed UTF-8, see other comments.
622+ unsafe {
623+ from_utf8_release_unchecked ( string_bytes)
624+ } . into ( )
625+ ) ;
608626 }
609627 let b = tokenizer. consume_byte ( ) ;
610628 match_byte ! { b,
@@ -1013,6 +1031,7 @@ fn consume_unquoted_url<'a>(tokenizer: &mut Tokenizer<'a>) -> Result<Token<'a>,
10131031 }
10141032
10151033 fn consume_bad_url < ' a > ( tokenizer : & mut Tokenizer < ' a > ) -> Token < ' a > {
1034+ let start_pos = tokenizer. position ( ) ;
10161035 // Consume up to the closing )
10171036 while !tokenizer. is_eof ( ) {
10181037 match_byte ! { tokenizer. consume_byte( ) ,
@@ -1023,7 +1042,7 @@ fn consume_unquoted_url<'a>(tokenizer: &mut Tokenizer<'a>) -> Result<Token<'a>,
10231042 _ => { } ,
10241043 }
10251044 }
1026- BadUrl
1045+ BadUrl ( tokenizer . slice_from ( start_pos ) . into ( ) )
10271046 }
10281047}
10291048
0 commit comments