Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion DOM/Sources/Parser.XML.Scanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ package extension XMLParser {
package mutating func scanStringIfPossible(_ token: String) -> Bool {
return (try? self.scanString(token)) == true
}


@discardableResult
package mutating func nextScanString(_ token: String) -> Bool {
scanner.currentIndex = currentIndex
defer { scanner.currentIndex = currentIndex }
return scanStringIfPossible(token)
}

package mutating func scanString(matchingAny tokens: Set<String>) throws -> String {
scanner.currentIndex = currentIndex
guard let match = tokens.first(where: { scanner.scanString($0) != nil }) else {
Expand Down
52 changes: 36 additions & 16 deletions DOM/Sources/Parser.XML.StyleSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ extension XMLParser {
var scanner = XMLParser.Scanner(text: removeCSSComments(from: text))
var entries = [DOM.StyleSheet.Selector: [String: String]]()

var last: (DOM.StyleSheet.Selector, [String: String])?
repeat {
last = try scanner.scanNextSelector()
if let last = last {
entries[last.0] = last.1
while let (selectors, attributes) = try scanner.scanNextSelectorDecl() {
for selector in selectors {
var copy = entries[selector] ?? [:]
for (key, value) in attributes {
copy[key] = value
}
entries[selector] = copy
}
} while last != nil
}

return entries
}
Expand All @@ -91,15 +93,10 @@ extension XMLParser {

extension XMLParser.Scanner {

mutating func scanNextSelector() throws -> (DOM.StyleSheet.Selector, [String: String])? {
if let c = try scanNextClass() {
return (.class(c), try scanAtttributes())
} else if let id = try scanNextID() {
return (.id(id), try scanAtttributes())
} else if let e = try scanNextElement() {
return (.element(e), try scanAtttributes())
}
return nil
mutating func scanNextSelectorDecl() throws -> ([DOM.StyleSheet.Selector], [String: String])? {
let selectorTypes = try scanSelectorTypes()
guard !selectorTypes.isEmpty else { return nil }
return (selectorTypes, try scanAtttributes())
}

private mutating func scanNextClass() throws -> String? {
Expand All @@ -124,7 +121,30 @@ extension XMLParser.Scanner {
}

private mutating func scanSelectorName() throws -> String? {
try scanString(upTo: "{").trimmingCharacters(in: .whitespacesAndNewlines)
guard !nextScanString("{") else { return nil }
let name = try scanString(upTo: .init(charactersIn: "{,")).trimmingCharacters(in: .whitespacesAndNewlines)
scanStringIfPossible(",")
return name
}

mutating func scanSelectorTypes() throws -> [DOM.StyleSheet.Selector] {
var selectors: [DOM.StyleSheet.Selector] = []
while let next = try scanNextSelectorType() {
selectors.append(next)
}
return selectors
}

private mutating func scanNextSelectorType() throws -> DOM.StyleSheet.Selector? {
if let name = try scanNextClass() {
return .class(name)
} else if let name = try scanNextID() {
return .id(name)
} else if let name = try scanNextElement() {
return .element(name)
} else {
return nil
}
}

private mutating func scanAtttributes() throws -> [String: String] {
Expand Down
39 changes: 39 additions & 0 deletions DOM/Tests/Parser.XML.StyleSheetTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,43 @@ final class ParserXMLStyleSheetTests: XCTestCase {
XCTAssertEqual(sheet[.class("b")]?.fill, .color(.keyword(.blue)))
XCTAssertEqual(sheet[.element("rect")]?.fill, .color(.keyword(.pink)))
}

func testMergesSelectors() throws {
let entries = try XMLParser.parseEntries(
"""
.a {
fill: red;
}
.a {
stroke: blue;
}
.a {
fill: purple;
}
"""
)

XCTAssertEqual(
entries,
[.class("a"): ["fill": "purple", "stroke": "blue"]]
)
}

func testMutlipleSelectors() throws {
let entries = try XMLParser.parseEntries(
"""
.a, .b {
fill: red;
}
"""
)

XCTAssertEqual(
entries,
[
.class("a"): ["fill": "red"],
.class("b"): ["fill": "red"]
]
)
}
}
3 changes: 2 additions & 1 deletion Examples/Sources/GalleryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ struct GalleryView: View {
"yawning.svg",
"thats-no-moon.svg",
"alert.svg",
"effigy.svg"
"effigy.svg",
"stylesheet-multiple.svg"
].compactMap {
SVG(named: $0, in: .samples)
}
Expand Down
1 change: 1 addition & 0 deletions Samples.bundle/stylesheet-multiple.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.