Skip to content

Instantly share code, notes, and snippets.

@auramagi
Created November 29, 2021 03:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save auramagi/a40833494b626c3166ede63ffc39cbf3 to your computer and use it in GitHub Desktop.
Save auramagi/a40833494b626c3166ede63ffc39cbf3 to your computer and use it in GitHub Desktop.
Parse Swift type into a tree where the child nodes are the generic types. Useful for making trees from SwiftUI View types.
import Foundation
final class BracketScanner {
let text: String
init(text: String) {
self.text = text.trimmingCharacters(in: CharacterSet(charactersIn: "()"))
}
lazy var scanner: Scanner = {
let scanner = Scanner(string: text)
scanner.charactersToBeSkipped = nil
return scanner
}()
func parse() -> (String, [String]) {
let result = (parseId(), parseTypes())
return result
}
private func parseId() -> String {
scanner.scanUpToString("<") ?? ""
}
private func parseTypes() -> [String] {
_ = scanner.scanCharacters(from: CharacterSet(charactersIn: "<"))
var result: [String] = []
var pending = ""
var nest = 0
let controlSet = CharacterSet(charactersIn: "<,>")
while !scanner.isAtEnd {
let chunk = scanner.scanUpToCharacters(from: controlSet) ?? ""
pending += chunk
guard let control = scanner.scanCharacter() else {
result.append(pending)
break
}
switch control {
case "<":
nest += 1
pending.append(control)
case ",":
if nest == 0 {
result.append(pending)
pending = ""
} else {
pending.append(control)
}
case ">":
if nest == 0 {
result.append(pending)
pending = ""
} else {
pending.append(control)
nest -= 1
}
default: break
}
}
return result
}
}
import Foundation
struct Tree<A> {
var value: A
var children: [Tree<A>] = []
}
extension Mirror {
func makeTree() -> Tree<String> {
let typeString = String(describing: subjectType)
return typeString.makeTypeTree()
}
}
extension String {
func makeTypeTree() -> Tree<String> {
let (id, typeChildren) = BracketScanner(text: self).parse()
return .init(
value: id,
children: typeChildren.map { $0.makeTypeTree() }
)
}
}
extension View {
var typeTree: Tree<String> {
Mirror(reflecting: self).makeTree()
}
}
import SwiftUI
let tree = Text("Hello World")
.padding()
.background(Color.red)
.typeTree
dump(tree)
/*
▿ Trees.Tree<Swift.String>
- value: "ModifiedContent"
▿ children: 2 elements
▿ Trees.Tree<Swift.String>
- value: "ModifiedContent"
▿ children: 2 elements
▿ Trees.Tree<Swift.String>
- value: "Text"
- children: 0 elements
▿ Trees.Tree<Swift.String>
- value: " _PaddingLayout"
- children: 0 elements
▿ Trees.Tree<Swift.String>
- value: " _BackgroundModifier"
▿ children: 1 element
▿ Trees.Tree<Swift.String>
- value: "Color"
- children: 0 elements
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment