diff --git a/src/parse/scanner.rs b/src/parse/scanner.rs index 1a0e3045b..fad44e89f 100644 --- a/src/parse/scanner.rs +++ b/src/parse/scanner.rs @@ -96,11 +96,6 @@ impl<'s> Scanner<'s> { self.peek().map(f).unwrap_or(false) } - /// Checks whether the remaining source starts with the given string. - pub fn starts_with(&self, string: &str) -> bool { - self.rest().starts_with(string) - } - /// The previous index in the source string. pub fn last_index(&self) -> usize { self.eaten() diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index c9b7dc21e..f21967402 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -72,9 +72,9 @@ impl<'s> Iterator for Tokens<'s> { // Whitespace. c if c.is_whitespace() => self.whitespace(c), - // Comments. - '/' if self.s.eat_if('/') => self.line_comment(), + // Comments with special case for URLs. '/' if self.s.eat_if('*') => self.block_comment(), + '/' if !self.maybe_in_url() && self.s.eat_if('/') => self.line_comment(), '*' if self.s.eat_if('/') => Token::Invalid(self.s.eaten_from(start)), // Other things. @@ -216,7 +216,7 @@ impl<'s> Tokens<'s> { self.s.eat_assert(c); Token::Text(&self.s.eaten_from(start)) } - 'u' if self.s.starts_with("u{") => { + 'u' if self.s.rest().starts_with("u{") => { self.s.eat_assert('u'); self.s.eat_assert('{'); Token::UnicodeEscape(UnicodeEscapeToken { @@ -367,7 +367,7 @@ impl<'s> Tokens<'s> { // Read the fractional part if not already done. // Make sure not to confuse a range for the decimal separator. - if c != '.' && !self.s.starts_with("..") && self.s.eat_if('.') { + if c != '.' && !self.s.rest().starts_with("..") && self.s.eat_if('.') { self.s.eat_while(|c| c.is_ascii_digit()); } @@ -464,6 +464,10 @@ impl<'s> Tokens<'s> { Token::BlockComment(self.s.get(start .. end)) } + + fn maybe_in_url(&self) -> bool { + self.mode == TokenMode::Markup && self.s.eaten().ends_with(":/") + } } impl Debug for Tokens<'_> { @@ -688,7 +692,6 @@ mod tests { t!(Markup[" /"]: r"\a" => Text(r"\"), Text("a")); t!(Markup[" /"]: r"\u" => Text(r"\"), Text("u")); t!(Markup[" /"]: r"\1" => Text(r"\"), Text("1")); - t!(Markup[" /"]: r"\:" => Text(r"\"), Text(":")); t!(Markup[" /"]: r#"\""# => Text(r"\"), Text("\"")); // Test basic unicode escapes. diff --git a/tests/ref/code/comment.png b/tests/ref/code/comment.png index 6654e1255..bb8bf69ab 100644 Binary files a/tests/ref/code/comment.png and b/tests/ref/code/comment.png differ diff --git a/tests/typ/code/comment.typ b/tests/typ/code/comment.typ index 25180211f..694600d6c 100644 --- a/tests/typ/code/comment.typ +++ b/tests/typ/code/comment.typ @@ -14,6 +14,11 @@ C/* #test(type(/*1*/ 1) // , "integer") +--- +// Line comments have a special case for URLs. +https://example.com \ +https:/* block comments don't ... */ + --- // End should not appear without start. // Error: 1:7-1:9 unexpected end of block comment diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json index aaad9ba8f..e1f416535 100644 --- a/tools/support/typst.tmLanguage.json +++ b/tools/support/typst.tmLanguage.json @@ -6,12 +6,6 @@ "repository": { "common": { "patterns": [ - { - "name": "comment.line.double-slash.typst", - "begin": "//", - "end": "\n", - "beginCaptures": { "0": { "name": "punctuation.definition.comment.typst" } } - }, { "name": "comment.block.typst", "begin": "/\\*", @@ -38,6 +32,12 @@ "markup": { "patterns": [ { "include": "#common" }, + { + "name": "comment.line.double-slash.typst", + "begin": "(?