Compare commits
10 commits
9db2e7d28c
...
8632b1f94c
Author | SHA1 | Date | |
---|---|---|---|
8632b1f94c | |||
|
7f03eafaad | ||
|
c386c16802 | ||
|
2b3a3866aa | ||
|
7231f8b2b8 | ||
|
3f62975e74 | ||
|
ae45b9413e | ||
|
bb4032eb12 | ||
|
b7d176dcfb | ||
|
bd5093e46b |
3 changed files with 123 additions and 38 deletions
|
@ -20,7 +20,7 @@ addresses, export contacts to a vCard file, put birthdays in your Org Agenda, an
|
||||||
|
|
||||||
* Installation
|
* Installation
|
||||||
|
|
||||||
Package will be submitted to NonGNU or MELPA. Wait me have time to do this work.
|
Package has been submitted to NonGNU or MELPA. You can install it through those package sources.
|
||||||
|
|
||||||
* Usage
|
* Usage
|
||||||
|
|
||||||
|
|
106
org-contacts.el
106
org-contacts.el
|
@ -900,6 +900,7 @@ This adds `org-contacts-gnus-check-mail-address' and
|
||||||
(add-hook 'gnus-article-prepare-hook #'org-contacts-gnus-check-mail-address)
|
(add-hook 'gnus-article-prepare-hook #'org-contacts-gnus-check-mail-address)
|
||||||
(add-hook 'gnus-article-prepare-hook #'org-contacts-gnus-store-last-mail))
|
(add-hook 'gnus-article-prepare-hook #'org-contacts-gnus-store-last-mail))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
(defun org-contacts-setup-completion-at-point ()
|
(defun org-contacts-setup-completion-at-point ()
|
||||||
"Add `org-contacts-message-complete-function' as a new function
|
"Add `org-contacts-message-complete-function' as a new function
|
||||||
to complete the thing at point."
|
to complete the thing at point."
|
||||||
|
@ -1272,7 +1273,13 @@ are effectively trimmed). If nil, all zero-length substrings are retained."
|
||||||
"Store the contact in `org-contacts-files' with a link."
|
"Store the contact in `org-contacts-files' with a link."
|
||||||
(when (and (eq major-mode 'org-mode)
|
(when (and (eq major-mode 'org-mode)
|
||||||
(member (buffer-file-name)
|
(member (buffer-file-name)
|
||||||
(mapcar #'expand-file-name (org-contacts-files))))
|
(mapcar #'expand-file-name (org-contacts-files)))
|
||||||
|
(not (org-before-first-heading-p))
|
||||||
|
(let ((element (org-element-at-point)))
|
||||||
|
(funcall (cdr (org-make-tags-matcher org-contacts-matcher))
|
||||||
|
(org-element-property :todo-keyword element)
|
||||||
|
(org-get-tags element)
|
||||||
|
(org-element-property :level element))))
|
||||||
(if (bound-and-true-p org-id-link-to-org-use-id)
|
(if (bound-and-true-p org-id-link-to-org-use-id)
|
||||||
(org-id-store-link)
|
(org-id-store-link)
|
||||||
(let ((headline-str (substring-no-properties (org-get-heading t t t t))))
|
(let ((headline-str (substring-no-properties (org-get-heading t t t t))))
|
||||||
|
@ -1294,38 +1301,53 @@ Each element has the form (NAME . (FILE . POSITION))."
|
||||||
(with-current-buffer (get-buffer (file-name-nondirectory file))
|
(with-current-buffer (get-buffer (file-name-nondirectory file))
|
||||||
(org-map-entries
|
(org-map-entries
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(let ((name (substring-no-properties (org-get-heading t t t t)))
|
(let* ((name (substring-no-properties (org-get-heading t t t t)))
|
||||||
(file (buffer-file-name))
|
(file (buffer-file-name))
|
||||||
(position (point)))
|
(position (point))
|
||||||
`(:name ,name :file ,file :position ,position))))))
|
;; extract properties Org entry headline at `position' as data API for better contacts searching.
|
||||||
|
(entry-properties (org-entry-properties position 'standard))
|
||||||
|
(property-name-chinese (cdr (assoc (upcase "NAME(Chinese)") entry-properties)))
|
||||||
|
(property-name-english (cdr (assoc (upcase "NAME(English)") entry-properties)))
|
||||||
|
(property-nick (cdr (assoc "NICK" entry-properties)))
|
||||||
|
(property-email (cdr (assoc "EMAIL" entry-properties)))
|
||||||
|
(property-mobile (cdr (assoc "MOBILE" entry-properties)))
|
||||||
|
(property-wechat (cdr (assoc (upcase "WeChat") entry-properties)))
|
||||||
|
(property-qq (cdr (assoc "QQ" entry-properties))))
|
||||||
|
(list :name name :file file :position position
|
||||||
|
:name-chinese property-name-chinese
|
||||||
|
:name-english property-name-english
|
||||||
|
:nick property-nick
|
||||||
|
:email property-email
|
||||||
|
:mobile property-email
|
||||||
|
:wechat property-wechat
|
||||||
|
:qq property-qq))))))
|
||||||
(org-contacts-files))))
|
(org-contacts-files))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-contacts-link-open (path)
|
(defun org-contacts-link-open (query)
|
||||||
"Open contacts: link type with jumping or searching."
|
"Open contacts: link type with jumping or searching."
|
||||||
(let ((query path))
|
(let* ((f (car (org-contacts-files)))
|
||||||
|
(fname (file-name-nondirectory f))
|
||||||
|
(buf (progn
|
||||||
|
(unless (buffer-live-p (get-buffer fname)) (find-file f))
|
||||||
|
(get-buffer fname))))
|
||||||
(cond
|
(cond
|
||||||
;; /query/ format searching
|
;; /query/ format searching
|
||||||
((string-match "/.*/" query)
|
((string-match "/.*/" query)
|
||||||
(let* ((f (car (org-contacts-files)))
|
(with-current-buffer buf
|
||||||
(buf (get-buffer (file-name-nondirectory f))))
|
(string-match "/\\(.*\\)/" query)
|
||||||
(unless (buffer-live-p buf) (find-file f))
|
(occur (match-string 1 query))))
|
||||||
(with-current-buffer buf
|
|
||||||
(string-match "/\\(.*\\)/" query)
|
|
||||||
(occur (match-string 1 query)))))
|
|
||||||
;; jump to exact contact headline directly
|
;; jump to exact contact headline directly
|
||||||
(t
|
(t
|
||||||
(let* ((f (car (org-contacts-files)))
|
(with-current-buffer buf
|
||||||
(_ (find-file f))
|
(if-let ((position (org-find-exact-headline-in-buffer query)))
|
||||||
(buf (get-buffer (file-name-nondirectory f))))
|
(goto-char (marker-position position))
|
||||||
(with-current-buffer buf
|
(user-error "[org-contacts] Can't find <%s> in your `org-contacts-files'." query)))
|
||||||
(goto-char (marker-position (org-find-exact-headline-in-buffer query))))
|
(display-buffer buf '(display-buffer-below-selected))
|
||||||
(display-buffer buf '(display-buffer-below-selected)))
|
|
||||||
|
|
||||||
;; (let* ((f (car (org-contacts-files)))
|
;; FIXME:
|
||||||
;; (_ (find-file f))
|
;; (let* ((contact-entry (map-filter
|
||||||
;; ;; FIXME:
|
|
||||||
;; (contact-entry (map-filter
|
|
||||||
;; (lambda (contact-plist)
|
;; (lambda (contact-plist)
|
||||||
;; (if (string-equal (plist-get contact-plist :name) query)
|
;; (if (string-equal (plist-get contact-plist :name) query)
|
||||||
;; contact-plist))
|
;; contact-plist))
|
||||||
|
@ -1363,20 +1385,30 @@ Each element has the form (NAME . (FILE . POSITION))."
|
||||||
|
|
||||||
(defun org-contacts-mailto-link--get-all-emails ()
|
(defun org-contacts-mailto-link--get-all-emails ()
|
||||||
"Retrieve all org-contacts EMAIL property values."
|
"Retrieve all org-contacts EMAIL property values."
|
||||||
(mapcar
|
(setq org-contacts-emails-list
|
||||||
(lambda (contact)
|
(mapcar
|
||||||
(let* ((org-contacts-buffer (find-file-noselect (car (org-contacts-files))))
|
(lambda (contact)
|
||||||
(name (plist-get contact :name))
|
(let* ((org-contacts-buffer (find-file-noselect (car (org-contacts-files))))
|
||||||
(position (plist-get contact :position))
|
(name (plist-get contact :name))
|
||||||
(email (save-excursion
|
(position (plist-get contact :position))
|
||||||
(with-current-buffer org-contacts-buffer
|
(email (save-excursion
|
||||||
(goto-char position)
|
(with-current-buffer org-contacts-buffer
|
||||||
;; (symbol-name (org-property-or-variable-value 'EMAIL))
|
(goto-char position)
|
||||||
(org-entry-get (point) "EMAIL")))))
|
;; (symbol-name (org-property-or-variable-value 'EMAIL))
|
||||||
(ignore name)
|
(when-let ((pvalue (org-entry-get (point) "EMAIL")))
|
||||||
;; (cons name email)
|
;; handle `mailto:' link. e.g. "[[mailto:yantar92@posteo.net]]", "[[mailto:yantar92@posteo.net][yantar92@posteo.net]]"
|
||||||
email))
|
;; Reference the testing file `test-org-contacts.el'.
|
||||||
(org-contacts--all-contacts)))
|
(if (string-match
|
||||||
|
"\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]\\(,\\ *\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]\\)"
|
||||||
|
pvalue)
|
||||||
|
(match-string 1 pvalue)
|
||||||
|
pvalue))))))
|
||||||
|
(ignore name)
|
||||||
|
;; (cons name email)
|
||||||
|
email))
|
||||||
|
(org-contacts--all-contacts)))
|
||||||
|
;; clean nil and empty string "" from result.
|
||||||
|
(delq "" (delq nil org-contacts-emails-list)))
|
||||||
|
|
||||||
(defun org-contacts-mailto-link-completion (&optional _arg)
|
(defun org-contacts-mailto-link-completion (&optional _arg)
|
||||||
"Org mode link `mailto:' completion with org-contacts emails."
|
"Org mode link `mailto:' completion with org-contacts emails."
|
||||||
|
|
53
test-org-contacts.el
Normal file
53
test-org-contacts.el
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
(require 'ert)
|
||||||
|
|
||||||
|
|
||||||
|
(ert-deftest ert-test-org-contacts-property-email-value-extracting-regexp ()
|
||||||
|
"Testing org-contacts property `EMAIL' value extracting regexp rule."
|
||||||
|
(let ((regexp-rule
|
||||||
|
;; "\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]" ; valid
|
||||||
|
"\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]\\(,\\ *\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]\\)" ; valid
|
||||||
|
))
|
||||||
|
(let ((pvalue "huangtc@outlook.com")) ; normal email
|
||||||
|
(if (string-match regexp-rule pvalue)
|
||||||
|
(should (string-equal (match-string 1 pvalue) "yantar92@posteo.net"))
|
||||||
|
pvalue))
|
||||||
|
|
||||||
|
(let ((pvalue "huangtc@outlook.com,")) ; has comma separator
|
||||||
|
(if (string-match regexp-rule pvalue)
|
||||||
|
(should (string-equal (match-string 1 pvalue) "yantar92@posteo.net"))
|
||||||
|
pvalue))
|
||||||
|
|
||||||
|
(let ((pvalue "huangtc@outlook.com, tristan.j.huang@gmail.com,"))
|
||||||
|
(if (string-match regexp-rule pvalue)
|
||||||
|
(should (string-equal (match-string 1 pvalue) "yantar92@posteo.net"))
|
||||||
|
pvalue))
|
||||||
|
|
||||||
|
(let ((pvalue "[[mailto:yantar92@posteo.net]]"))
|
||||||
|
(if (string-match regexp-rule pvalue)
|
||||||
|
(should (string-equal (match-string 1 pvalue) "yantar92@posteo.net"))
|
||||||
|
pvalue))
|
||||||
|
|
||||||
|
(let ((pvalue "[[mailto:yantar92@posteo.net][yantar92@posteo.net]]"))
|
||||||
|
(if (string-match regexp-rule pvalue)
|
||||||
|
(should (string-equal (match-string 1 pvalue) "yantar92@posteo.net"))
|
||||||
|
pvalue))
|
||||||
|
|
||||||
|
(let ((pvalue "[[mailto:yantar92@posteo.net][yantar92@posteo.net]], [[mailto:yantar92@gmail.com][yantar92@gmail.com]]"))
|
||||||
|
(if (string-match regexp-rule pvalue)
|
||||||
|
(should (string-equal (match-string 1 pvalue) "yantar92@posteo.net"))
|
||||||
|
pvalue))
|
||||||
|
))
|
||||||
|
|
||||||
|
;;; literal testing
|
||||||
|
|
||||||
|
;; (let ((regexp-rule "\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]")
|
||||||
|
;; (pvalue "[[mailto:yantar92@posteo.net][yantar92@posteo.net]]"))
|
||||||
|
;; (if (string-match regexp-rule pvalue)
|
||||||
|
;; (match-string 1 pvalue)
|
||||||
|
;; pvalue))
|
||||||
|
|
||||||
|
;; (let ((regexp-rule "\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]\\(,\\ *\\[\\[mailto:\\(.*\\)\\]\\(\\[.*\\]\\)\\]\\)")
|
||||||
|
;; (pvalue "[[mailto:yantar92@posteo.net][yantar92@posteo.net]], [[mailto:yantar92@gmail.com][yantar92@gmail.com]]"))
|
||||||
|
;; (if (string-match regexp-rule pvalue)
|
||||||
|
;; (match-string 1 pvalue)
|
||||||
|
;; pvalue))
|
Loading…
Reference in a new issue