-** Lightweight messages about links
-To get a lighter weight message about the label, ref and cite links, we define a function that gives us the minibuffer message, without the menu. We run this in an idle timer.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-label-context (label)
- "Return a string of context around a label."
- (save-excursion
- (catch 'result
- (goto-char (point-min))
- (when (re-search-forward
- (format "label:%s\\b" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
-
- (goto-char (point-min))
- (when (re-search-forward
- (format "\\label{%s}" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
-
- (goto-char (point-min))
- (when (re-search-forward
- (format "^#\\+label:\\s-*\\(%s\\)\\b" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
-
- (goto-char (point-min))
- (when (re-search-forward
- (format "^#\\+tblname:\\s-*\\(%s\\)\\b" label) nil t)
- (throw 'result (buffer-substring
- (progn
- (previous-line)
- (beginning-of-line)
- (point))
- (progn
- (forward-line 4)
- (point)))))
- (throw 'result "!!! NO CONTEXT FOUND !!!"))))
-
-
-(defun org-ref-link-message ()
- "Print a minibuffer message about the link that point is on."
- (interactive)
- (when (eq major-mode 'org-mode)
- (let* ((object (org-element-context))
- (type (org-element-property :type object)))
- (save-excursion
- (cond
- ;; cite links
- ((-contains? org-ref-cite-types type)
- (message (org-ref-get-citation-string-at-point)))
-
- ;; message some context about the label we are referring to
- ((string= type "ref")
- (message "%scount: %s"
- (org-ref-get-label-context
- (org-element-property :path object))
- (org-ref-count-labels
- (org-element-property :path object))))
-
- ((string= type "eqref")
- (message "%scount: %s"
- (org-ref-get-label-context
- (org-element-property :path object))
- (org-ref-count-labels
- (org-element-property :path object))))
-
- ;; message the count
- ((string= type "label")
- (let ((count (org-ref-count-labels
- (org-element-property :path object))))
- ;; get plurality on occurrence correct
- (message (concat
- (number-to-string count)
- " occurence"
- (when (or (= count 0)
- (> count 1))
- "s")))))
-
- ((string= type "custom-id")
- (save-excursion
- (org-open-link-from-string
- (format "[[#%s]]" (org-element-property :path object)))
- (message "%s" (org-get-heading))))
-
- ;; check if the bibliography files exist.
- ((string= type "bibliography")
- (let* ((bibfile)
- ;; object is the link you clicked on
- (object (org-element-context))
- (link-string (org-element-property :path object))
- (link-string-beginning)
- (link-string-end))
- (save-excursion
- (goto-char (org-element-property :begin object))
- (search-forward link-string nil nil 1)
- (setq link-string-beginning (match-beginning 0))
- (setq link-string-end (match-end 0)))
-
- ;; make sure we are in link and not before the :
- (when (> link-string-beginning (point))
- (goto-char link-string-beginning))
-
- ;; now if we have comma separated bibliographies
- ;; we find the one clicked on. we want to
- ;; search forward to next comma from point
- (save-excursion
- (if (search-forward "," link-string-end 1 1)
- (setq key-end (- (match-end 0) 1)) ; we found a match
- (setq key-end (point)))) ; no comma found so take the point
-
- ;; and backward to previous comma from point
- (save-excursion
- (if (search-backward "," link-string-beginning 1 1)
- (setq key-beginning (+ (match-beginning 0) 1)) ; we found a match
- (setq key-beginning (point)))) ; no match found
- ;; save the key we clicked on.
- (setq bibfile
- (org-ref-strip-string
- (buffer-substring key-beginning key-end)))
- (if (file-exists-p bibfile)
- (message "%s exists." bibfile)
- (message "!!! %s NOT FOUND !!!" bibfile))))
- )))))
-#+END_SRC
-
-* Aliases
-I like convenience. Here are some aliases for faster typing.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defalias 'oro 'org-ref-open-citation-at-point)
-(defalias 'orc 'org-ref-citation-at-point)
-(defalias 'orp 'org-ref-open-pdf-at-point)
-(defalias 'oru 'org-ref-open-url-at-point)
-(defalias 'orn 'org-ref-open-notes-at-point)
-(defalias 'ornr 'org-ref-open-notes-from-reftex)
-
-(defalias 'orib 'org-ref-insert-bibliography-link)
-(defalias 'oric 'org-ref-insert-cite-link)
-(defalias 'orir 'org-ref-insert-ref-link)
-(defalias 'orsl 'org-ref-store-bibtex-entry-link)
-
-(defalias 'orcb 'org-ref-clean-bibtex-entry)
-#+END_SRC
-* Helm interface
-[[https://github.com/tmalsburg/helm-bibtex][helm-bibtex]] is a very cool interface to bibtex files. Out of the box though, it is not super convenient for org-ref. Here, we modify it to make it fit our workflow and extend it where needed.
-
-Let us add keywords as a searchable field.
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(setq helm-bibtex-additional-search-fields '(keywords))
-#+END_SRC
-
-Next, we are going to add keywords to the helm interface. This modifies the helm-bibtex function to add our keywords.
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun helm-bibtex-candidates-formatter (candidates source)
- "Formats BibTeX entries for display in results list."
- (cl-loop
- with width = (with-helm-window (window-width))
- for entry in candidates
- for entry = (cdr entry)
- for entry-key = (helm-bibtex-get-value entry 'entry-key)
- for fields = (--map (helm-bibtex-clean-string
- (helm-bibtex-get-value entry it " "))
- '(author title year has-pdf has-note entry-type))
- for fields = (-update-at 0 'helm-bibtex-shorten-authors fields)
- for fields = (append fields
- (list (or (helm-bibtex-get-value entry 'keywords)
- "" )))
- collect
- (cons (s-format "$0 $1 $2 $3 $4$5 $6" 'elt
- (-zip-with (lambda (f w) (truncate-string-to-width f w 0 ?\s))
- fields (list 36 (- width 85) 4 1 1 7 7)))
- entry-key)))
-#+END_SRC
-
-Next, we add some functions to add keywords to a bibtex entry using a helm interface, and a new action to add keywords to entries from helm-bibtex.
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-;; adapted from bibtex-utils.el
-;; these are candidates for selecting keywords/tags
-(defun org-ref-bibtex-keywords ()
- "Get keywords defined in current bibtex file.
-These are in the keywords field, and are comma or semicolon separated."
- (save-excursion
- (goto-char (point-min))
- (let (keywords kstring)
- (while (re-search-forward "^\\s-*keywords.*{\\([^}]+\\)}" nil t)
- ;; TWS - remove newlines/multiple spaces:
- (setq kstring (replace-regexp-in-string "[ \t\n]+" " " (match-string 1)))
- (mapc
- (lambda (v)
- (add-to-list 'keywords v t))
- (split-string kstring "\\(,\\|;\\)[ \n]*\\|{\\|}" t)))
- keywords)))
-
-
-(defun org-ref-set-bibtex-keywords (keywords &optional arg)
- "Add KEYWORDS to a bibtex entry.
-If KEYWORDS is a list, it is converted to a comma-separated string. The KEYWORDS are added to the beginning of the field. Otherwise KEYWORDS should be a string of comma-separate keywords."
- (interactive "sKeywords: \nP")
- (bibtex-set-field
- "keywords"
- (if arg
- ;; replace with arg
- (if (listp keywords)
- (mapconcat 'identity keywords ", ")
- keywords)
- ;; else concatentate
- (concat
- (if (listp keywords)
- (mapconcat 'identity keywords ", ")
- keywords)
- (when (not (string= "" (bibtex-autokey-get-field "keywords")))
- (concat ", " (bibtex-autokey-get-field "keywords"))))))
- (save-buffer))
-
-
-(defun helm-tag-bibtex-entry ()
- "Helm interface to add keywords to a bibtex entry.
-Run this with the point in a bibtex entry."
- (interactive)
- (let ((keyword-source `((name . "Existing keywords")
- (candidates . ,(org-ref-bibtex-keywords))
- (action . (lambda (candidate)
- (org-ref-set-bibtex-keywords
- (mapconcat
- 'identity
- (helm-marked-candidates)
- ", "))))))
- (fallback-source `((name . "Add new keywords")
- (dummy)
- (action . (lambda (candidate)
- (org-ref-set-bibtex-keywords helm-pattern)
- )))))
- (helm :sources '(keyword-source fallback-source))))
-
-(defun helm-bibtex-show-entry (key)
- "Show the entry in the BibTeX file.
-The original function in helm-bibtex has a bug where it finds the
-first key that partially matches. This version avoids that."
- (catch 'break
- (dolist (bibtex-file (if (listp helm-bibtex-bibliography)
- helm-bibtex-bibliography
- (list helm-bibtex-bibliography)))
- (let ((buf (helm-bibtex-buffer-visiting bibtex-file))
- (entries '()))
- (find-file bibtex-file)
- (bibtex-map-entries
- (lambda (key start end)
- (add-to-list 'entries (cons key start))))
- (if (assoc key entries)
- (progn
- (goto-char (cdr (assoc key entries)))
- (throw 'break t))
- (unless buf
- (kill-buffer)))))))
-
-(defun org-ref-helm-tag-entries (candidates)
- "Set tags on selected bibtex entries from helm-bibtex.
-User is prompted for tags. This function is called from `helm-bibtex'."
- (message "")
- (let ((keywords (read-input "Keywords (comma separated): ")))
- (loop for key in (helm-marked-candidates)
- do
- (save-window-excursion
- (helm-bibtex-show-entry key)
- (bibtex-set-field
- "keywords"
- (concat
- keywords
- ", " (bibtex-autokey-get-field "keywords")))
- (save-buffer)))))
-#+END_SRC
-
-Next, adapt the helm-bibtex source with these features:
-
-1. Make the default action to insert selected keys.
-2. Make open entry second action
-3. Add some features for adding keywords to bibtex entries.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(setq helm-source-bibtex
- '((name . "BibTeX entries")
- (init . helm-bibtex-init)
- (candidates . helm-bibtex-candidates)
- (filtered-candidate-transformer . helm-bibtex-candidates-formatter)
- (action . (("Insert citation" . helm-bibtex-insert-citation)
- ("Show entry" . helm-bibtex-show-entry)
- ("Open PDF file (if present)" . helm-bibtex-open-pdf)
- ("Open URL or DOI in browser" . helm-bibtex-open-url-or-doi)
- ("Insert formatted reference" . helm-bibtex-insert-reference)
- ("Insert BibTeX key" . helm-bibtex-insert-key)
- ("Insert BibTeX entry" . helm-bibtex-insert-bibtex)
- ("Attach PDF to email" . helm-bibtex-add-PDF-attachment)
- ("Edit notes" . helm-bibtex-edit-notes)
- ("Add keywords to entries" . org-ref-helm-tag-entries)
- ))))
-#+END_SRC
-
-Now, let us define a function that inserts the cite links:
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun helm-bibtex-format-org-ref (keys)
- "Insert selected KEYS as cite link. Append KEYS if you are on a link.
-Technically, this function should return a string that is inserted by helm. This function does the insertion and gives helm an empty string to insert. This lets us handle appending to a link properly.
-
-In the helm-bibtex buffer, C-u will give you a helm menu to select a new link type for the selected entries.
-
-C-u C-u will change the key at point to the selected keys.
-"
- (let* ((object (org-element-context))
- (last-char (save-excursion
- (goto-char (org-element-property :end object))
- (backward-char)
- (if (looking-at " ")
- " "
- ""))))
- (cond
- ;; case where we are in a link
- ((and (equal (org-element-type object) 'link)
- (-contains?
- org-ref-cite-types
- (org-element-property :type object)))
- (cond
- ;; no prefix. append keys
- ((equal helm-current-prefix-arg nil)
- (goto-char (org-element-property :end object))
- (while (looking-back " ") (backward-char))
- (insert (concat "," (mapconcat 'identity keys ","))))
- ;; double prefix, replace key at point
- ((equal helm-current-prefix-arg '(16))
- (setf (buffer-substring
- (org-element-property :begin object)
- (org-element-property :end object))
- (concat
- (replace-regexp-in-string
- (car (org-ref-get-bibtex-key-and-file)) ; key
- (mapconcat 'identity keys ",") ; new keys
- (org-element-property :raw-link object))
- ;; replace space at end to avoid collapsing into next word.
- last-char))
- ;; and we want to go to the end of the new link
- (goto-char
- (org-element-property :end (org-element-context))))
- (t
- (message "Not found"))))
-
- ;; We are next to a link, and we want to append
- ;; next to a link means one character back is on a link.
- ((save-excursion
- (backward-char)
- (and (equal (org-element-type (org-element-context)) 'link)
- (-contains?
- org-ref-cite-types
- (org-element-property :type (org-element-context)))))
- (while (looking-back " ") (backward-char))
- (insert (concat "," (mapconcat 'identity keys ","))))
-
- ;; insert fresh link
- (t
- ;;(message-box "fresh link")
- (insert
- (concat (if (equal helm-current-prefix-arg '(4))
- (helm :sources `((name . "link types")
- (candidates . ,org-ref-cite-types)
- (action . (lambda (x) x))))
- org-ref-default-citation-link)
- ":"
- (s-join "," keys))))))
- ;; return empty string for helm
- "")
-
-(setq helm-bibtex-format-citation-functions
- '((org-mode . helm-bibtex-format-org-ref)))
-
-;;;###autoload
-(defun org-ref-helm-insert-cite-link (arg)
- "org-ref function to use helm-bibtex to insert a citation link.
-With one prefix arg, insert a ref link.
-With two prefix args, insert a label link."
- (interactive "P")
- (cond
- ((equal arg nil)
- (let ((helm-bibtex-bibliography (org-ref-find-bibliography)))
- (helm-bibtex)))
- ((equal arg '(4))
- (org-ref-helm-insert-ref-link))
- ((equal arg '(16))
- (org-ref-helm-insert-label-link))))
-
-
-;; add our own fallback entries where we want them. These appear in reverse order of adding in the menu
-(setq helm-bibtex-fallback-options
- (-insert-at 1 '("Crossref" . "http://search.crossref.org/?q=%s") helm-bibtex-fallback-options))
-
-(setq helm-bibtex-fallback-options
- (-insert-at
- 1
- '("Scopus" . "http://www.scopus.com/scopus/search/submit/xadvanced.url?searchfield=TITLE-ABS-KEY(%s)")
- helm-bibtex-fallback-options))
-
-(setq helm-bibtex-fallback-options
- (-insert-at 1 '("WOS" . "http://gateway.webofknowledge.com/gateway/Gateway.cgi?topic=%s&GWVersion=2&SrcApp=WEB&SrcAuth=HSB&DestApp=UA&DestLinkType=GeneralSearchSummary") helm-bibtex-fallback-options))
-#+END_SRC
-
-** A helm click menu
-This code provides a helm interface to things you can do when you click on a citation link. This is an alternative to the minibuffer menu.
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(defun org-ref-get-citation-string-at-point ()
- "Get a string of a formatted citation"
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (bibfile (cdr results)))
- (if bibfile
- (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (org-ref-bib-citation)))
- "!!! No entry found !!!" )))
-
-
-(defun org-ref-cite-candidates ()
- "Generate the list of possible candidates for click actions on a cite link.
-Checks for pdf and doi, and add appropriate functions."
- (let* ((results (org-ref-get-bibtex-key-and-file))
- (key (car results))
- (pdf-file (format (concat org-ref-pdf-directory "%s.pdf") key))
- (bibfile (cdr results))
- (url (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- (bibtex-autokey-get-field "url"))))
- (doi (save-excursion
- (with-temp-buffer
- (insert-file-contents bibfile)
- (bibtex-search-entry key)
- ;; I like this better than bibtex-url which does not always find
- ;; the urls
- (bibtex-autokey-get-field "doi"))))
- (candidates `(("Quit" . org-ref-citation-at-point)
- ("Open bibtex entry" . org-ref-open-citation-at-point))))
- ;; for some reason, when there is no doi or url, they are returned as "". I
- ;; prefer nil so we correct this here.
- (when (string= doi "") (setq doi nil))
- (when (string= url "") (setq url nil))
-
- ;; Conditional pdf functions
- (if (file-exists-p pdf-file)
- (add-to-list
- 'candidates
- '("Open pdf" . org-ref-open-pdf-at-point)
- t)
- (add-to-list
- 'candidates
- '("Try to get pdf" . (lambda ()
- (save-window-excursion
- (org-ref-open-citation-at-point)
- (bibtex-beginning-of-entry)
- (doi-utils-get-bibtex-entry-pdf))))
- t))
-
-
- (add-to-list
- 'candidates
- '("Open notes" . org-ref-open-notes-at-point)
- t)
-
- ;; conditional url and doi functions
- (when (or url doi)
- (add-to-list
- 'candidates
- '("Open in browser" . org-ref-open-url-at-point)
- t))
-
- (when doi
- (mapc (lambda (x)
- (add-to-list 'candidates x t))
- `(("WOS" . org-ref-wos-at-point)
- ("Related articles in WOS" . org-ref-wos-related-at-point)
- ("Citing articles in WOS" . org-ref-wos-citing-at-point)
- ("Google Scholar" . org-ref-google-scholar-at-point)
- ("Pubmed" . org-ref-pubmed-at-point)
- ("Crossref" . org-ref-crossref-at-point)
- )))
-
- (add-to-list
- 'candidates
- '("Copy formatted citation to clipboard" . org-ref-copy-entry-as-summary)
- t)
-
- (add-to-list
- 'candidates
- '("Copy key to clipboard" . (lambda ()
- (kill-new
- (car (org-ref-get-bibtex-key-and-file)))))
- t)
-
- (add-to-list
- 'candidates
- '("Copy bibtex entry to file" . org-ref-copy-entry-at-point-to-file)
- t)
-
- (add-to-list
- 'candidates
- '("Email bibtex entry and pdf" . (lambda ()
- (save-excursion
- (org-ref-open-citation-at-point)
- (email-bibtex-entry))))
- t)
- ;; finally return a numbered list of the candidates
- (loop for i from 0
- for cell in candidates
- collect (cons (format "%2s. %s" i (car cell))
- (cdr cell)))))
-
-
-(defvar org-ref-helm-user-candidates '()
- "List of user-defined candidates to act when clicking on a cite link.
-This is a list of cons cells '((\"description\" . action)). The action function should not take an argument, and should assume point is on the cite key of interest.
-")
-
-;; example of adding your own function
-(add-to-list
- 'org-ref-helm-user-candidates
- '("Example" . (lambda () (message-box "You did it!")))
- t)
-
-;;;###autoload
-(defun org-ref-cite-click-helm (key)
- "Open helm for actions on a cite link.
-subtle points.
-1. get name and candidates before entering helm because we need the org-buffer.
-2. switch back to the org buffer before evaluating the action. most of them need the point and buffer."
- (interactive)
- (let ((name (org-ref-get-citation-string-at-point))
- (candidates (org-ref-cite-candidates))
- (cb (current-buffer)))
-
- (helm :sources `(((name . ,name)
- (candidates . ,candidates)
- (action . (lambda (f)
- (switch-to-buffer cb)
- (funcall f))))
- ((name . "User functions")
- (candidates . ,org-ref-helm-user-candidates)
- (action . (lambda (f)
- (switch-to-buffer cb)
- (funcall f))))
- ))))
-#+END_SRC
-
-#+RESULTS:
-: org-ref-cite-click-helm
-
-** A hydra click interface
-I like hydra a lot. Here we define a hydra menu you might like for the link click action.
-
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(when (featurep 'hydra)
- (require 'hydra)
- (setq hydra-is-helpful t)
-
- (defhydra org-ref-cite-hydra (:color blue)
- "
-_p_: Open pdf _w_: WOS _g_: Google Scholar _K_: Copy citation to clipboard
-_u_: Open url _r_: WOS related _P_: Pubmed _k_: Copy key to clipboard
-_n_: Open notes _c_: WOS citing _C_: Crossref _f_: Copy bibtex entry to file
-_o_: Open entry _e_: Email entry and pdf
-"
- ("o" org-ref-open-citation-at-point nil)
- ("p" org-ref-open-pdf-at-point nil)
- ("n" org-ref-open-notes-at-point nil)
- ("u" org-ref-open-url-at-point nil)
- ("w" org-ref-wos-at-point nil)
- ("r" org-ref-wos-related-at-point nil)
- ("c" org-ref-wos-citing-at-point nil)
- ("g" org-ref-google-scholar-at-point nil)
- ("P" org-ref-pubmed-at-point nil)
- ("C" org-ref-crossref-at-point nil)
- ("K" org-ref-copy-entry-as-summary nil)
- ("k" (progn
- (kill-new
- (car (org-ref-get-bibtex-key-and-file)))) nil)
- ("f" org-ref-copy-entry-at-point-to-file nil)
-
- ("e" (save-excursion
- (org-ref-open-citation-at-point)
- (email-bibtex-entry)) nil)))
-#+END_SRC
-
-* End of code
-#+BEGIN_SRC emacs-lisp :tangle org-ref.el
-(provide 'org-ref)
-
-;;; org-ref.el ends here
-#+END_SRC