krz/orgstar

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

Sources/OrgCore/Capture/CaptureSchedule.swift

2fa201330f61c801f777baa3f2943d49d8758cda
orgstar/Sources/OrgCore/Capture/CaptureSchedule.swift history · blame · raw

33 lines · 2013 bytes

 1import Foundation
 2
 3extension Capture {
 4    /// A filled entry scheduled on `day` (as `Days` numbers it), for capturing from an agenda
 5    /// day: `SCHEDULED: <2026-10-09 Fri>` goes on the planning line after the heading. Text that
 6    /// is not an entry, or already has an active timestamp (`%t`, `%^t`), is left as it is.
 7    /// `cursor` is a UTF-16 offset and moves with the text after it.
 8    public static func scheduling(_ text: String, cursor: Int, on day: Int) -> (text: String, cursor: Int) {
 9        let ns = text as NSString
10        guard text.range(of: "^\\*+ ", options: .regularExpression) != nil,
11              text.range(of: "<[0-9]{4}-[0-9]{2}-[0-9]{2}", options: .regularExpression) == nil else { return (text, cursor) }
12        let d = Days.date(day)
13        let name = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][Days.weekday(day)]
14        let stamp = String(format: "SCHEDULED: <%04d-%02d-%02d %@>", d.year, d.month, d.day, name)
15        let headingEnd = ns.range(of: "\n").location
16        let insertion: (at: Int, text: String)
17        if headingEnd == NSNotFound {
18            insertion = (ns.length, "\n" + stamp)
19        } else {
20            let rest = ns.substring(from: headingEnd + 1)
21            let lineEnd = (rest as NSString).range(of: "\n").location
22            let next = lineEnd == NSNotFound ? rest : (rest as NSString).substring(to: lineEnd)
23            if next.range(of: "^\\s*(CLOSED|DEADLINE|SCHEDULED):", options: .regularExpression) != nil {
24                insertion = (headingEnd + 1 + (next as NSString).length, " " + stamp)
25            } else {
26                insertion = (headingEnd + 1, stamp + "\n")
27            }
28        }
29        let result = ns.replacingCharacters(in: NSRange(location: insertion.at, length: 0), with: insertion.text)
30        let moved = cursor > insertion.at || (cursor == insertion.at && headingEnd != NSNotFound && cursor > headingEnd)
31        return (result, moved ? cursor + (insertion.text as NSString).length : cursor)
32    }
33}