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
 

Reply via email to