krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/OrgCore/Syntax/SyntaxNode.swift

b8d091bdcbf2889c220b12b2d0640d50cea17c24
orgstar/Sources/OrgCore/Syntax/SyntaxNode.swift history · blame · raw

118 lines · 4010 bytes

  1/// A view of a green node at an absolute offset, with a parent link. Created on demand.
  2public final class SyntaxNode: Sendable {
  3    public let green: GreenNode
  4    public let offset: Int
  5    public let parent: SyntaxNode?
  6
  7    init(green: GreenNode, offset: Int, parent: SyntaxNode?) {
  8        self.green = green
  9        self.offset = offset
 10        self.parent = parent
 11    }
 12
 13    public var kind: SyntaxKind { green.kind }
 14    public var range: Range<Int> { offset..<(offset + green.length) }
 15    public var text: String { green.text }
 16
 17    public var children: [SyntaxNode] {
 18        var result: [SyntaxNode] = []
 19        var at = offset
 20        for child in green.children {
 21            if case .node(let node) = child {
 22                result.append(SyntaxNode(green: node, offset: at, parent: self))
 23            }
 24            at += child.length
 25        }
 26        return result
 27    }
 28
 29    public var tokens: [SyntaxToken] {
 30        var result: [SyntaxToken] = []
 31        var at = offset
 32        for child in green.children {
 33            if case .token(let token) = child {
 34                result.append(SyntaxToken(kind: token.kind, text: token.text, range: at..<(at + token.length)))
 35            }
 36            at += child.length
 37        }
 38        return result
 39    }
 40
 41    /// Child nodes overlapping `range`. Only those are created, so a section with thousands of
 42    /// children costs little when the range is small.
 43    public func children(overlapping range: Range<Int>) -> [SyntaxNode] {
 44        var result: [SyntaxNode] = []
 45        var at = offset
 46        for child in green.children {
 47            let childRange = at..<(at + child.length)
 48            if childRange.lowerBound >= range.upperBound, !range.isEmpty { break }
 49            if case .node(let node) = child, childRange.overlaps(range) {
 50                result.append(SyntaxNode(green: node, offset: at, parent: self))
 51            }
 52            at = childRange.upperBound
 53        }
 54        return result
 55    }
 56
 57    /// The child node whose range contains `position`.
 58    public func child(containing position: Int) -> SyntaxNode? {
 59        var at = offset
 60        for child in green.children {
 61            let end = at + child.length
 62            if position < end {
 63                if case .node(let node) = child, position >= at { return SyntaxNode(green: node, offset: at, parent: self) }
 64                return nil
 65            }
 66            at = end
 67        }
 68        return nil
 69    }
 70
 71    /// The first child node of `kind`.
 72    public func firstChild(_ kind: SyntaxKind) -> SyntaxNode? {
 73        var at = offset
 74        for child in green.children {
 75            if case .node(let node) = child, node.kind == kind { return SyntaxNode(green: node, offset: at, parent: self) }
 76            at += child.length
 77        }
 78        return nil
 79    }
 80
 81    public enum Element {
 82        case node(SyntaxNode)
 83        case token(SyntaxToken)
 84    }
 85
 86    /// Child nodes and tokens in document order.
 87    public var elements: [Element] {
 88        var result: [Element] = []
 89        var at = offset
 90        for child in green.children {
 91            switch child {
 92            case .node(let node): result.append(.node(SyntaxNode(green: node, offset: at, parent: self)))
 93            case .token(let token): result.append(.token(SyntaxToken(kind: token.kind, text: token.text, range: at..<(at + child.length))))
 94            }
 95            at += child.length
 96        }
 97        return result
 98    }
 99
100    /// This node and every node below it, in document order.
101    public func descendants() -> [SyntaxNode] {
102        [self] + children.flatMap { $0.descendants() }
103    }
104}
105
106public struct SyntaxToken: Sendable, Equatable {
107    public let kind: SyntaxKind
108    public let text: String
109    public let range: Range<Int>
110}
111
112public struct OrgTree: Sendable {
113    public let green: GreenNode
114    public let settings: OrgSettings
115
116    public var root: SyntaxNode { SyntaxNode(green: green, offset: 0, parent: nil) }
117    public var text: String { green.text }
118}