// // TableOfContents.swift // GeminiFormat // // Created by Shadowfacts on 12/19/20. // import Foundation public struct TableOfContents { public let entries: [Entry] public init(document: Document) { self.entries = TableOfContents.entries(lines: document.lines) } private static func entries(lines: [Document.Line]) -> [Entry] { var topLevelEntries = [Entry]() var currentEntries = [Entry]() var index = 0 while index < lines.count { defer { index += 1 } let line = lines[index] guard case let .heading(_, level: level) = line else { continue } let newEntry = Entry(line: line, lineIndex: index) while !currentEntries.isEmpty && level <= currentEntries.last!.level { currentEntries.removeLast() } if let last = currentEntries.last { last.children.append(newEntry) currentEntries.append(newEntry) } else { topLevelEntries.append(newEntry) currentEntries.append(newEntry) } } return topLevelEntries } } public extension TableOfContents { class Entry: Equatable { public let line: Document.Line let level: Document.HeadingLevel public let lineIndex: Int public fileprivate(set) var children: [Entry] init(line: Document.Line, lineIndex: Int) { guard case let .heading(_, level: level) = line else { fatalError() } self.line = line self.level = level self.lineIndex = lineIndex self.children = [] } public static func ==(lhs: Entry, rhs: Entry) -> Bool { return lhs.line == rhs.line && lhs.lineIndex == rhs.lineIndex && lhs.children == rhs.children } } }