[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/ebdb 1219b93 100/350: Rework *EBDB* buffer searching
From: |
Eric Abrahamsen |
Subject: |
[elpa] externals/ebdb 1219b93 100/350: Rework *EBDB* buffer searching |
Date: |
Mon, 14 Aug 2017 11:46:15 -0400 (EDT) |
branch: externals/ebdb
commit 1219b930e61ca1dd36e7f1699a13a2f5410cbc97
Author: Eric Abrahamsen <address@hidden>
Commit: Eric Abrahamsen <address@hidden>
Rework *EBDB* buffer searching
Does not fix but does address #12
BUG: Searching on the labels of ebdb-user-field-simple instances still
doesn't work correctly, but at least it errs on the side of
permissiveness.
From a plumbing point of view, there's now a clear chain from 1)
various interactive search commands that call 2)
`ebdb-search-display', which is responsible for handling and
displaying the records found by 3) `ebdb-search' which actually does
the searching.
From a UX point of view, most of the search commands can now be called
with one of three prefix keys: "/" which searches all records and
replaces the contents of the *EBDB* buffer, "|" which filters on the
already-displayed records, and "+" which appends to the
already-displayed records.
This means that the `ebdb-append-*' stuff is gone.
Meanwhile, `ebdb-search-invert' etc still exists, but has been moved
to ebdb-com.el.
---
ebdb-com.el | 272 +++++++++++++++++++++++++++++++-----------------------------
ebdb.el | 100 +++++-----------------
2 files changed, 163 insertions(+), 209 deletions(-)
diff --git a/ebdb-com.el b/ebdb-com.el
index 5107cce..1c35f3b 100644
--- a/ebdb-com.el
+++ b/ebdb-com.el
@@ -177,13 +177,10 @@ Used by `ebdb-mouse-menu'."
In the *EBDB* buffers it includes the records that are actually displayed
and its elements are (RECORD DISPLAY-FORMAT MARKER-POS MARK).")
-(defvar ebdb-append-display nil
- "Controls the behavior of the command `ebdb-append-display'.")
-
-(defvar ebdb-modeline-info (make-vector 4 nil)
+(defvar ebdb-modeline-info (make-vector 2 nil)
"Precalculated mode line info for EBDB commands.
-This is a vector [APPEND-M APPEND INVERT-M INVERT].
-APPEND-M is the mode line info if `ebdb-append-display' is non-nil.
+This is a vector [INVERT-M INVERT].
+
INVERT-M is the mode line info if `ebdb-search-invert' is non-nil.")
(defun ebdb-get-records (prompt)
@@ -210,15 +207,40 @@ If FULL is non-nil, assume that RECORDS include display
information."
(if (eieio-object-p (car records)) (list records) records)
(if (eieio-object-p records) (list records) records))))
-;; Note about EBDB prefix commands: `ebdb-append-display' and
-;; `ebdb-search-invert' are fake prefix commands. They need not
-;; precede the main commands. Also, `ebdb-append-display' can act on
-;; multiple commands.
+;; Note about EBDB prefix commands: `ebdb-search-invert' is a fake
+;; prefix commands. They need not precede the main commands.
+
+(defvar ebdb-search-invert nil
+ "Bind this variable to t in order to invert the result of `ebdb-search'.")
+
+(defun ebdb-search-invert-p ()
+ "Return variable `ebdb-search-invert' and set it to nil.
+To set it again, use command `ebdb-search-invert'."
+ (let ((result ebdb-search-invert))
+ (setq ebdb-search-invert nil)
+ (aset ebdb-modeline-info 0 nil)
+ (aset ebdb-modeline-info 1 nil)
+ result))
+
+;;;###autoload
+(defun ebdb-search-invert (&optional arg)
+ "Toggle inversion of the next search command.
+With prefix ARG a positive number, invert next search.
+With prefix ARG a negative number, do not invert next search."
+ (interactive "P")
+ (setq ebdb-search-invert
+ (or (and (numberp arg) (< 0 arg))
+ (and (not (numberp arg)) (not ebdb-search-invert))))
+ (aset ebdb-modeline-info 0 (if ebdb-search-invert "inv"))
+ (aset ebdb-modeline-info 1 (if ebdb-search-invert
+ (substitute-command-keys
+ "\\<ebdb-mode-map>\\[ebdb-search-invert]")))
+ (ebdb-prefix-message))
(defun ebdb-prefix-message ()
"Display a message about selected EBDB prefix commands."
- (let ((msg (ebdb-concat " " (elt ebdb-modeline-info 1)
- (elt ebdb-modeline-info 3))))
+ (let ((msg (ebdb-concat " " (elt ebdb-modeline-info 0)
+ (elt ebdb-modeline-info 1))))
(unless (string= "" msg) (message "%s" msg))))
;;;###autoload
@@ -232,54 +254,9 @@ display information."
(recs (or marked (list (ebdb-current-record t)))))
(if full recs (mapcar 'car recs))))
-;;;###autoload
-(defun ebdb-append-display-p ()
- "Return variable `ebdb-append-display' and reset."
- (let ((job (cond ((eq t ebdb-append-display))
- ((numberp ebdb-append-display)
- (setq ebdb-append-display (1- ebdb-append-display))
- (if (zerop ebdb-append-display)
- (setq ebdb-append-display nil))
- t)
- (ebdb-append-display
- (setq ebdb-append-display nil)
- t))))
- (cond ((numberp ebdb-append-display)
- (aset ebdb-modeline-info 0
- (format "(add %dx)" ebdb-append-display)))
- ((not ebdb-append-display)
- (aset ebdb-modeline-info 0 nil)
- (aset ebdb-modeline-info 1 nil)))
- job))
-
-;;;###autoload
-(defun ebdb-append-display (&optional arg)
- "Toggle appending next searched records in the *EBDB* buffer.
-With prefix ARG \\[universal-argument] always append.
-With ARG a positive number append for that many times.
-With ARG a negative number do not append."
- (interactive "P")
- (setq ebdb-append-display
- (cond ((and arg (listp arg)) t)
- ((and (numberp arg) (< 1 arg)) arg)
- ((or (and (numberp arg) (< arg 0)) ebdb-append-display) nil)
- (t 'once)))
- (aset ebdb-modeline-info 0
- (cond ((numberp ebdb-append-display)
- (format "(add %dx)" ebdb-append-display))
- ((eq t ebdb-append-display) "Add")
- (ebdb-append-display "add")
- (t nil)))
- (aset ebdb-modeline-info 1
- (if ebdb-append-display
- (substitute-command-keys
- "\\<ebdb-mode-map>\\[ebdb-append-display]")))
- (ebdb-prefix-message))
-
;;; Keymap
(defvar ebdb-mode-map
(let ((km (make-sparse-keymap)))
- (define-key km (kbd "+") 'ebdb-append-display)
(define-key km (kbd "!") 'ebdb-search-invert)
(define-key km (kbd "a") 'ebdb-record-action)
(define-key km (kbd "A") 'ebdb-mail-aliases)
@@ -333,12 +310,23 @@ With ARG a negative number do not append."
(define-key km (kbd "/ /") 'ebdb)
(define-key km (kbd "/ 1") 'ebdb-search-single-record)
(define-key km (kbd "/ n") 'ebdb-search-name)
+ (define-key km (kbd "| n") 'ebdb-search-name)
+ (define-key km (kbd "+ n") 'ebdb-search-name)
(define-key km (kbd "/ o") 'ebdb-search-organization)
+ (define-key km (kbd "| o") 'ebdb-search-organization)
+ (define-key km (kbd "+ o") 'ebdb-search-organization)
(define-key km (kbd "/ p") 'ebdb-search-phone)
+ (define-key km (kbd "| p") 'ebdb-search-phone)
+ (define-key km (kbd "+ p") 'ebdb-search-phone)
(define-key km (kbd "/ a") 'ebdb-search-address)
+ (define-key km (kbd "| a") 'ebdb-search-address)
+ (define-key km (kbd "+ a") 'ebdb-search-address)
(define-key km (kbd "/ m") 'ebdb-search-mail)
- (define-key km (kbd "/ N") 'ebdb-search-user-fields)
+ (define-key km (kbd "| m") 'ebdb-search-mail)
+ (define-key km (kbd "+ m") 'ebdb-search-mail)
(define-key km (kbd "/ x") 'ebdb-search-user-fields)
+ (define-key km (kbd "| x") 'ebdb-search-user-fields)
+ (define-key km (kbd "+ x") 'ebdb-search-user-fields)
(define-key km (kbd "/ c") 'ebdb-search-changed)
(define-key km (kbd "/ C") 'ebdb-search-record-class)
(define-key km (kbd "/ d") 'ebdb-search-duplicates)
@@ -688,7 +676,6 @@ SELECT and HORIZ-P have the same meaning as in
`ebdb-pop-up-window'. BUF indicates which *EBDB* buffer to use,
or nil to generate a buffer name based on the current major
mode."
- (if (ebdb-append-display-p) (setq append t))
;; All functions that call `ebdb-display-records' set the "fmt"
;; argument, but that's not guaranteed.
@@ -935,7 +922,6 @@ displayed records."
["New creation date" ebdb-creation-newer t]
["Creation date = time stamp" ebdb-creation-no-change t]
"--"
- ["Append search" ebdb-append-display t]
["Invert search" ebdb-search-invert t])
("Mail"
["Send mail" ebdb-mail t]
@@ -1149,7 +1135,7 @@ There are numerous hooks. M-x apropos ^ebdb.*hook RET
(length (ebdb-records))))
'(:eval (concat " "
(ebdb-concat " " (elt ebdb-modeline-info 0)
- (elt ebdb-modeline-info 2)))))
+ (elt ebdb-modeline-info 1)))))
mode-line-modified
;; For the mode-line we want to be fast. So we skip the checks
;; performed by `ebdb-with-db-buffer'.
@@ -1931,108 +1917,130 @@ With prefix N, omit the next N records. If negative,
omit backwards."
;; Entry points to EBDB
+;; We need to have this first thing in the interactive declaration,
+;; because if you check `this-command-keys' after the
+;; `ebdb-search-read' interactive bit, it will return ^M.
+(defsubst ebdb-search-style ()
+ "Interactive form used for setting up search style.
+
+\"Style\" means whether to append/filter/display the search
+results -- it does this by checking `this-command-keys'."
+ (cl-case (car (string-to-list (this-command-keys)))
+ (?| 'filter)
+ (?+ 'append)
+ (t 'display)))
+
+(defun ebdb-search-display (style clauses &optional fmt)
+ "Common routine for interactive searches in the *EBDB* buffer.
+
+STYLE indicates whether the results are being displayed straight,
+appended to existing records, or filtered from existing records.
+TYPE is the type of search being conducted (ie, 'name, 'mail,
+'address, etc). CRIT is the search criteria; often a regexp, but
+not necessarily. FMT is the optional formatter to use."
+ (let* ((pool (if (eql style 'filter)
+ (mapcar #'car ebdb-records)
+ (ebdb-records)))
+ (invert (ebdb-search-invert-p))
+ (recs (ebdb-search pool clauses invert)))
+ (if recs
+ (ebdb-display-records recs fmt (eql style 'append))
+ (message "No matching records"))))
+
;;;###autoload
-(defun ebdb (regexp &optional fmt)
+(defun ebdb (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP
in either the name(s), organization, address, phone, mail, or xfields."
- (interactive (list (ebdb-search-read) (ebdb-formatter-prefix)))
- (let ((records (ebdb-search (ebdb-records) regexp regexp regexp
- regexp (cons '* regexp) regexp regexp)))
- (if records
- (ebdb-display-records records fmt nil t)
- (message "No records matching '%s'" regexp))))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read)
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((name ,regexp)
+ (organization ,regexp)
+ (mail ,regexp)
+ (notes ,regexp)
+ (user ,regexp)
+ (phone ,regexp)
+ (address ,regexp))
+ fmt))
;;;###autoload
-(defun ebdb-search-name (regexp &optional fmt)
+(defun ebdb-search-name (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP in the name
\(or ``alternate'' names\)."
- (interactive (list (ebdb-search-read "names") (ebdb-formatter-prefix)))
- (ebdb-display-records (ebdb-search (ebdb-records) regexp) fmt))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read "names")
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((name ,regexp)) fmt))
;;;###autoload
-(defun ebdb-search-organization (regexp &optional fmt)
+(defun ebdb-search-organization (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP in the organization field."
- (interactive (list (ebdb-search-read "organization")
(ebdb-formatter-prefix)))
- (ebdb-display-records
- (seq-filter
- (lambda (r)
- (ebdb-record-search r 'organization regexp))
- (ebdb-records))
- fmt))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read "organization")
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((organization ,regexp)) fmt))
;;;###autoload
-(defun ebdb-search-address (regexp &optional fmt)
+(defun ebdb-search-address (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP in the address fields."
- (interactive (list (ebdb-search-read "address") (ebdb-formatter-prefix)))
- (ebdb-display-records
- (seq-filter
- (lambda (r)
- (ebdb-record-search r 'address regexp))
- (ebdb-records))
- fmt))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read "address")
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((address ,regexp)) fmt))
;;;###autoload
-(defun ebdb-search-mail (regexp &optional fmt)
+(defun ebdb-search-mail (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP in the mail address."
- (interactive (list (ebdb-search-read "mail address")
(ebdb-formatter-prefix)))
- (ebdb-display-records
- (seq-filter
- (lambda (r)
- (ebdb-record-search r 'mail regexp))
- (ebdb-records))
- fmt))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read "mail address")
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((mail ,regexp)) fmt))
;;;###autoload
-(defun ebdb-search-phone (regexp &optional fmt)
+(defun ebdb-search-phone (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP in the phones field."
- (interactive (list (ebdb-search-read "phone") (ebdb-formatter-prefix)))
- (ebdb-display-records
- (seq-filter
- (lambda (r)
- (ebdb-record-search r 'phone regexp))
- (ebdb-records))
- fmt))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read "phone")
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((phone ,regexp)) fmt))
;;;###autoload
-(defun ebdb-search-notes (regexp &optional fmt)
+(defun ebdb-search-notes (style regexp &optional fmt)
"Display all records in the EBDB matching REGEXP in the phones field."
- (interactive (list (ebdb-search-read "notes") (ebdb-formatter-prefix)))
- (ebdb-display-records
- (seq-filter
- (lambda (r)
- (ebdb-record-search r 'notes regexp))
- (ebdb-records))
- fmt))
+ (interactive (list (ebdb-search-style)
+ (ebdb-search-read "notes")
+ (ebdb-formatter-prefix)))
+ (ebdb-search-display style `((notes ,regexp)) fmt))
;;;###autoload
-(defun ebdb-search-user-fields (field regexp &optional fmt)
+(defun ebdb-search-user-fields (style field regexp &optional fmt)
"Display all EBDB records for which user field FIELD matches REGEXP."
(interactive
;; TODO: Refactor this with `ebdb-prompt-for-field-type'
- (let* ((field-alist
- (append
- (mapcar
- (lambda (f)
- (cons
- (ebdb-field-readable-name (intern (car f)))
- (intern (car f))))
- (eieio-build-class-alist 'ebdb-field-user t))
- (mapcar
- (lambda (l)
- (cons l 'ebdb-field-user-simple))
- ebdb-user-label-list)))
- (field (assoc (completing-read "Field to search (RET for all): "
- field-alist
- nil t)
- field-alist)))
- (list (if (null field) '* field)
+ (let* ((style (ebdb-search-style))
+ (field-alist
+ (append
+ (mapcar
+ (lambda (f)
+ (cons
+ (ebdb-field-readable-name (intern (car f)))
+ (intern (car f))))
+ (eieio-build-class-alist 'ebdb-field-user t))
+ (mapcar
+ (lambda (l)
+ (cons l 'ebdb-field-user-simple))
+ ebdb-user-label-list)))
+ (field (assoc (completing-read "Field to search (RET for all): "
+ field-alist
+ nil t)
+ field-alist)))
+ (list style
+ (if (null field) '* field)
(ebdb-search-read (if (null field)
"any user field"
(car field)))
(ebdb-formatter-prefix))))
- (ebdb-display-records (ebdb-search (ebdb-records) nil nil nil nil
- (cons field regexp))
- fmt))
+ (ebdb-search-display style `((user ,(cons field regexp))) fmt))
;;;###autoload
(defun ebdb-search-changed (&optional fmt)
diff --git a/ebdb.el b/ebdb.el
index c18a654..35988d3 100644
--- a/ebdb.el
+++ b/ebdb.el
@@ -4571,83 +4571,29 @@ With prefix ARG, insert string at point."
;;; Searching EBDB
-(defvar ebdb-search-invert nil
- "Bind this variable to t in order to invert the result of `ebdb-search'.")
-
-(defun ebdb-search-invert-p ()
- "Return variable `ebdb-search-invert' and set it to nil.
-To set it again, use command `ebdb-search-invert'."
- (let ((result ebdb-search-invert))
- (setq ebdb-search-invert nil)
- (aset ebdb-modeline-info 2 nil)
- (aset ebdb-modeline-info 3 nil)
- result))
-
-;;;###autoload
-(defun ebdb-search-invert (&optional arg)
- "Toggle inversion of the next search command.
-With prefix ARG a positive number, invert next search.
-With prefix ARG a negative number, do not invert next search."
- (interactive "P")
- (setq ebdb-search-invert
- (or (and (numberp arg) (< 0 arg))
- (and (not (numberp arg)) (not ebdb-search-invert))))
- (aset ebdb-modeline-info 2 (if ebdb-search-invert "inv"))
- (aset ebdb-modeline-info 3 (if ebdb-search-invert
- (substitute-command-keys
- "\\<ebdb-mode-map>\\[ebdb-search-invert]")))
- (ebdb-prefix-message))
-
-(defmacro ebdb-search (records &optional name-re org-re mail-re notes-re
- user-field-re phone-re address-re)
- "Search RECORDS for fields matching regexps.
-Regexp NAME-RE is matched against FIRST_LAST, LAST_FIRST, and
-AKA. Regexp USER-FIELD-RE is matched against user fields.
-USER-FIELD-RE may also be a cons (LABEL . RE). Then RE is
-matched against field LABEL. If LABEL is '* then RE is matched
-against any user field.
-
-This macro only generates code for those fields actually being searched for;
-literal nils at compile-time cause no code to be generated.
-
-To reverse the search, bind variable `ebdb-search-invert' to t.
-
-See also `ebdb-message-search' for fast searches using `ebdb-hashtable'
-but not allowing for regexps."
- (let (clauses)
- ;; I did not protect these vars from multiple evaluation because that
- ;; actually generates *less efficient code* in elisp, because the extra
- ;; bindings cannot easily be optimized away without lexical scope. fmh.
- (or (stringp name-re) (symbolp name-re) (error "name-re must be atomic"))
- (or (stringp org-re) (symbolp org-re) (error "org-re must be atomic"))
- (or (stringp mail-re) (symbolp mail-re) (error "mail-re must be atomic"))
- (or (stringp user-field-re) (symbolp user-field-re) (consp user-field-re)
- (error "user-field-re must be atomic or cons"))
- (or (stringp phone-re) (symbolp phone-re) (error "phone-re must be
atomic"))
- (or (stringp address-re) (symbolp address-re) (error "address-re must be
atomic"))
- (when name-re
- (push `(ebdb-record-search record 'name ,name-re) clauses))
- (when org-re
- (push `(ebdb-record-search record 'organization ,org-re)
- clauses))
-
- (when phone-re
- (push `(ebdb-record-search record 'phone ,phone-re) clauses))
- (when address-re
- (push `(ebdb-record-search record 'address ,address-re) clauses))
- (when mail-re
- (push `(ebdb-record-search record 'mail ,mail-re) clauses))
- (when notes-re
- (push `(ebdb-record-search record 'notes ,notes-re) clauses))
- (when user-field-re
- (push `(ebdb-record-search record 'user ,user-field-re) clauses))
- `(let ((case-fold-search ebdb-case-fold-search)
- (invert (ebdb-search-invert-p))
- matches)
- (dolist (record ,records)
- (unless (eq (not invert) (not (or ,@clauses)))
- (push record matches)))
- (nreverse matches))))
+(defun ebdb-search (records clauses &optional invert)
+ "Search RECORDS for records matching CLAUSES.
+
+Each element of CLAUSES is either a two-element list of (symbol
+criteria), which will be used to make a call to
+`ebdb-record-search', or it is a callable, which will be called
+with a record as the argument. All other values will be
+interpreted as t, ie the record passes."
+ (let ((case-fold-search ebdb-case-fold-search))
+ (seq-filter
+ (lambda (r)
+ (eql (null invert)
+ (catch 'found
+ (dolist (c clauses)
+ (pcase c
+ (`(,(and type (pred symbolp)) ,criteria)
+ (and (ebdb-record-search r type criteria)
+ (throw 'found t)))
+ (`,(and func (pred functionp))
+ (and (funcall func r)
+ (throw 'found t)))
+ (_ t))))))
+ records)))
(defun ebdb-search-read (&optional field)
"Read regexp to search FIELD values of records."
- [elpa] externals/ebdb 8c20202 103/350: Update company-ebdb to work with new search style, (continued)
- [elpa] externals/ebdb 8c20202 103/350: Update company-ebdb to work with new search style, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 5ec40d5 102/350: Add sections on searching and record marking to manual, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 7da77f4 089/350: Expand ebdb-with-record-edits, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 8312ea4 109/350: Allow company-ebdb to work in notmuch-message-mode as well, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb dcb7dbc 104/350: Change default keybindings in Gnus, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 9ac0f0f 111/350: Searching on empty strings should happen in field-search method, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb c43e39b 097/350: Another round of compiler-inspired fixes, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 40df5bc 114/350: Remove ebdb-new-mails-primary, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 825c4cc 112/350: Simplify the structure of ebdb-org-hashtable, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb c0979b0 135/350: Typo in ebdb-mua-check-header, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 1219b93 100/350: Rework *EBDB* buffer searching,
Eric Abrahamsen <=
- [elpa] externals/ebdb 80ef19d 108/350: Make ebdb-search-read and ebdb-search-field into generics, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 6c85728 116/350: Manual and README additions, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 4688493 125/350: Wrap ebdb-parse in save-match-data, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 587ebbc 129/350: Use ebdb-prompt-for-record in org link completion, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 8f82b0f 121/350: Complete changes from ed3e270, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 9edc54f 120/350: Merge snarf branch, basic framework of snarfing in place, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 61b533c 127/350: Simplify ebdb-record-field for strings, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb b610b96 138/350: ebdb-record-search can accept symbols for search type, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb b9da0f4 142/350: Check Organization headers and display/update organization records, Eric Abrahamsen, 2017/08/14
- [elpa] externals/ebdb 6fe34b0 145/350: Fix bugs in ebdb-annotate-message, Eric Abrahamsen, 2017/08/14