diff --git a/Sources/Splash/Grammar/SwiftGrammar.swift b/Sources/Splash/Grammar/SwiftGrammar.swift index 0a4a023..6451e0d 100644 --- a/Sources/Splash/Grammar/SwiftGrammar.swift +++ b/Sources/Splash/Grammar/SwiftGrammar.swift @@ -22,6 +22,7 @@ public struct SwiftGrammar: Grammar { syntaxRules = [ PreprocessingRule(), CommentRule(), + RawStringRule(), MultiLineStringRule(), SingleLineStringRule(), AttributeRule(), @@ -98,6 +99,14 @@ private extension SwiftGrammar { } } + struct RawStringRule: SyntaxRule { + var tokenType: TokenType { return .string } + + func matches(_ segment: Segment) -> Bool { + return segment.isWithinStringLiteral(withStart: "#\"", end: "\"#") + } + } + struct MultiLineStringRule: SyntaxRule { var tokenType: TokenType { return .string } @@ -114,7 +123,7 @@ private extension SwiftGrammar { var tokenType: TokenType { return .string } func matches(_ segment: Segment) -> Bool { - guard segment.isWithinStringLiteral else { + guard segment.isWithinStringLiteral(withStart: "\"", end: "\"") else { return false } @@ -377,14 +386,12 @@ private extension SwiftGrammar { } private extension Segment { - var isWithinStringLiteral: Bool { - let delimiter = "\"" - - if tokens.current.hasPrefix(delimiter) { + func isWithinStringLiteral(withStart start: String, end: String) -> Bool { + if tokens.current.hasPrefix(start) { return true } - if tokens.current.hasSuffix(delimiter) { + if tokens.current.hasSuffix(end) { return true } @@ -397,18 +404,20 @@ private extension Segment { continue } - if token == delimiter { - if markerCounts.start == markerCounts.end { + if token == start { + if start != end || markerCounts.start == markerCounts.end { markerCounts.start += 1 } else { markerCounts.end += 1 } + } else if token == end && start != end { + markerCounts.end += 1 } else { - if token.hasPrefix(delimiter) { + if token.hasPrefix(start) { markerCounts.start += 1 } - if token.hasSuffix(delimiter) { + if token.hasSuffix(end) { markerCounts.end += 1 } } diff --git a/Tests/SplashTests/Tests/LiteralTests.swift b/Tests/SplashTests/Tests/LiteralTests.swift index 1478fee..27d0216 100644 --- a/Tests/SplashTests/Tests/LiteralTests.swift +++ b/Tests/SplashTests/Tests/LiteralTests.swift @@ -100,6 +100,24 @@ final class LiteralTests: SyntaxHighlighterTestCase { ]) } + func testSingleLineRawStringLiteral() { + let components = highlighter.highlight(""" + #"A raw string \\(withoutInterpolation) yes"# + """) + + XCTAssertEqual(components, [ + .token("#\"A", .string), + .whitespace(" "), + .token("raw", .string), + .whitespace(" "), + .token("string", .string), + .whitespace(" "), + .token("\\(withoutInterpolation)", .string), + .whitespace(" "), + .token("yes\"#", .string) + ]) + } + func testDoubleLiteral() { let components = highlighter.highlight("let double = 1.13") @@ -142,6 +160,7 @@ extension LiteralTests { ("testStringLiteralWithAttribute", testStringLiteralWithAttribute), ("testStringLiteralInterpolation", testStringLiteralInterpolation), ("testMultiLineStringLiteral", testMultiLineStringLiteral), + ("testSingleLineRawStringLiteral", testSingleLineRawStringLiteral), ("testDoubleLiteral", testDoubleLiteral), ("testIntegerLiteralWithSeparators", testIntegerLiteralWithSeparators) ]