| @@ -234,7 +234,7 @@ public struct EmacsImport: Sendable { |
| 234 | return modes.isEmpty ? [nil] : modes |
234 | return modes.isEmpty ? [nil] : modes |
| 235 | } |
235 | } |
| 236 | |
236 | |
| 237 | /// `(kbd "C-c x")`, `"C-c x"` (keymap-set), or a vector of one key. |
237 | /// `(kbd "C-c x")` or `"C-c x"` (keymap-set); key vectors such as `[f5]` are skipped. |
| 238 | func keyString(_ sexp: Sexp) -> String? { |
238 | func keyString(_ sexp: Sexp) -> String? { |
| 239 | if let s = sexp.string { return s } |
239 | if let s = sexp.string { return s } |
| 240 | if sexp.head == "kbd", let s = sexp.elements?.dropFirst().first?.string { return s } |
240 | if sexp.head == "kbd", let s = sexp.elements?.dropFirst().first?.string { return s } |
| @@ -618,7 +618,7 @@ public struct EmacsImport: Sendable { |
| 618 | |
618 | |
| 619 | /// Emacs commands and the Orgstar commands that do the same. |
619 | /// Emacs commands and the Orgstar commands that do the same. |
| 620 | static let commands: [String: String] = [ |
620 | static let commands: [String: String] = [ |
| 621 | "org-todo": "org.todo.cycle", "org-shiftright": "org.todo.cycle", "org-priority-up": "org.priority.up", "org-priority-down": "org.priority.down", |
621 | "org-todo": "org.todo.cycle", "org-shiftright": "org.todo.next", "org-shiftleft": "org.todo.previous", "org-priority-up": "org.priority.up", "org-priority-down": "org.priority.down", |
| 622 | "org-metaleft": "org.heading.promote", "org-metaright": "org.heading.demote", "org-do-promote": "org.heading.promote", "org-do-demote": "org.heading.demote", |
622 | "org-metaleft": "org.heading.promote", "org-metaright": "org.heading.demote", "org-do-promote": "org.heading.promote", "org-do-demote": "org.heading.demote", |
| 623 | "org-promote-subtree": "org.subtree.promote", "org-demote-subtree": "org.subtree.demote", |
623 | "org-promote-subtree": "org.subtree.promote", "org-demote-subtree": "org.subtree.demote", |
| 624 | "org-shiftmetaleft": "org.subtree.promote", "org-shiftmetaright": "org.subtree.demote", |
624 | "org-shiftmetaleft": "org.subtree.promote", "org-shiftmetaright": "org.subtree.demote", |
| @@ -629,8 +629,8 @@ public struct EmacsImport: Sendable { |
| 629 | "org-next-visible-heading": "org.heading.next", "org-previous-visible-heading": "org.heading.previous", |
629 | "org-next-visible-heading": "org.heading.next", "org-previous-visible-heading": "org.heading.previous", |
| 630 | "org-forward-heading-same-level": "org.heading.forward-same-level", "org-backward-heading-same-level": "org.heading.backward-same-level", |
630 | "org-forward-heading-same-level": "org.heading.forward-same-level", "org-backward-heading-same-level": "org.heading.backward-same-level", |
| 631 | "outline-up-heading": "org.heading.up", "org-up-element": "org.heading.up", |
631 | "outline-up-heading": "org.heading.up", "org-up-element": "org.heading.up", |
| 632 | "org-set-tags-command": "org.tags.set", "org-set-property": "org.property.set", |
632 | "org-set-tags-command": "org.tags.set", "org-set-property": "org.property.read-and-set", |
| 633 | "org-toggle-checkbox": "org.checkbox.toggle", "org-ctrl-c-ctrl-c": "org.dwim", "+org/dwim-at-point": "org.dwim", |
633 | "org-toggle-checkbox": "org.checkbox.toggle", "org-ctrl-c-ctrl-c": "org.ctrl-c-ctrl-c", "+org/dwim-at-point": "org.dwim", |
| 634 | "org-schedule": "org.schedule", "org-deadline": "org.deadline", "org-time-stamp": "org.timestamp.active", "org-timestamp": "org.timestamp.active", |
634 | "org-schedule": "org.schedule", "org-deadline": "org.deadline", "org-time-stamp": "org.timestamp.active", "org-timestamp": "org.timestamp.active", |
| 635 | "org-time-stamp-inactive": "org.timestamp.inactive", "org-timestamp-inactive": "org.timestamp.inactive", |
635 | "org-time-stamp-inactive": "org.timestamp.inactive", "org-timestamp-inactive": "org.timestamp.inactive", |
| 636 | "org-fill-paragraph": "org.fill-paragraph", "org-cycle": "org.cycle", "org-shifttab": "org.cycle-global", "org-global-cycle": "org.cycle-global", |
636 | "org-fill-paragraph": "org.fill-paragraph", "org-cycle": "org.cycle", "org-shifttab": "org.cycle-global", "org-global-cycle": "org.cycle-global", |
| @@ -652,7 +652,8 @@ public struct EmacsImport: Sendable { |
| 652 | extension EmacsImport { |
652 | extension EmacsImport { |
| 653 | /// Applies `items`: settings to `defaults` (and so config.toml), folders to the workspace, |
653 | /// Applies `items`: settings to `defaults` (and so config.toml), folders to the workspace, |
| 654 | /// capture templates and key bindings appended to capture.toml and keymap.toml in |
654 | /// capture templates and key bindings appended to capture.toml and keymap.toml in |
| 655 | /// `directory`, leaving out templates whose key is there already. Returns a summary. |
655 | /// `directory`, leaving out templates whose key is there already and bindings that are |
| |
656 | /// there already. Returns a summary. |
| 656 | @MainActor |
657 | @MainActor |
| 657 | public static func apply( |
658 | public static func apply( |
| 658 | _ items: [Item], defaults: UserDefaults = .standard, workspace: WorkspaceModel?, directory: URL = UserConfig.directory |
659 | _ items: [Item], defaults: UserDefaults = .standard, workspace: WorkspaceModel?, directory: URL = UserConfig.directory |
| @@ -695,9 +696,13 @@ extension EmacsImport { |
| 695 | } |
696 | } |
| 696 | let keymapFile = directory.appendingPathComponent("keymap.toml") |
697 | let keymapFile = directory.appendingPathComponent("keymap.toml") |
| 697 | var keymap = (try? String(contentsOf: keymapFile, encoding: .utf8)) ?? "" |
698 | var keymap = (try? String(contentsOf: keymapFile, encoding: .utf8)) ?? "" |
| |
699 | func binding(_ keys: String?, _ command: String?, _ mode: String?) -> String { [keys ?? "", command ?? "", mode ?? ""].joined(separator: "\u{0}") } |
| |
700 | var existingBindings = Set((try? TOML.parse(keymap))?.filter { $0.name == "bind" && $0.values["when"] == nil } |
| |
701 | .map { binding($0.values["keys"]?.string, $0.values["command"]?.string, $0.values["mode"]?.string) } ?? []) |
| 698 | var addedKeys = "" |
702 | var addedKeys = "" |
| 699 | for item in items { |
703 | for item in items { |
| 700 | guard case .binding(let keys, let command, let mode) = item.change else { continue } |
704 | guard case .binding(let keys, let command, let mode) = item.change, |
| |
705 | existingBindings.insert(binding(keys, command, mode)).inserted else { continue } |
| 701 | addedKeys += "\n[[bind]]\nkeys = \(UserConfig.format(.string(keys)))\ncommand = \(UserConfig.format(.string(command)))\n" |
706 | addedKeys += "\n[[bind]]\nkeys = \(UserConfig.format(.string(keys)))\ncommand = \(UserConfig.format(.string(command)))\n" |
| 702 | if let mode { addedKeys += "mode = \(UserConfig.format(.string(mode)))\n" } |
707 | if let mode { addedKeys += "mode = \(UserConfig.format(.string(mode)))\n" } |
| 703 | bindings += 1 |
708 | bindings += 1 |