krz/orgstar

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

Sources/OrgApp/OrgPreferences.swift

15f6b0709d88971fb62ed432c3e5b8032643670a
orgstar/Sources/OrgApp/OrgPreferences.swift history · blame · raw

86 lines · 5024 bytes

 1import Foundation
 2import OrgCore
 3import OrgPresentation
 4
 5/// The Org settings `config.toml` holds, read from user defaults with the file's fallbacks, for
 6/// both apps.
 7public enum OrgPreferences {
 8    public static let defaultTodoKeywords = "TODO(t) PROJ(p) LOOP(r) STRT(s) WAIT(w) HOLD(h) IDEA(i) | DONE(d) KILL(k)"
 9
10    /// TODO keywords for files without `#+TODO`, and alphabetical lists.
11    public static func orgSettings(_ defaults: UserDefaults = .standard) -> OrgSettings {
12        let line = defaults.string(forKey: "todoKeywords") ?? defaultTodoKeywords
13        var settings = OrgSettings.default.withTodoKeywords(line) ?? .default
14        settings.listAllowAlphabetical = bool("alphabeticalLists", defaults)
15        return settings
16    }
17
18    /// The editing options, as the Mac's Settings apply them.
19    public static func options(_ defaults: UserDefaults = .standard) -> EditingOptions {
20        var options = EditingOptions(
21            tagsColumn: integer("tagsColumn", defaults), insertHeadingRespectContent: bool("headingAfterSubtree", defaults),
22            metaReturnMaySplitLine: bool("splitLine", defaults),
23            listAllowAlphabetical: bool("alphabeticalLists", defaults), fillColumn: integer("fillColumn", defaults),
24            hideEmphasisMarkers: bool("hiddenMarkers", defaults), prettyEntities: bool("prettyEntities", defaults), subSuperscriptsNeedBraces: true,
25            logDone: LogHow(rawValue: string("logDone", defaults)), logIntoDrawer: string("logIntoDrawer", defaults).nonEmpty,
26            hideDrawerStartup: bool("hideDrawerStartup", defaults), hideBlockStartup: bool("hideBlockStartup", defaults),
27            logReschedule: LogHow(rawValue: string("logReschedule", defaults)), logRedeadline: LogHow(rawValue: string("logRedeadline", defaults)),
28            useSpeedCommands: bool("useSpeedCommands", defaults), startupWithInlineImages: bool("startupWithInlineImages", defaults),
29            startupIndented: bool("startupIndented", defaults), hideLeadingStars: bool("hideLeadingStars", defaults),
30            startupAlignAllTables: bool("startupAlignAllTables", defaults), startupTruncated: bool("startupTruncated", defaults),
31            spellCheck: bool("spellCheck", defaults)
32        )
33        options.electricPair = bool("electricPair", defaults)
34        return options
35    }
36
37    /// `colors` with the type settings of `[theme]`: font, size, line spacing, heading step.
38    public static func theme(_ colors: ThemeSpec, _ defaults: UserDefaults = .standard) -> ThemeSpec {
39        var spec = colors
40        spec.font = string("themeFont", defaults)
41        spec.fontSize = integer("themeFontSize", defaults)
42        spec.lineSpacing = integer("themeLineSpacing", defaults)
43        spec.headingSizeStep = integer("themeHeadingStep", defaults)
44        return spec
45    }
46
47    /// `show-markup`: link brackets and emphasis markers shown.
48    public static func showMarkup(_ defaults: UserDefaults = .standard) -> Bool { bool("showMarkup", defaults) }
49
50    public static func agendaSpan(_ defaults: UserDefaults = .standard) -> Int { integer("agendaSpan", defaults) }
51    public static func agendaStartOffset(_ defaults: UserDefaults = .standard) -> Int { integer("agendaStartOffset", defaults) }
52    public static func agendaSubfolders(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaSubfolders", defaults) }
53    public static func reminders(_ defaults: UserDefaults = .standard) -> Bool { bool("reminders", defaults) }
54    /// `appt-message-warning-time`, in minutes.
55    public static func reminderLead(_ defaults: UserDefaults = .standard) -> Int { integer("reminderLead", defaults) }
56    /// `org-clock-idle-time` in minutes; 0 is nil (never).
57    public static func clockIdleTime(_ defaults: UserDefaults = .standard) -> Int { integer("clockIdleTime", defaults) }
58    /// `org-clock-history-length`.
59    public static func clockHistoryLength(_ defaults: UserDefaults = .standard) -> Int { integer("clockHistoryLength", defaults) }
60
61    private static func fallback(_ key: String) -> TOML.Value? {
62        UserConfig.settings.first { $0.defaultsKey == key }?.fallback
63    }
64
65    private static func bool(_ key: String, _ defaults: UserDefaults) -> Bool {
66        defaults.object(forKey: key) as? Bool ?? fallback(key)?.bool ?? false
67    }
68
69    private static func string(_ key: String, _ defaults: UserDefaults) -> String {
70        defaults.string(forKey: key) ?? fallback(key)?.string ?? ""
71    }
72
73    /// Integers, and day offsets stored as integers with `"-3d"` as the fallback.
74    private static func integer(_ key: String, _ defaults: UserDefaults) -> Int {
75        if let value = defaults.object(forKey: key) as? Int { return value }
76        switch fallback(key) {
77        case .integer(let value)?: return value
78        case .string(let text)?: return Int(text.trimmingCharacters(in: CharacterSet(charactersIn: "+d"))) ?? 0
79        default: return 0
80        }
81    }
82}
83
84private extension String {
85    var nonEmpty: String? { isEmpty ? nil : self }
86}