summaryrefslogtreecommitdiff
path: root/modules/mu4e-org-contacts-integration.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-10-12 11:47:26 -0500
committerCraig Jennings <c@cjennings.net>2025-10-12 11:47:26 -0500
commit092304d9e0ccc37cc0ddaa9b136457e56a1cac20 (patch)
treeea81999b8442246c978b364dd90e8c752af50db5 /modules/mu4e-org-contacts-integration.el
changing repositories
Diffstat (limited to 'modules/mu4e-org-contacts-integration.el')
-rw-r--r--modules/mu4e-org-contacts-integration.el167
1 files changed, 167 insertions, 0 deletions
diff --git a/modules/mu4e-org-contacts-integration.el b/modules/mu4e-org-contacts-integration.el
new file mode 100644
index 00000000..7fe89389
--- /dev/null
+++ b/modules/mu4e-org-contacts-integration.el
@@ -0,0 +1,167 @@
+;;; mu4e-org-contacts-integration.el --- Integrate org-contacts with mu4e completion -*- lexical-binding: t; -*-
+;; author: Craig Jennings <c@cjennings.net>
+
+;;; Commentary:
+;; This module provides seamless integration between org-contacts and mu4e's
+;; email composition, enabling automatic contact completion in email fields.
+
+;;; Code:
+
+(require 'mu4e)
+(require 'org-contacts)
+
+;; ---------------------- Completion at Point Function -------------------------
+
+(defun cj/org-contacts-completion-at-point ()
+ "Provide completion-at-point function for org-contacts emails.
+This function is designed to work with mu4e's compose buffers."
+ (when (and (derived-mode-p 'mu4e-compose-mode 'org-msg-edit-mode)
+ (mail-abbrev-in-expansion-header-p))
+ (let* ((end (point))
+ (start (save-excursion
+ (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*" nil t)
+ (goto-char (match-end 0))
+ (point)))
+ (initial (buffer-substring-no-properties start end))
+ (contacts (cj/get-all-contact-emails)))
+ (when contacts
+ (list start end
+ (completion-table-dynamic
+ (lambda (_)
+ contacts))
+ :exclusive 'no ; Allow other completion sources
+ :annotation-function
+ (lambda (s)
+ (when-let* ((email-match (string-match "<\\([^>]+\\)>" s))
+ (email (match-string 1 s)))
+ (propertize (format " [%s]" (file-name-nondirectory contacts-file))
+ 'face 'completions-annotations))))))))
+
+;; ---------------------- Smart TAB Completion -------------------------
+
+(defun cj/mu4e-org-contacts-tab-complete ()
+ "Smart TAB completion for mu4e compose buffers.
+In email header fields (To, Cc, Bcc), complete using org-contacts.
+Elsewhere, perform the default TAB action."
+ (interactive)
+ (cond
+ ;; In email header fields, use completion-at-point
+ ((mail-abbrev-in-expansion-header-p)
+ (if (and (boundp 'completion-in-region-mode) completion-in-region-mode)
+ ;; If we're already in completion mode, cycle through candidates
+ (completion-at-point)
+ ;; Start new completion
+ (completion-at-point)))
+ ;; In org-msg-edit-mode body, use org-cycle
+ ((and (eq major-mode 'org-msg-edit-mode)
+ (not (mail-abbrev-in-expansion-header-p)))
+ (org-cycle))
+ ;; Default: indent
+ (t (indent-for-tab-command))))
+
+;; ---------------------- Comma-triggered Completion -------------------------
+
+(defun cj/mu4e-org-contacts-comma-complete ()
+ "Insert comma and optionally trigger contact completion.
+In email header fields, insert comma with space and offer completion.
+Elsewhere, just insert comma."
+ (interactive)
+ (if (mail-abbrev-in-expansion-header-p)
+ (progn
+ (insert ", ") ; Insert comma with space
+ ;; Trigger completion immediately if there's no text after the comma
+ (when (looking-at-p "\\s-*$")
+ (completion-at-point)))
+ (insert ",")))
+
+;; ---------------------- Direct Insertion (Alternative) -------------------------
+
+(defun cj/mu4e-org-contacts-insert-email ()
+ "Directly insert a contact email using completing-read.
+This bypasses the completion-at-point system for direct selection."
+ (interactive)
+ (when (mail-abbrev-in-expansion-header-p)
+ (let* ((contacts (cj/get-all-contact-emails))
+ (selected (completing-read "Contact: " contacts nil t)))
+ ;; If we're not at the beginning of a field, check if we need a comma
+ (when (and (not (save-excursion
+ (skip-chars-backward " \t")
+ (or (bolp) (looking-back "[:,]" 1))))
+ (not (looking-at-p "\\s-*$")))
+ (insert ", "))
+ (insert selected))))
+
+;; ---------------------- Setup Functions -------------------------
+
+(defun cj/mu4e-org-contacts-setup-completion ()
+ "Setup org-contacts completion for mu4e compose buffers."
+ ;; Add our completion function with high priority
+ (add-hook 'completion-at-point-functions
+ #'cj/org-contacts-completion-at-point
+ -10 t) ; High priority, buffer-local
+
+ ;; Setup completion behavior
+ (setq-local completion-ignore-case t)
+ (setq-local completion-cycle-threshold 7)
+
+ ;; Add substring matching if not already present
+ (unless (member 'substring completion-styles)
+ (setq-local completion-styles
+ (append '(substring) completion-styles))))
+
+(defun cj/mu4e-org-contacts-setup-keybindings ()
+ "Setup keybindings for org-contacts completion in compose buffers."
+ ;; TAB for smart completion
+ (local-set-key (kbd "TAB") #'cj/mu4e-org-contacts-tab-complete)
+ (local-set-key (kbd "<tab>") #'cj/mu4e-org-contacts-tab-complete)
+
+ ;; Comma for comma-triggered completion
+ (local-set-key (kbd ",") #'cj/mu4e-org-contacts-comma-complete)
+
+ ;; Optional: Direct insertion binding
+ (local-set-key (kbd "C-c e") #'cj/mu4e-org-contacts-insert-email))
+
+;; ---------------------- Mode Hooks -------------------------
+
+(defun cj/mu4e-org-contacts-compose-setup ()
+ "Setup function to be called in mu4e compose mode hooks."
+ (cj/mu4e-org-contacts-setup-completion)
+ (cj/mu4e-org-contacts-setup-keybindings))
+
+;; ---------------------- Activation -------------------------
+
+(defun cj/activate-mu4e-org-contacts-integration ()
+ "Activate org-contacts integration with mu4e email composition."
+ (interactive)
+
+ ;; Ensure mu4e's built-in completion is disabled
+ (setq mu4e-compose-complete-addresses nil)
+
+ ;; Setup hooks for mu4e-compose-mode
+ (add-hook 'mu4e-compose-mode-hook #'cj/mu4e-org-contacts-compose-setup)
+
+ ;; Setup hooks for org-msg-edit-mode (HTML email composition)
+ (with-eval-after-load 'org-msg
+ (add-hook 'org-msg-edit-mode-hook #'cj/mu4e-org-contacts-compose-setup))
+
+ ;; Remove any existing mu4e completion setup
+ (remove-hook 'mu4e-compose-mode-hook #'mu4e--compose-setup-completion)
+
+ (message "mu4e org-contacts integration activated"))
+
+(defun cj/deactivate-mu4e-org-contacts-integration ()
+ "Deactivate org-contacts integration with mu4e email composition."
+ (interactive)
+
+ ;; Remove our hooks
+ (remove-hook 'mu4e-compose-mode-hook #'cj/mu4e-org-contacts-compose-setup)
+ (remove-hook 'org-msg-edit-mode-hook #'cj/mu4e-org-contacts-compose-setup)
+
+ ;; Re-enable mu4e's built-in completion if desired
+ (setq mu4e-compose-complete-addresses t)
+ (add-hook 'mu4e-compose-mode-hook #'mu4e--compose-setup-completion)
+
+ (message "mu4e org-contacts integration deactivated"))
+
+(provide 'mu4e-org-contacts-integration)
+;;; mu4e-org-contacts-integration.el ends here \ No newline at end of file