1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
extension RawSyntax {
/// A view into the ``RawSyntax`` that exposes functionality that's specific to tokens.
/// The token's payload must be a token, otherwise this traps.
@_spi(RawSyntax)
public var tokenView: RawSyntaxTokenView? {
switch raw.payload {
case .parsedToken, .materializedToken:
return RawSyntaxTokenView(raw: self)
case .layout(_):
return nil
}
}
}
/// A view into ``RawSyntax`` that exposes functionality that only applies to tokens.
@_spi(RawSyntax)
public struct RawSyntaxTokenView: Sendable {
let raw: RawSyntax
fileprivate init(raw: RawSyntax) {
self.raw = raw
switch raw.payload {
case .parsedToken, .materializedToken:
break
case .layout(_):
preconditionFailure("RawSyntax must be a token")
}
}
/// Token kind of this node.
@_spi(RawSyntax)
public var rawKind: RawTokenKind {
switch raw.rawData.payload {
case .materializedToken(let dat):
return dat.tokenKind
case .parsedToken(let dat):
return dat.tokenKind
case .layout(_):
preconditionFailure("'tokenKind' is not available for non-token node")
}
}
/// Token text of this node.
@_spi(RawSyntax)
public var rawText: SyntaxText {
switch raw.rawData.payload {
case .parsedToken(let dat):
return dat.tokenText
case .materializedToken(let dat):
return dat.tokenText
case .layout(_):
preconditionFailure("'rawText' is not available for non-token node")
}
}
/// The UTF-8 byte length of the leading trivia.
@_spi(RawSyntax)
public var leadingTriviaByteLength: Int {
switch raw.rawData.payload {
case .parsedToken(let dat):
return dat.leadingTriviaText.count
case .materializedToken(let dat):
return dat.leadingTrivia.reduce(0) { $0 + $1.byteLength }
case .layout(_):
preconditionFailure("'leadingTriviaByteLength' is not available for non-token node")
}
}
/// The UTF-8 byte length of the trailing trivia.
@_spi(RawSyntax)
public var trailingTriviaByteLength: Int {
switch raw.rawData.payload {
case .parsedToken(let dat):
return dat.trailingTriviaText.count
case .materializedToken(let dat):
return dat.trailingTrivia.reduce(0) { $0 + $1.byteLength }
case .layout(_):
preconditionFailure("'trailingTriviaByteLength' is not available for non-token node")
}
}
@_spi(RawSyntax)
public var leadingRawTriviaPieces: [RawTriviaPiece] {
switch raw.rawData.payload {
case .parsedToken(let dat):
return raw.arenaReference.parseTrivia(source: dat.leadingTriviaText, position: .leading)
case .materializedToken(let dat):
return Array(dat.leadingTrivia)
case .layout(_):
preconditionFailure("'leadingRawTriviaPieces' is called on non-token raw syntax")
}
}
@_spi(RawSyntax)
public var trailingRawTriviaPieces: [RawTriviaPiece] {
switch raw.rawData.payload {
case .parsedToken(let dat):
return raw.arenaReference.parseTrivia(source: dat.trailingTriviaText, position: .trailing)
case .materializedToken(let dat):
return Array(dat.trailingTrivia)
case .layout(_):
preconditionFailure("'trailingRawTriviaPieces' is called on non-token raw syntax")
}
}
/// Returns the leading ``Trivia`` length.
@_spi(RawSyntax)
public var leadingTriviaLength: SourceLength {
return SourceLength(utf8Length: leadingTriviaByteLength)
}
/// Returns the trailing ``Trivia`` length.
@_spi(RawSyntax)
public var trailingTriviaLength: SourceLength {
return SourceLength(utf8Length: trailingTriviaByteLength)
}
/// Run `body` with text of the leading trivia and return its result.
@_spi(RawSyntax)
public func leadingTrivia<T>(_ body: (SyntaxText) -> T) -> T {
switch raw.rawData.payload {
case .parsedToken(let dat):
return body(dat.leadingTriviaText)
case .materializedToken(let dat):
var leadingTriviaStr = Trivia(pieces: dat.leadingTrivia.map(TriviaPiece.init)).description
return leadingTriviaStr.withSyntaxText(body)
case .layout(_):
preconditionFailure("'leadingTrivia' is called on non-token raw syntax")
}
}
/// Run `body` with text of the leading trivia and return its result.
@_spi(RawSyntax)
public func trailingTrivia<T>(_ body: (SyntaxText) -> T) -> T {
switch raw.rawData.payload {
case .parsedToken(let dat):
return body(dat.trailingTriviaText)
case .materializedToken(let dat):
var trailingTriviaStr = Trivia(pieces: dat.trailingTrivia.map(TriviaPiece.init)).description
return trailingTriviaStr.withSyntaxText(body)
case .layout(_):
preconditionFailure("'trailingTrivia' is called on non-token raw syntax")
}
}
/// Returns the leading ``Trivia``.
@_spi(RawSyntax)
public func formLeadingTrivia() -> Trivia {
return Trivia(pieces: leadingRawTriviaPieces.map({ TriviaPiece(raw: $0) }))
}
/// Returns the trailing ``Trivia``.
/// - Returns: nil if called on a layout node.
@_spi(RawSyntax)
public func formTrailingTrivia() -> Trivia {
return Trivia(pieces: trailingRawTriviaPieces.map({ TriviaPiece(raw: $0) }))
}
/// Returns a ``RawSyntax`` node with the same source text but with the token
/// kind changed to `newValue`.
@_spi(RawSyntax)
public func withKind(_ newValue: TokenKind, arena: SyntaxArena) -> RawSyntax {
arena.addChild(self.raw.arenaReference)
switch raw.rawData.payload {
case .parsedToken(_):
// The wholeText can't be continuous anymore. Make a materialized token.
return .makeMaterializedToken(
kind: newValue,
leadingTrivia: formLeadingTrivia(),
trailingTrivia: formTrailingTrivia(),
presence: presence,
tokenDiagnostic: tokenDiagnostic,
arena: arena
)
case .materializedToken(var payload):
let decomposed = newValue.decomposeToRaw()
let rawKind = decomposed.rawKind
let text: SyntaxText = (decomposed.string.map({ arena.intern($0) }) ?? decomposed.rawKind.defaultText ?? "")
payload.tokenKind = rawKind
payload.tokenText = text
return RawSyntax(arena: arena, payload: .materializedToken(payload))
default:
preconditionFailure("'withKind()' is called on non-token raw syntax")
}
}
/// Returns a ``RawSyntax`` node with the presence changed to `newValue`.
@_spi(RawSyntax)
public func withPresence(_ newValue: SourcePresence, arena: SyntaxArena) -> RawSyntax {
arena.addChild(self.raw.arenaReference)
switch raw.rawData.payload {
case .parsedToken(var payload):
if arena == self.raw.arenaReference {
payload.presence = newValue
return RawSyntax(arena: arena, payload: .parsedToken(payload))
}
// If the modified token is allocated in a different arena, it might have
// a different or no `parseTrivia` function. We thus cannot use a
// `parsedToken` anymore.
return .makeMaterializedToken(
kind: formKind(),
leadingTrivia: formLeadingTrivia(),
trailingTrivia: formTrailingTrivia(),
presence: newValue,
tokenDiagnostic: tokenDiagnostic,
arena: arena
)
case .materializedToken(var payload):
payload.presence = newValue
return RawSyntax(arena: arena, payload: .materializedToken(payload))
default:
preconditionFailure("'withKind()' is called on non-token raw syntax")
}
}
/// The length of the token without leading or trailing trivia, assuming this
/// is a token node.
@_spi(RawSyntax)
public var textByteLength: Int {
switch raw.rawData.payload {
case .parsedToken(let dat):
return dat.tokenText.count
case .materializedToken(let dat):
return dat.tokenText.count
case .layout(_):
preconditionFailure("'textByteLength' is not available for non-token node")
}
}
@_spi(RawSyntax)
public var trimmedLength: SourceLength {
SourceLength(utf8Length: textByteLength)
}
@_spi(RawSyntax)
public func formKind() -> TokenKind {
switch raw.rawData.payload {
case .parsedToken(let dat):
return TokenKind.fromRaw(kind: dat.tokenKind, text: String(syntaxText: dat.tokenText))
case .materializedToken(let dat):
return TokenKind.fromRaw(kind: dat.tokenKind, text: String(syntaxText: dat.tokenText))
case .layout(_):
preconditionFailure("'formKind' is not available for non-token node")
}
}
@_spi(RawSyntax)
public var presence: SourcePresence {
switch raw.rawData.payload {
case .parsedToken(let dat):
return dat.presence
case .materializedToken(let dat):
return dat.presence
case .layout(_):
preconditionFailure("'presence' is not available for non-token node")
}
}
@_spi(RawSyntax)
public var tokenDiagnostic: TokenDiagnostic? {
switch raw.rawData.payload {
case .parsedToken(let dat):
return dat.tokenDiagnostic
case .materializedToken(let dat):
return dat.tokenDiagnostic
case .layout(_):
preconditionFailure("'tokenDiagnostic' is not available for non-token node")
}
}
@_spi(RawSyntax)
public func withTokenDiagnostic(tokenDiagnostic: TokenDiagnostic?, arena: SyntaxArena) -> RawTokenSyntax {
arena.addChild(self.raw.arenaReference)
switch raw.rawData.payload {
case .parsedToken(var dat):
if arena == self.raw.arenaReference {
dat.tokenDiagnostic = tokenDiagnostic
return RawSyntax(arena: arena, payload: .parsedToken(dat)).cast(RawTokenSyntax.self)
}
// If the modified token is allocated in a different arena, it might have
// a different or no `parseTrivia` function. We thus cannot use a
// `parsedToken` anymore.
return RawSyntax.makeMaterializedToken(
kind: formKind(),
leadingTrivia: formLeadingTrivia(),
trailingTrivia: formTrailingTrivia(),
presence: presence,
tokenDiagnostic: tokenDiagnostic,
arena: arena
).cast(RawTokenSyntax.self)
case .materializedToken(var dat):
dat.tokenDiagnostic = tokenDiagnostic
return RawSyntax(arena: arena, payload: .materializedToken(dat)).cast(RawTokenSyntax.self)
default:
preconditionFailure("'withTokenDiagnostic' is not available for non-token node")
}
}
}
|