1// SPDX-FileCopyrightText: 2026 The Project Pterodactyl Developers
2//
3// SPDX-License-Identifier: MPL-2.0
4
5import Foundation
6
7public enum ArraySymmetry {
8 case identity
9 case reverse
10}
11
12extension Array {
13 fileprivate func apply(symmetry: ArraySymmetry) -> any Collection<Element> {
14 switch symmetry {
15 case .identity: self
16 case .reverse: reversed()
17 }
18 }
19}
20
21public protocol NodeSearching {
22 func firstToken(under symmetry: ArraySymmetry, satisfying predicate: (Token) -> Bool) -> SyntaxTokenCursor?
23}
24
25extension NodeSearching {
26 public func utf16RangeClamped(to predicate: (Token) -> Bool) -> Range<Int>? {
27 guard
28 let first = firstToken(under: .identity, satisfying: predicate),
29 let last = firstToken(under: .reverse, satisfying: predicate)
30 else { return nil }
31 return first.utf16Range.lowerBound..<last.utf16Range.upperBound
32 }
33
34 public var visibleUtf16Range: Range<Int>? {
35 utf16RangeClamped(to: \.kind.isVisible)
36 }
37
38 public var nontrivialUtf16Range: Range<Int>? {
39 utf16RangeClamped { token in
40 !token.kind.isTrivia
41 }
42 }
43}
44
45extension SyntaxTokenCursor: NodeSearching {
46 public func firstToken(under symmetry: ArraySymmetry, satisfying predicate: (Token) -> Bool) -> SyntaxTokenCursor? {
47 if predicate(token) { self } else { nil }
48 }
49}
50
51extension SyntaxTreeCursor.Child: NodeSearching {
52 public func firstToken(under symmetry: ArraySymmetry, satisfying predicate: (Token) -> Bool) -> SyntaxTokenCursor? {
53 switch self {
54 case .token(let syntaxTokenCursor): syntaxTokenCursor.firstToken(under: symmetry, satisfying: predicate)
55 case .tree(let syntaxTreeCursor): syntaxTreeCursor.firstToken(under: symmetry, satisfying: predicate)
56 }
57 }
58}
59
60extension SyntaxTreeCursor: NodeSearching {
61 public func firstToken(under symmetry: ArraySymmetry, satisfying predicate: (Token) -> Bool) -> SyntaxTokenCursor? {
62 for child in children.apply(symmetry: symmetry) {
63 if let first = child.firstToken(under: symmetry, satisfying: predicate) { return first }
64 }
65
66 return nil
67 }
68}