branch: externals/ebdb commit ebb5fc01ac7d000a09cc1c6129cc8212c8dbd137 Author: Eric Abrahamsen <e...@ericabrahamsen.net> Commit: Eric Abrahamsen <e...@ericabrahamsen.net>
Rework diary integration, release version 0.8 The nutshell: before, we dynamically injected entries into the diary list using `diary-add-to-list'. That only kicked in when listing diary entries for a day, not when marking the calendar. We now do it "the right way", by providing a `ebdb-diary-anniversaries' function that users can put in their diary file (or Org files). * ebdb.el (ebdb-use-diary): Obsolete this option. Users now "use the diary" by explicitly putting our function in their diary file. (ebdb-diary-entries): Now a hash table, keyed on day-month pairs. (ebdb-diary-anniversaries): New function for use in diary or Org files. Checks the dynamic variable `date', and produces diary entries for all anniversaries on that date. (ebdb-init-field, ebdb-delete-field): Change the kind of data that's pushed onto `ebdb-diary-entries'. (ebdb-field-anniv-diary-entry): Simplify this function to just return the string. * ebdb.org: Note change in manual. --- ebdb.el | 111 ++++++++++++++++++++++++++++--------------------- ebdb.info | 141 +++++++++++++++++++++++++++++++------------------------------- ebdb.org | 25 ++++++----- ebdb.texi | 23 +++++----- 4 files changed, 159 insertions(+), 141 deletions(-) diff --git a/ebdb.el b/ebdb.el index 6a5e7a2..729cda0 100644 --- a/ebdb.el +++ b/ebdb.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2016-2021 Free Software Foundation, Inc. -;; Version: 0.7.1 +;; Version: 0.8 ;; Package-Requires: ((emacs "25.1") (seq "2.15")) ;; Maintainer: Eric Abrahamsen <e...@ericabrahamsen.net> @@ -62,8 +62,7 @@ (autoload 'widget-group-match "wid-edit") (autoload 'ebdb-migrate-from-bbdb "ebdb-migrate") (autoload 'eieio-customize-object "eieio-custom") -(autoload 'diary-sexp-entry "diary-lib") -(autoload 'diary-add-to-list "diary-lib") +(autoload 'diary-ordinal-suffix "diary-lib") (autoload 'org-agenda-list "org-agenda") (autoload 'org-make-tags-matcher "org") (defvar ebdb-i18n-countries) @@ -377,6 +376,10 @@ Emacs, always query before reverting." :group 'ebdb-utilities-anniv :type 'boolean) +(make-obsolete-variable + 'ebdb-use-diary + "Add %%(ebdb-diary-anniversaries) to your diary or to Org" "0.8") + (defcustom ebdb-anniversary-md-format "%B %d" "Format string used for displaying month-day anniversary dates. See the docstring of `format-time-string' for the meaning of @@ -393,26 +396,12 @@ month, and day values are available." :group 'ebdb-utilities-anniv :type 'string) -(defvar ebdb-diary-entries nil - "A list of all anniversary diary entries. -Entries are added and removed in the `ebdb-init-field' and -`ebdb-delete-field' methods of the `ebdb-field-anniversary' -class, and added with the `ebdb-diary-add-entries' function. - -Each entry is a two-element list: a string representation of the -anniversary date, and the sexp (as a string): - -\(diary-anniversary MM DD YYYY) (the year is optional)") - -;; Dynamic var needed by `diary-sexp-entry'. -(defvar original-date) - -(defun ebdb-diary-add-entries () - "Add anniversaries from EBDB to the diary." - (pcase-dolist (`(,entry ,sexp) ebdb-diary-entries) - (let ((parsed (cdr-safe (diary-sexp-entry sexp entry original-date)))) - (when parsed - (diary-add-to-list original-date parsed sexp))))) +(defvar ebdb-diary-entries (make-hash-table :test #'equal) + "Hash table holding anniversary entries for the diary. +Keys are dates in the format (MONTH DAY YEAR), values are lists +of anniversary strings. Instances of `ebdb-field-anniversary' +fields can push descriptive strings into the hash entries for +their dates. Also see `ebdb-diary-anniversaries'.") (defcustom ebdb-before-load-hook nil "Hook run before loading databases." @@ -2203,12 +2192,35 @@ Eventually this method will go away." (list month day year)) obj))) -;; `ebdb-field-anniv-diary-entry' is defined below. +(defun ebdb-diary-anniversaries (&optional mark) + (with-no-warnings + (defvar date) + (defvar original-date)) + (let ((entries (gethash (seq-subseq date 0 2) ebdb-diary-entries))) + (when (and (null (calendar-leap-year-p (nth 2 date))) + (= 3 (nth 0 date)) (= 1 (nth 1 date))) + ;; If it's not a leap year, we "shift" all anniversaries for Feb + ;; 29th onto Mar 1. + (setq entries (append entries (gethash '(2 29) ebdb-diary-entries)))) + (when entries + (cons mark + (mapconcat (pcase-lambda (`(,field ,record)) + (if (bound-and-true-p original-date) + ;; If we have `original-date', we're + ;; displaying the diary list, so we need + ;; the detailed string. + (ebdb-field-anniv-diary-entry + field record (nth 2 date)) + ;; If not, we're just marking dates on the + ;; calendar, so any non-nil response value is + ;; fine. + entry)) + entries "; "))))) + (cl-defmethod ebdb-init-field ((anniv ebdb-field-anniversary) record) - (when ebdb-use-diary - (add-to-list - 'ebdb-diary-entries - (ebdb-field-anniv-diary-entry anniv record)))) + (with-slots (date) anniv + (push (list anniv record) + (gethash (seq-subseq date 0 2) ebdb-diary-entries)))) (cl-defmethod ebdb-string ((ann ebdb-field-anniversary)) (let* ((date (slot-value ann 'date)) @@ -2230,11 +2242,12 @@ Eventually this method will go away." (cl-defmethod ebdb-delete-field ((anniv ebdb-field-anniversary) record &optional _unload) - (when ebdb-use-diary - (setq - ebdb-diary-entries - (delete (ebdb-field-anniv-diary-entry anniv record) - ebdb-diary-entries)))) + (with-slots (date) anniv + (puthash (seq-subseq date 0 2) + (seq-remove (lambda (e) + (equal e (list anniv record))) + (gethash (seq-subseq date 0 2) ebdb-diary-entries)) + ebdb-diary-entries))) ;;; Id field @@ -3222,19 +3235,22 @@ If FIELD doesn't specify a year, use the current year." (format "%d-%d-%d" year (nth 0 date) (nth 1 date))))) (cl-defmethod ebdb-field-anniv-diary-entry ((field ebdb-field-anniversary) - (record ebdb-record)) - "Add a diary entry for FIELD's date." - (let ((cal-date (slot-value field 'date))) - (list (concat (format "%s's " - (ebdb-string record)) - (if (nth 2 cal-date) - "%d%s " - "%s ") - (slot-value field 'label)) - (apply #'format (if (nth 2 cal-date) - "(diary-anniversary %s %s %s)" - "(diary-anniversary %s %s)") - cal-date)))) + (record ebdb-record) + &optional now-year) + "Produce a diary entry for FIELD's date. +The entry is a string noting how many years have passed for +RECORD's FIELD anniversary, relative to NOW-YEAR." + ;; Essentially a re-write of `diary-anniversary'. + (pcase-let* ((`(,month ,day ,year) (slot-value field 'date)) + (label (slot-value field 'label)) + (num-years (when (and year now-year) + (- now-year year)))) + (concat (format "%s's " (ebdb-string record)) + (when year + (format "%d%s " num-years (diary-ordinal-suffix num-years))) + label + (unless (string= label "birthday") + " anniversary")))) ;;; `ebdb-record' subclasses @@ -4342,6 +4358,7 @@ process.") ebdb-record-tracker nil) (clrhash ebdb-org-hashtable) (clrhash ebdb-hashtable) + (clrhash ebdb-diary-entries) (clrhash ebdb-relation-hashtable)) ;; Changing which database a record belongs to. @@ -5376,8 +5393,6 @@ All the important work is done by the `ebdb-db-load' method." (cons db-file-regexp 'lisp-data-mode) auto-mode-alist)) (run-hooks 'ebdb-after-load-hook) - (when ebdb-use-diary - (add-hook 'diary-list-entries-hook #'ebdb-diary-add-entries)) (add-hook 'kill-emacs-hook #'ebdb-save-on-emacs-exit) (length ebdb-record-tracker))) diff --git a/ebdb.info b/ebdb.info index 9f5c9de..d54213d 100644 --- a/ebdb.info +++ b/ebdb.info @@ -1,4 +1,4 @@ -This is ebdb.info, produced by makeinfo version 6.7 from ebdb.texi. +This is ebdb.info, produced by makeinfo version 6.8 from ebdb.texi. Copyright © 2016 Free Software Foundation, Inc. @@ -288,24 +288,24 @@ database classes may store contacts elsewhere. Databases have a few user-facing settings: - -- Instance Variable of Database: ‘boolean’ read-only + -- Instance Variable of Database: boolean read-only If non-nil, records can only be read from the database, not edited or deleted. - -- Instance Variable of Database: ‘boolean’ auto-save + -- Instance Variable of Database: boolean auto-save If non-nil, the database’s records will not be autosaved. - -- Instance Variable of Database: ‘character’ buffer-char + -- Instance Variable of Database: character buffer-char A single character that will be displayed next to records in the *EBDB* buffer, indicating which database they belong to. - -- Instance Variable of Database: ‘boolean’ disabled + -- Instance Variable of Database: boolean disabled When non-nil , the database will essentially be ignored—no records will be read from it. Setting this to t will only take effect on next restart; to disable a database immediately, use ‘ebdb-disable-database’ below. - -- Instance Variable of Database: ‘symbol’ record-class + -- Instance Variable of Database: symbol record-class The default record class to use when creating new records in this database. The default is ‘ebdb-default-record-class’. @@ -1654,17 +1654,14 @@ File: ebdb.info, Node: Diary Integration, Next: Mail Aliases, Prev: Internati Some EBDB fields hold dates or anniversaries (most notably the ‘ebdb-field-anniversary’ field). It’s possible to integrate this -information with Emacs’ diary package (and from there to Org, via the -‘org-agenda-include-diary’ option). At present, you’ll need to have an -actual diary file present at the location indicated by ‘diary-file’, -though the file can be blank. +information with Emacs’ diary package. To activate this, add the +following line to your diary file: - -- User Option: ebdb-use-diary - If non-nil, EBDB fields with date information will attempt to add - that information to the diary. + %%(ebdb-diary-anniversaries) - When viewing the calendar, you can use the ‘d’ key to see diary -information for that day. + To integrate this with the Org Agenda, either set +‘org-agenda-include-diary’ non-nil, or the above line can be inserted +directly in an Org file (instead of the diary file). Support for this feature is rudimentary. More customization options are forthcoming. @@ -1739,6 +1736,9 @@ an *EBDB* buffer alongside an Org Agenda buffer. This function could also be added to the ‘org-agenda-mode-hook’, to pop up a buffer any time relevant records are found. + It’s also possible to have EBDB anniversaries and other date +notifications show up in the Org Agenda. *note Diary Integration::. + File: ebdb.info, Node: Citing Records, Next: Hacking EBDB, Prev: Org Integration, Up: Top @@ -2665,7 +2665,6 @@ File: ebdb.info, Node: Index, Prev: Hacking EBDB, Up: Top * ebdb-toggle-records-format: The Basics of ebdb-mode. (line 73) * ebdb-try-speedups: The EBDB Database. (line 87) -* ebdb-use-diary: Diary Integration. (line 13) * ebdb-user-name-address-re: Auto-Updating Records. (line 77) * ebdb-vm-auto-update-p: Auto-Updating Records. @@ -2762,61 +2761,61 @@ Node: Record Migration5697 Node: Variables and Options6337 Node: Migration from Org Contacts6824 Node: The EBDB Database7584 -Node: Creating Records11775 -Node: Record classes12856 -Node: Record names13201 -Node: Record Fields13876 -Node: Inserting New Fields14120 -Node: Editing Existing Fields14916 -Node: Deleting Records and Fields15516 -Node: Field Types15912 -Node: Role fields18198 -Node: Tag field20564 -Node: Mail folder field21133 -Node: MUA Interaction21461 -Node: Loading MUA Code22014 -Node: Display and Updating22727 -Node: Pop-up Buffers23493 -Node: Auto-Updating Records26343 -Node: Noticing and Automatic Rules31137 -Node: Interactive Commands32959 -Node: EBDB and MUA summary buffers35433 -Node: Sender name display35951 -Node: Summary buffer marks37178 -Node: Mail Address Completion38357 -Node: A Note on Completion40866 -Node: Specific MUAs41489 -Node: Gnus41637 -Node: Posting Styles41859 -Node: EBDB Buffers43415 -Node: Searching44626 -Node: Changing Search Behavior46340 -Node: The Basics of ebdb-mode47587 -Node: Customizing Record Display51935 -Node: Marking56255 -Node: Exporting/Formatting56682 -Node: Completion57617 -Node: Snarfing58413 -Node: Internationalization60430 -Node: Diary Integration63131 -Node: Mail Aliases63996 -Node: vCard Support64710 -Node: Org Integration65209 -Node: Citing Records67107 -Node: Hacking EBDB67865 -Node: Field Classes70458 -Node: Init and Delete Methods73639 -Node: Manipulating Field Data Programmatically75163 -Node: The Labeled Field Class76875 -Node: The Singleton Field Class77746 -Node: Actions78184 -Node: Custom Field Searching78856 -Node: Fast Lookups81723 -Node: Formatting in the EBDB Buffer83533 -Node: Writing Internationalization Libraries85609 -Node: Writing Integration For New MUAs90025 -Node: Article snarfing93473 -Node: Index94191 +Node: Creating Records11745 +Node: Record classes12826 +Node: Record names13171 +Node: Record Fields13846 +Node: Inserting New Fields14090 +Node: Editing Existing Fields14886 +Node: Deleting Records and Fields15486 +Node: Field Types15882 +Node: Role fields18168 +Node: Tag field20534 +Node: Mail folder field21103 +Node: MUA Interaction21431 +Node: Loading MUA Code21984 +Node: Display and Updating22697 +Node: Pop-up Buffers23463 +Node: Auto-Updating Records26313 +Node: Noticing and Automatic Rules31107 +Node: Interactive Commands32929 +Node: EBDB and MUA summary buffers35403 +Node: Sender name display35921 +Node: Summary buffer marks37148 +Node: Mail Address Completion38327 +Node: A Note on Completion40836 +Node: Specific MUAs41459 +Node: Gnus41607 +Node: Posting Styles41829 +Node: EBDB Buffers43385 +Node: Searching44596 +Node: Changing Search Behavior46310 +Node: The Basics of ebdb-mode47557 +Node: Customizing Record Display51905 +Node: Marking56225 +Node: Exporting/Formatting56652 +Node: Completion57587 +Node: Snarfing58383 +Node: Internationalization60400 +Node: Diary Integration63101 +Node: Mail Aliases63794 +Node: vCard Support64508 +Node: Org Integration65007 +Node: Citing Records67041 +Node: Hacking EBDB67799 +Node: Field Classes70392 +Node: Init and Delete Methods73573 +Node: Manipulating Field Data Programmatically75097 +Node: The Labeled Field Class76809 +Node: The Singleton Field Class77680 +Node: Actions78118 +Node: Custom Field Searching78790 +Node: Fast Lookups81657 +Node: Formatting in the EBDB Buffer83467 +Node: Writing Internationalization Libraries85543 +Node: Writing Integration For New MUAs89959 +Node: Article snarfing93407 +Node: Index94125 End Tag Table diff --git a/ebdb.org b/ebdb.org index e58a8b3..cc03e03 100644 --- a/ebdb.org +++ b/ebdb.org @@ -1587,22 +1587,22 @@ option will shadow the values in the variable. #+end_defopt * Diary Integration +:PROPERTIES: +:ID: d7e3f675-b37c-45ed-9636-7851611b6a19 +:END: #+CINDEX: Diary integration Some EBDB fields hold dates or anniversaries (most notably the ~ebdb-field-anniversary~ field). It's possible to integrate this -information with Emacs' diary package (and from there to Org, via the -~org-agenda-include-diary~ option). At present, you'll need to have -an actual diary file present at the location indicated by -~diary-file~, though the file can be blank. +information with Emacs' diary package. To activate this, add the +following line to your diary file: -#+ATTR_TEXINFO: :options ebdb-use-diary -#+BEGIN_defopt -If non-nil, EBDB fields with date information will attempt to add that -information to the diary. -#+END_defopt +#+begin_example +%%(ebdb-diary-anniversaries) +#+end_example -When viewing the calendar, you can use the {{{kbd(d)}}} key to see -diary information for that day. +To integrate this with the Org Agenda, either set +~org-agenda-include-diary~ non-nil, or the above line can be inserted +directly in an Org file (instead of the diary file). Support for this feature is rudimentary. More customization options are forthcoming. @@ -1671,6 +1671,9 @@ buffer. This function could also be added to the ~org-agenda-mode-hook~, to pop up a buffer any time relevant records are found. + +It's also possible to have EBDB anniversaries and other date +notifications show up in the Org Agenda. [[id:d7e3f675-b37c-45ed-9636-7851611b6a19][Diary Integration]]. * Citing Records Often one wants to share contact information into other channels: for instance, pasting a contact's name and mail address in a message diff --git a/ebdb.texi b/ebdb.texi index be21c45..158c7a6 100644 --- a/ebdb.texi +++ b/ebdb.texi @@ -1824,18 +1824,16 @@ option will shadow the values in the variable. @cindex Diary integration Some EBDB fields hold dates or anniversaries (most notably the @code{ebdb-field-anniversary} field). It's possible to integrate this -information with Emacs' diary package (and from there to Org, via the -@code{org-agenda-include-diary} option). At present, you'll need to have -an actual diary file present at the location indicated by -@code{diary-file}, though the file can be blank. - -@defopt ebdb-use-diary -If non-nil, EBDB fields with date information will attempt to add that -information to the diary. -@end defopt +information with Emacs' diary package. To activate this, add the +following line to your diary file: + +@example +%%(ebdb-diary-anniversaries) +@end example -When viewing the calendar, you can use the @kbd{d} key to see -diary information for that day. +To integrate this with the Org Agenda, either set +@code{org-agenda-include-diary} non-nil, or the above line can be inserted +directly in an Org file (instead of the diary file). Support for this feature is rudimentary. More customization options are forthcoming. @@ -1909,6 +1907,9 @@ buffer. This function could also be added to the @code{org-agenda-mode-hook}, to pop up a buffer any time relevant records are found. +It's also possible to have EBDB anniversaries and other date +notifications show up in the Org Agenda. @ref{Diary Integration}. + @node Citing Records @chapter Citing Records