krz/orgstar

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

Sources/OrgApp/OrgPreferences.swift

b98a6509c5ae75e5172dd333d1b6105fd6ceee0e
orgstar/Sources/OrgApp/OrgPreferences.swift history · blame · raw

112 lines · 7359 bytes

24 symbols in this file
  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        options.enforceTodoDependencies = bool("enforceTodoDependencies", defaults)
 35        options.enforceTodoCheckboxDependencies = bool("enforceTodoCheckboxDependencies", defaults)
 36        return options
 37    }
 38
 39    /// `colors` with the type settings of `[theme]`: font, size, line spacing, heading step.
 40    public static func theme(_ colors: ThemeSpec, _ defaults: UserDefaults = .standard) -> ThemeSpec {
 41        var spec = colors
 42        spec.font = string("themeFont", defaults)
 43        spec.fontSize = integer("themeFontSize", defaults)
 44        spec.lineSpacing = integer("themeLineSpacing", defaults)
 45        spec.headingSizeStep = integer("themeHeadingStep", defaults)
 46        return spec
 47    }
 48
 49    /// `show-markup`: link brackets and emphasis markers shown.
 50    public static func showMarkup(_ defaults: UserDefaults = .standard) -> Bool { bool("showMarkup", defaults) }
 51
 52    public static func agendaSpan(_ defaults: UserDefaults = .standard) -> Int { integer("agendaSpan", defaults) }
 53    public static func agendaStartOffset(_ defaults: UserDefaults = .standard) -> Int { integer("agendaStartOffset", defaults) }
 54    public static func agendaSubfolders(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaSubfolders", defaults) }
 55    /// `org-agenda-show-all-dates`: days without entries show.
 56    public static func agendaShowAllDates(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaShowAllDates", defaults) }
 57    public static func agendaSkipScheduledIfDone(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaSkipScheduledIfDone", defaults) }
 58    public static func agendaSkipDeadlineIfDone(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaSkipDeadlineIfDone", defaults) }
 59    public static func agendaSearchAlwaysBoolean(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaSearchAlwaysBoolean", defaults) }
 60    public static func agendaSearchForceFullWords(_ defaults: UserDefaults = .standard) -> Bool { bool("agendaSearchForceFullWords", defaults) }
 61    /// `options` with the skip-if-done, search and blocked-entry settings.
 62    public static func agendaOptions(_ options: AgendaOptions, _ defaults: UserDefaults = .standard) -> AgendaOptions {
 63        var options = options
 64        options.skipScheduledIfDone = agendaSkipScheduledIfDone(defaults)
 65        options.skipDeadlineIfDone = agendaSkipDeadlineIfDone(defaults)
 66        options.searchAlwaysBoolean = agendaSearchAlwaysBoolean(defaults)
 67        options.searchForceFullWords = agendaSearchForceFullWords(defaults)
 68        options.enforceTodoDependencies = bool("enforceTodoDependencies", defaults)
 69        options.enforceTodoCheckboxDependencies = bool("enforceTodoCheckboxDependencies", defaults)
 70        options.dimBlockedTasks = AgendaOptions.DimBlocked(rawValue: string("agendaDimBlockedTasks", defaults)) ?? .dim
 71        return options
 72    }
 73    /// `calendar-events`: Calendar's events show in the agenda.
 74    public static func calendarEvents(_ defaults: UserDefaults = .standard) -> Bool { bool("calendarEvents", defaults) }
 75    /// `calendar-event-calendars`: the calendars chosen, by title or identifier; empty for all.
 76    public static func calendarEventCalendars(_ defaults: UserDefaults = .standard) -> [String] {
 77        string("calendarEventCalendars", defaults).split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) }.filter { !$0.isEmpty }
 78    }
 79    public static func reminders(_ defaults: UserDefaults = .standard) -> Bool { bool("reminders", defaults) }
 80    /// `appt-message-warning-time`, in minutes.
 81    public static func reminderLead(_ defaults: UserDefaults = .standard) -> Int { integer("reminderLead", defaults) }
 82    /// `org-clock-idle-time` in minutes; 0 is nil (never).
 83    public static func clockIdleTime(_ defaults: UserDefaults = .standard) -> Int { integer("clockIdleTime", defaults) }
 84    /// `org-clock-history-length`.
 85    public static func clockHistoryLength(_ defaults: UserDefaults = .standard) -> Int { integer("clockHistoryLength", defaults) }
 86
 87    private static func fallback(_ key: String) -> TOML.Value? {
 88        UserConfig.settings.first { $0.defaultsKey == key }?.fallback
 89    }
 90
 91    private static func bool(_ key: String, _ defaults: UserDefaults) -> Bool {
 92        defaults.object(forKey: key) as? Bool ?? fallback(key)?.bool ?? false
 93    }
 94
 95    private static func string(_ key: String, _ defaults: UserDefaults) -> String {
 96        defaults.string(forKey: key) ?? fallback(key)?.string ?? ""
 97    }
 98
 99    /// Integers, and day offsets stored as integers with `"-3d"` as the fallback.
100    private static func integer(_ key: String, _ defaults: UserDefaults) -> Int {
101        if let value = defaults.object(forKey: key) as? Int { return value }
102        switch fallback(key) {
103        case .integer(let value)?: return value
104        case .string(let text)?: return Int(text.trimmingCharacters(in: CharacterSet(charactersIn: "+d"))) ?? 0
105        default: return 0
106        }
107    }
108}
109
110private extension String {
111    var nonEmpty: String? { isEmpty ? nil : self }
112}