diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 12:08:21 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 12:22:47 +0200 |
| commit | 501fdce29e4d2c46c4708ad7f44056878e0db3fa (patch) | |
| tree | db923b77037db9f5b37600a6e34c3bb2e8a971be /Sources/NorgKit/Parsers/TaskScanner.swift | |
Initial extraction from Norganize1.0.0
Diffstat (limited to 'Sources/NorgKit/Parsers/TaskScanner.swift')
| -rw-r--r-- | Sources/NorgKit/Parsers/TaskScanner.swift | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/Sources/NorgKit/Parsers/TaskScanner.swift b/Sources/NorgKit/Parsers/TaskScanner.swift new file mode 100644 index 0000000..39c16b5 --- /dev/null +++ b/Sources/NorgKit/Parsers/TaskScanner.swift @@ -0,0 +1,52 @@ +import Foundation + +/// Scans Norg source for tasks and rewrites their status markers. +/// Handy to extract TODOs quicker than the parser can. +public enum TaskScanner { + + private static let markers = ASCIIByteSet("*-~>$^:") + + /// Extracts every task in `content`, attributing each to `fileURL`. + public static func scan(content: String, fileURL: URL) -> [NorgTask] { + var tasks: [NorgTask] = [] + TextHelper.enumerateLines(in: content) { line, index in + if let task = task(in: line, fileURL: fileURL, line: index) { + tasks.append(task) + } + } + return tasks + } + + /// Parses a single line into a task, if it carries a status extension. + public static func task(in raw: String, fileURL: URL, line: Int) -> NorgTask? { + task(in: raw[...], fileURL: fileURL, line: line) + } + + /// Span-free fast path over a line slice. + static func task(in line: Substring, fileURL: URL, line index: Int) -> NorgTask? { + guard let m = DetachedModifier.parse(line, accepting: markers), let status = m.status else { + return nil + } + return NorgTask( + fileURL: fileURL, + line: index, + status: status, + text: NorgInlineParser.plainText(m.content) + ) + } + + /// Returns `content` with the status marker on `line` replaced by `status`, + /// or `nil` if the line carries no recognisable task marker. Line endings are + /// preserved, so a CRLF file round-trips unchanged. + public static func updatedContent(_ content: String, line index: Int, to status: TaskStatus) + -> String? { + guard let line = TextHelper.line(in: content, at: index), + let m = DetachedModifier.parse(line, accepting: markers), + let statusIndex = m.statusIndex + else { return nil } + + var updated = content + updated.replaceSubrange(statusIndex...statusIndex, with: status.rawValue) + return updated + } +} |