使用 swift-syntax 解析 swift 代码为语法树,从而根据不同的 token 设置不同的高亮颜色。
使用 AttrubtedString 显示 Swift 代码h2
struct SwiftCodeView: View { @State private var codeString: AttributedString = ""
var body: some View { Text(codeString) .padding() .onAppear { codeString = AttributedString( """ let individualScores = [75, 43, 103, 87, 12] var teamScore = 0 for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 } } print(teamScore) """ ) } }}
使用 swift-syntax 库解析代码字符串h2
使用 swift-syntax 提供的 Parser 解析代码字符串,并获取其中的 tokens 属性:
let codeString = """ let individualScores = [75, 43, 103, 87, 12] var teamScore = 0 for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 } } print(teamScore) """let sourceFileSynatx = Parser.parse(source: codeString)let tokens = sourceFileSynatx.tokens(viewMode: .sourceAccurate)for token in tokens { print("\(token): \(token.tokenKind)")}let individualScores = [75, 43, 103, 87, 12]var teamScore = 0for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 }}print(teamScore)下面是 Parser 生成的 token 及其类型:
Optional("swift")let : keyword(SwiftSyntax.Keyword.let)individualScores : identifier("individualScores")= : equal50 collapsed lines
[: leftSquare75: integerLiteral("75"), : comma43: integerLiteral("43"), : comma103: integerLiteral("103"), : comma87: integerLiteral("87"), : comma12: integerLiteral("12")]: rightSquare
var : keyword(SwiftSyntax.Keyword.var)teamScore : identifier("teamScore")= : equal0: integerLiteral("0")
for : keyword(SwiftSyntax.Keyword.for)score : identifier("score")in : keyword(SwiftSyntax.Keyword.in)individualScores : identifier("individualScores"){: leftBrace
if : keyword(SwiftSyntax.Keyword.if)score : identifier("score")> : binaryOperator(">")50 : integerLiteral("50"){: leftBrace
teamScore : identifier("teamScore")+= : binaryOperator("+=")3: integerLiteral("3")
} : rightBraceelse : keyword(SwiftSyntax.Keyword.else){: leftBrace
teamScore : identifier("teamScore")+= : binaryOperator("+=")1: integerLiteral("1")
}: rightBrace
}: rightBrace
print: identifier("print")(: leftParenteamScore: identifier("teamScore")): rightParen: endOfFile得到了这些信息后,我们就可以根据 token 的类型设置不同的样式:
for token in tokens { let color: Color = switch token.tokenKind { case .keyword: Color( red: 252 / 255, green: 95 / 255, blue: 163 / 255 ) case .identifier("print"): Color( red: 103 / 255, green: 183 / 255, blue: 164 / 255 ) case .integerLiteral: Color( red: 208 / 255, green: 191 / 255, blue: 105 / 255 ) default: .primary } let font: Font = switch token.tokenKind { case .keyword: .system(.headline, design: .monospaced) default: .system(.body, design: .monospaced) }
let startOffset = code.utf8.index( code.startIndex, offsetBy: token.position.utf8Offset ) let endOffset = code.utf8.index( code.startIndex, offsetBy: token.endPosition.utf8Offset ) if let lowerBound = AttributedString.Index( startOffset, within: highlightedString ), let upperBound = AttributedString.Index( endOffset, within: highlightedString ) { highlightedString[lowerBound..<upperBound].foregroundColor = color highlightedString[lowerBound..<upperBound].font = font }}上述代码中的样式参考了 Xcode 默认主题的参数:
评论