X-Git-Url: https://git.donarmstrong.com/?p=org-ref.git;a=blobdiff_plain;f=org-ref.org;h=8852a84dd72c48895a52ce4113aaabfe086a617a;hp=fbff15988d49a5c3d1f0898bba2f56bfba24ff0f;hb=e8bdba99af3806a9d61c33ffc4a16e256b627260;hpb=0a6ff51e6124ba2546c16c36269db80804b534dc diff --git a/org-ref.org b/org-ref.org index fbff159..8852a84 100644 --- a/org-ref.org +++ b/org-ref.org @@ -12,6 +12,20 @@ This document is an experiment at creating a literate program to provide similar 4. Exportable links to LaTeX 5. Utility functions for dealing with bibtex files and org-files +Some additional features include +1. Get minibuffer messages for the cite/ref/label link under point + +With helm integration (default) you can: + +1. C-c ] to insert a citation link + in helm-bibtex + - Enter to insert or append citation(s) + - C-u Enter to insert an alternative cite link + - C-u C-u Enter to replace the citation at point +2. C-u C-c ] to insert a ref link with helm completion +3. C-u C-u C-c ] to insert a label with completion +4. M-x org-ref to get a helm completion buffer with link checks, utilities and export options + ** Header #+BEGIN_SRC emacs-lisp :tangle org-ref.el ;;; org-ref.el --- setup bibliography, cite, ref and label org-mode links. @@ -52,6 +66,7 @@ The only external require is reftex-cite (require 'reftex-cite) (require 'dash) (require 'helm) +(require 'helm-config) (require 'helm-bibtex) #+END_SRC @@ -128,8 +143,8 @@ There are some variables needed later to tell this library where you store your (defcustom org-ref-insert-cite-function - 'helm-bibtex - "Function to call to insert citation links. The default is `helm-bibtex'. org-ref modifies helm-bibtex a little bit to give org-mode citations, and to reorder default actions. You may use `org-ref-insert-cite-link' if you like the reftex interface." + 'org-ref-helm-insert-cite-link + "Function to call to insert citation links. The default is `org-ref-helm-insert-cite-link' which uses `helm-bibtex'. org-ref modifies helm-bibtex a little bit to give org-mode citations, and to reorder default actions. You may use `org-ref-insert-cite-link' if you like the reftex interface." :type 'function :group 'org-ref) @@ -218,11 +233,15 @@ We setup reftex here. We use a custom insert cite link function defined here: [[ ;; I do not remember why I put this next line in. It doesn't ;; work for org-files. Nothing very bad happens, but it gives ;; an annoying error. Commenting it out for now. - ;(reftex-parse-all) + ;(reftex-parse-all ) (make-local-variable 'reftex-cite-format) - (setq reftex-cite-format 'org) - (define-key org-mode-map (kbd org-ref-insert-cite-key) org-ref-insert-cite-function)) + (setq reftex-cite-format 'org)) + +;; define key for inserting citations +(define-key org-mode-map + (kbd org-ref-insert-cite-key) + org-ref-insert-cite-function) (add-hook 'org-mode-hook 'org-mode-reftex-setup) @@ -259,6 +278,189 @@ You can define a new citation link like this: (org-ref-define-citation-link "citez" ?z) #+END_SRC +** Messages for link at cursor +Here we setup code that shows you a context message for the element under the cursor when emacs is idle. +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defvar org-ref-message-timer nil + "Variable to store the link message timer in.") + + +(defun org-ref-show-link-messages () + "Turn on link messages. You will see a message in the +minibuffer when on a cite, ref or label link." + (interactive) + (or org-ref-message-timer + (setq org-ref-message-timer + (run-with-idle-timer 0.5 t 'org-ref-link-message)))) + + +(defun org-ref-cancel-link-messages () + "Stop showing messages in minibuffer when on a link." + (interactive) + (cancel-timer org-ref-message-timer) + (setq org-ref-message-timer nil)) + + +(when org-ref-show-citation-on-enter + (org-ref-show-link-messages)) + +;; this approach caused the selected region to not be highlighted any more. +; (add-hook 'post-command-hook 'org-ref-link-message)) +; (remove-hook 'post-command-hook 'org-ref-link-message)) +#+END_SRC + +** Messages for context under mouse pointer +Sometimes, when reading a document, I actually use the mouse more than the cursor. This code enables the mouse cursor to trigger a message in the minibuffer about what is under the cursor. I run this on a timer. + +The basic idea here is to get the mouse position, and if we can determine there is a character that (point) can move to, we move (point) and run the org-ref-link-message function. Since this runs on a timer, we store the last mouse position, and only run the function when the mouse has moved to avoid getting messages every time the timer runs. + +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defvar org-ref-last-mouse-pos nil + "Stores last mouse position for use in `org-ref-mouse-message'.") + +(defun org-ref-can-move-p () + "See if a character is under the mouse. If so return the position for `goto-char'." + (let* ((line (cddr org-ref-last-mouse-pos)) + (col (cadr org-ref-last-mouse-pos))) + (save-excursion + (goto-char (window-start)) + (forward-line line) + (if + (> (- (line-end-position) (line-beginning-position)) col) + (progn (forward-char col) (point)) + nil)))) + + +(defun org-ref-mouse-message () + "Display message for link under mouse cursor" + (interactive) + (when (not (equal (mouse-position) org-ref-last-mouse-pos)) + (setq org-ref-last-mouse-pos (mouse-position)) + (let ((p (org-ref-can-move-p))) + (when p + (save-excursion + (goto-char p) + (org-ref-link-message)))))) + + +(defvar org-ref-message-timer-mouse nil + "Store mouse timer.") + + +(defvar org-ref-mouse-message-interval 0.5 + "How often to run the mouse message timer in seconds") + + +(defun org-ref-mouse-messages-on () + "Turn on mouse messages." + (interactive) + (or org-ref-message-timer-mouse + (setq org-ref-message-timer-mouse + (run-at-time "0.5 sec" + org-ref-mouse-message-interval + 'org-ref-mouse-message)))) + + +(defun org-ref-mouse-messages-off () + "Turn off mouse messages" + (interactive) + (cancel-timer org-ref-message-timer-mouse) + (setq org-ref-message-timer-mouse nil) + (message "Mouse messages are off")) +#+END_SRC + +#+RESULTS: +: org-ref-mouse-messages-off + +** Color-coded links +Here we make the org-ref links a different color. + +citations are green +refs are blue +labels are black + +mailto:john + +cite:sokalski-2012-optim-ta,zhang-2011-spatial-tio2,li-2012-heter-ceram,li-2013-photoc + +cite*:sokalski-2012-optim-ta,zhang-2011-spatial-tio2,li-2012-heter-ceram,li-2013-photoc + +citenum:sokalski-2012-optim-ta,zhang-2011-spatial-tio2,li-2012-heter-ceram,li-2013-photoc + +ref:test + +label:test + +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defcustom org-ref-colorize-links + t + "When non-nil, change colors of links" + :group 'org-ref) + + +(defcustom org-ref-cite-color + "forest green" + "Color of cite like links" + :group 'org-ref) + + +(defcustom org-ref-ref-color + "dark red" + "Color of ref like links" + :group 'org-ref) + + +(defcustom org-ref-label-color + "black" + "Color of label links" + :group 'org-ref) + + +(defvar org-ref-cite-re nil + "regexp for cite links") + + +(setq org-ref-cite-re + (concat "\\(" (mapconcat + (lambda (x) + (replace-regexp-in-string "\*" "\\\\*" x) + ) + org-ref-cite-types "\\|") "\\)" + ":\\([a-zA-Z0-9-_:]*,?\\)*")) + + +(setq org-ref-label-re + "label:\\([a-zA-Z0-9-_:]*,?\\)*") + + +(setq org-ref-ref-re + "ref:\\([a-zA-Z0-9-_:]*,?\\)*") + + +(defface org-ref-cite-face + `((t (:inherit org-link :foreground ,org-ref-cite-color))) + "Color for cite-like links in org-ref.") + + +(defface org-ref-label-face + `((t (:inherit org-link :foreground ,org-ref-label-color))) + "Color for ref links in org-ref.") + + +(defface org-ref-ref-face + `((t (:inherit org-link :foreground ,org-ref-ref-color))) + "Face for ref links in org-ref.") + + +(when org-ref-colorize-links + (highlight-regexp org-ref-cite-re 'org-ref-cite-face) + (highlight-regexp org-ref-label-re 'org-ref-label-face) + (highlight-regexp org-ref-ref-re 'org-ref-ref-face)) +#+END_SRC + +#+RESULTS: + + * Links Most of this library is the creation of functional links to help with references and citations. ** General utilities @@ -447,7 +649,7 @@ This code provides some functions to generate a simple sorted bibliography in ht #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-get-bibtex-keys () - "return a list of unique keys in the buffer." + "Return a list of unique keys in the buffer." (let ((keys '())) (org-element-map (org-element-parse-buffer) 'link (lambda (link) @@ -457,7 +659,9 @@ This code provides some functions to generate a simple sorted bibliography in ht (key (org-ref-split-and-strip-string (plist-get plist ':path))) (when (not (-contains? keys key)) - (setq keys (append keys (list key))))))))) + (setq keys (append keys (list key)))))))) + ;; set with-affiliated to get keys in captions + nil nil nil t) ;; Sort keys alphabetically (setq keys (cl-sort keys 'string-lessp :key 'downcase)) keys)) @@ -575,7 +779,6 @@ We use a link for the bibliography so that we can click on it to open the biblio (let* ((bibfile) ;; object is the link you clicked on (object (org-element-context)) - (link-string-beginning) (link-string-end)) @@ -889,23 +1092,37 @@ In long documents, a list of figures is not uncommon. Here we create a clickable #+END_SRC ** label -The label link provides a way to create labels in org-mode. We make it clickable because we want to make sure labels are unique. This code will tell you how many instances of a label are found. We search for label links, LaTeX labels, and the org-mode format for labels. We probably should search for tblnames too. -*************** TODO search tblnames, custom_ids and check for case sensitivity -*************** END +The label link provides a way to create labels in org-mode. We make it clickable because we want to make sure labels are unique. This code will tell you how many instances of a label are found. We search for label links, LaTeX labels, and org-mode format for labels, tblnames too. #+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defun org-ref-count-labels (label) + "Counts number of matches for label in the document" + (+ (count-matches (format "label:%s\\b[^-:]" label) (point-min) (point-max)) + ;; for tblname, it is not enough to get word boundary + ;; tab-little and tab-little-2 match then. + (count-matches (format "^#\\+tblname:\\s-*%s\\b[^-:]" label) (point-min) (point-max)) + (count-matches (format "\\label{%s}" label) (point-min) (point-max)) + ;; this is the org-format #+label: + (count-matches (format "^#\\+label:\\s-*%s\\b[^-:]" label) (point-min) (point-max)) + (let ((custom-id-count 0)) + (org-map-entries + (lambda () + (when (string= label (org-entry-get (point) "CUSTOM_ID")) + (setq custom-id-count (+ 1 custom-id-count))))) + custom-id-count))) + (org-add-link-type "label" (lambda (label) "on clicking count the number of label tags used in the buffer. A number greater than one means multiple labels!" - (message (format "%s occurences" - (+ (count-matches (format "label:%s\\b[^-:]" label) (point-min) (point-max) t) - ;; for tblname, it is not enough to get word boundary - ;; tab-little and tab-little-2 match then. - (count-matches (format "^#\\+tblname:\\s-*%s\\b[^-:]" label) (point-min) (point-max) t) - (count-matches (format "\\label{%s}\\b" label) (point-min) (point-max) t) - ;; this is the org-format #+label: - (count-matches (format "^#\\+label:\\s-*%s\\b[^-:]" label) (point-min) (point-max) t))))) + (let ((count (org-ref-count-labels label))) + (message (format "%s occurence%s" + count + (if (or (= count 0) + (> count 1)) + "s" + "")) + (org-ref-count-labels label)))) (lambda (keyword desc format) (cond ((eq format 'html) (format "()" path)) @@ -1013,7 +1230,7 @@ It would be nice to use completion to enter a ref link, where a list of labels i #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-get-org-labels () - "find #+LABEL: labels" + "Return a list of #+LABEL: labels." (save-excursion (goto-char (point-min)) (let ((matches '())) @@ -1024,7 +1241,7 @@ matches))) #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-get-custom-ids () - "return a list of custom_id properties in the buffer" + "Return a list of custom_id properties in the buffer." (let ((results '()) custom_id) (org-map-entries (lambda () @@ -1049,6 +1266,7 @@ Finally, we get the table names. #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-get-tblnames () + "Return list of table names in the buffer." (org-element-map (org-element-parse-buffer 'element) 'table (lambda (table) (org-element-property :name table)))) @@ -1058,18 +1276,100 @@ Now, we can put all the labels together which will give us a list of candidates. #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-get-labels () - "returns a list of labels in the buffer that you can make a ref link to. this is used to auto-complete ref links." + "Returns a list of labels in the buffer that you can make a ref link to. +This is used to auto-complete ref links and in helm menus." (save-excursion (save-restriction (widen) (goto-char (point-min)) (let ((matches '())) - (while (re-search-forward "label:\\([a-zA-z0-9:-]*\\)" (point-max) t) + ;; these are the label:stuff kinds + (while (re-search-forward "[^#+]label:\\([a-zA-z0-9:-]*\\)" (point-max) t) (add-to-list 'matches (match-string-no-properties 1) t)) - (append matches (org-ref-get-org-labels) (org-ref-get-latex-labels) (org-ref-get-tblnames) (org-ref-get-custom-ids)))))) + (append matches + (org-ref-get-org-labels) + (org-ref-get-latex-labels) + (org-ref-get-tblnames) + (org-ref-get-custom-ids)))))) #+END_SRC -Now we create the completion function. This works from the org-machinery, e.g. if you type C-c C-l to insert a link, and use completion by pressing tab. +Let us make a helm function to insert a label link. This will help you enter unique labels by showing matching labels until they are all gone and you are left with a unique one. If you are on a link, it means you want to replace it. +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defun org-ref-helm-insert-label-link () + "Insert a label link. helm just shows you what labels already exist. +If you are on a label link, replace it." + (interactive) + (let* ((labels (org-ref-get-labels)) + (cb (current-buffer))) + (helm :sources `(((name . "Existing labels") + (candidates . ,labels) + ;; default action is to open to the label + (action . (lambda (label) + ;; unfortunately I do not have markers here + (org-open-link-from-string (format "ref:%s" label)))) + ;; if you select a label, replace current one + (action . (lambda (label) + (switch-to-buffer ,cb) + (cond + ;; no prefix or on a link + ((equal helm-current-prefix-arg nil) + (let* ((object (org-element-context)) + (last-char (save-excursion + (goto-char (org-element-property :end object)) + (backward-char) + (if (looking-at " ") + " " + "")))) + (when (-contains? '("label") + (org-element-property :type object)) + ;; we are on a link, so replace it. + (setf + (buffer-substring + (org-element-property :begin object) + (org-element-property :end object)) + (concat + (replace-regexp-in-string + (org-element-property :path object) + label + (org-element-property :raw-link object)) + last-char))))) + ;; no prefix options defined + )))) + ;; no matching selection creates a new label + ((name . "Create new label") + (dummy) + ;; default action creates a new label, or replaces old one + (action . (lambda (label) + (switch-to-buffer ,cb) + (let* ((object (org-element-context)) + (last-char (save-excursion + (goto-char (org-element-property :end object)) + (backward-char) + (if (looking-at " ") + " " + "")))) + (if (-contains? '("label") + (org-element-property :type object)) + ;; we are on a link, so replace it. + (setf + (buffer-substring + (org-element-property :begin object) + (org-element-property :end object)) + (concat + (replace-regexp-in-string + (org-element-property :path object) + helm-pattern + (org-element-property :raw-link object)) + last-char)) + ;; new link + (insert + (concat + "label:" + (or label + helm-pattern)))))))))))) +#+END_SRC + +Now we create a completion function. This works from the org-machinery, e.g. if you type C-c C-l to insert a link, and use completion by pressing tab. #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-complete-link (&optional arg) @@ -1087,6 +1387,89 @@ Alternatively, you may want to just call a function that inserts a link with com (insert (org-ref-complete-link))) #+END_SRC +Another alternative ref insertion is to use helm. + +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defun org-ref-helm-insert-ref-link () + "Helm menu to insert ref links to labels in the document. +If you are on link, replace with newly selected label. +Use C-u to insert a different kind of ref link. +Use C-u C-u to insert a [[#custom-id]] link +" + (interactive) + (let* ((labels (org-ref-get-labels)) + (bs (buffer-string)) + (contexts (with-temp-buffer + (insert bs) + (mapcar 'org-ref-get-label-context labels))) + (cb (current-buffer))) + + (helm :input (thing-at-point 'word) + :sources `(((name . "Available labels to ref") + (candidates . ,(loop for label in labels + for context in contexts + ;; we do some kludgy adding spaces + ;; and bars to make it "easier" to + ;; see in helm. + collect (cons (concat + label "\n" + (mapconcat + (lambda (x) + (concat " |" x)) + (split-string context "\n") + "\n" + ) "\n\n") label))) + ;; default action to replace or insert ref link. + (action . (lambda (label) + (switch-to-buffer ,cb) + + (cond + ;; no prefix or on a link + ((equal helm-current-prefix-arg nil) + (let* ((object (org-element-context)) + (last-char (save-excursion + (goto-char (org-element-property :end object)) + (backward-char) + (if (looking-at " ") + " " + "")))) + (if (-contains? '("ref" "eqref" "pageref" "nameref") + (org-element-property :type object)) + ;; we are on a link, so replace it. + (setf + (buffer-substring + (org-element-property :begin object) + (org-element-property :end object)) + (concat + (replace-regexp-in-string + (org-element-property :path object) + label + (org-element-property :raw-link object)) + last-char)) + ;; insert a new link + (insert + (concat + "ref:" label)) + ))) + ;; one prefix, alternate ref link + ((equal helm-current-prefix-arg '(4)) + (insert + (concat + (helm :sources '((name . "Ref link types") + (candidates . ("ref" "eqref" "pageref" "nameref")) + (action . (lambda (x) x)))) + ":" label))) + ;; two prefixes, insert section custom-id link + ((equal helm-current-prefix-arg '(16)) + (insert + (format "[[#%s]]" label))) + )) + )))))) +#+END_SRC + +#+RESULTS: +: org-ref-helm-insert-ref-link + ** pageref This refers to the page of a label in LaTeX. @@ -1330,9 +1713,15 @@ Now, we can see if an entry is in a file. #+BEGIN_SRC emacs-lisp :tangle org-ref.el (defun org-ref-key-in-file-p (key filename) "determine if the key is in the file" - (interactive "skey: \nsFile: ") (save-current-buffer (let ((bibtex-files (list filename))) + ;; This is something I am trying because when the bibtex file is open, and + ;; you have added to it, the only way I find to get the update to update + ;; is to close it and reopen it. or to save it and revert it. + (when (get-file-buffer filename) + (set-buffer (get-file-buffer filename)) + (save-buffer) + (revert-buffer t t)) (bibtex-search-entry key t)))) #+END_SRC @@ -1725,7 +2114,25 @@ We will want to generate formatting functions for each citation type. The reason ;; bibtex format (concat "\\" ,type (when desc (org-ref-format-citation-description desc)) "{" (mapconcat (lambda (key) key) (org-ref-split-and-strip-string keyword) ",") - "}")))))) + "}"))) + ;; for markdown we generate pandoc citations + ((eq format 'md) + (cond + (desc ;; pre and or post text + (let* ((text (split-string desc "::")) + (pre (car text)) + (post (cadr text))) + (concat + (format "[@%s," keyword) + (when pre (format " %s" pre)) + (when post (format ", %s" post)) + "]"))) + (t + (format "[%s]" + (mapconcat + (lambda (key) (concat "@" key)) + (org-ref-split-and-strip-string keyword) + "; ")))))))) #+END_SRC @@ -2019,8 +2426,6 @@ And at the end of the document put \makeglossaries. (format "\\Glspl{%s}" path))))) #+END_SRC - - * Utilities ** create simple text citation from bibtex entry @@ -2236,8 +2641,8 @@ Here is the python script for uploading. *************** TODO document how to get the cookies *************** END - -#+BEGIN_SRC python :tangle upload_bibtex_citeulike.py +# :tangle upload_bibtex_citeulike.py +#+BEGIN_SRC python #!python import pickle, requests, sys @@ -2361,6 +2766,9 @@ If no bibliography is in the buffer the `reftex-default-bibliography' is used." #+END_SRC ** Find bad cite links + :PROPERTIES: + :ID: 8515E800-EDA0-4B2A-85FD-55B6FF849203 + :END: Depending on how you enter citations, you may have citations with no corresponding bibtex entry. This function finds them and gives you a clickable table to navigate to them. #+BEGIN_SRC emacs-lisp :tangle org-ref.el @@ -2394,13 +2802,21 @@ Makes a new buffer with clickable links." (org-element-map (org-element-parse-buffer) 'link (lambda (link) (let ((plist (nth 1 link))) - (when (equal (plist-get plist ':type) "cite") - (dolist (key (org-ref-split-and-strip-string (plist-get plist ':path)) ) + (when (-contains? org-ref-cite-types (plist-get plist :type)) + (dolist (key (org-ref-split-and-strip-string (plist-get plist :path))) (when (not (index key bibtex-keys)) - (setq bad-citations (append bad-citations - `(,(format "%s [[elisp:(progn (switch-to-buffer-other-frame \"%s\")(goto-char %s))][not found here]]\n" - key (buffer-name)(plist-get plist ':begin))))) - )))))) + (message-box "%s" link) + (setq + bad-citations + (append + bad-citations + `(,(format "%s [[elisp:(progn (switch-to-buffer-other-frame \"%s\")(goto-char %s))][not found here]]\n" + key + (buffer-name) + (plist-get plist :begin))))) + ))))) + ;; set with-affilates to t to get citations in a caption + nil nil nil t) (if bad-citations (progn @@ -2418,6 +2834,162 @@ Makes a new buffer with clickable links." (message "No bad cite links found")))) #+END_SRC +** helm interface to org-ref +In [[id:8515E800-EDA0-4B2A-85FD-55B6FF849203][Find bad cite links]] we wrote a function that finds bad links and creates a buffer of links to them. + +Here we develop a similar idea, but instead of an org-buffer with links, we create helm sources for bad cite links, bad ref links, and multiple labels. + +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defun org-ref-bad-cite-candidates () + "Returns a list of conses (key . marker) where key does not exist in the known bibliography files, and marker points to the key." + (let* ((cp (point)) ; save to return to later + (bibtex-files (org-ref-find-bibliography)) + (bibtex-file-path (mapconcat + (lambda (x) + (file-name-directory (file-truename x))) + bibtex-files ":")) + (bibtex-keys (mapcar (lambda (x) (car x)) + (bibtex-global-key-alist))) + (bad-citations '())) + + (org-element-map (org-element-parse-buffer) 'link + (lambda (link) + (let ((plist (nth 1 link))) + (when (-contains? org-ref-cite-types (plist-get plist :type)) + (dolist (key (org-ref-split-and-strip-string (plist-get plist :path)) ) + (when (not (index key bibtex-keys)) + (goto-char (plist-get plist :begin)) + (re-search-forward key) + (push (cons key (point-marker)) bad-citations))) + ))) + ;; add with-affiliates to get cites in caption + nil nil nil t) + (goto-char cp) + bad-citations)) + + +(defun org-ref-bad-ref-candidates () + "Returns a list of conses (ref . marker) where ref is a ref link that does not point to anything (i.e. a label)." + ;; first get a list of legitimate labels + (let ((cp (point)) + (labels (org-ref-get-labels)) + (bad-refs '())) + ;; now loop over ref links + (goto-char (point-min)) + (org-element-map (org-element-parse-buffer) 'link + (lambda (link) + (let ((plist (nth 1 link))) + (when (or (equal (plist-get plist ':type) "ref") + (equal (plist-get plist ':type) "eqref") + (equal (plist-get plist ':type) "pageref") + (equal (plist-get plist ':type) "nameref")) + (unless (-contains? labels (plist-get plist :path)) + (goto-char (plist-get plist :begin)) + (add-to-list + 'bad-refs + (cons (plist-get plist :path) + (point-marker)))))))) + (goto-char cp) + bad-refs)) + + +(defun org-ref-bad-label-candidates () + "Return a list of labels where label is multiply defined." + (let ((labels (org-ref-get-labels)) + (multiple-labels '())) + (when (not (= (length labels) + (length (-uniq labels)))) + (dolist (label labels) + (when (> (-count (lambda (a) + (equal a label)) + labels) 1) + ;; this is a multiply defined label. + (let ((cp (point))) + (goto-char (point-min)) + (while (re-search-forward + (format "[^#+]label:%s\\s-" label) nil t) + (push (cons label (point-marker)) multiple-labels)) + + (goto-char (point-min)) + (while (re-search-forward + (format "\\label{%s}\\s-?" label) nil t) + (push (cons label (point-marker)) multiple-labels)) + + (goto-char (point-min)) + (while (re-search-forward + (format "^#\\+label:\\s-*%s" label) nil t) + (push (cons label (point-marker)) multiple-labels)) + + (goto-char (point-min)) + (while (re-search-forward + (format "^#\\+tblname:\\s-*%s" label) nil t) + (push (cons label (point-marker)) multiple-labels)) + (goto-char cp))))) + multiple-labels)) +#+END_SRC + +#+RESULTS: +: org-ref-bad-label-candidates + +Now, we have a functions for candidates, we can make helm sources for each one, and then run a helm command to view them. + +#+BEGIN_SRC emacs-lisp :tangle org-ref.el +(defun org-ref () + "Opens a helm interface to actions for org-ref. +Shows bad citations, ref links and labels" + (interactive) + (let ((cb (current-buffer)) + (bad-citations (org-ref-bad-cite-candidates)) + (bad-refs (org-ref-bad-ref-candidates)) + (bad-labels (org-ref-bad-label-candidates))) + + (helm :sources `(((name . "Bad citations") + (candidates . ,bad-citations) + (action . (lambda (marker) + (switch-to-buffer (marker-buffer marker)) + (goto-char marker)))) + ;; + ((name . "Bad Labels") + (candidates . ,bad-labels) + (action . (lambda (marker) + (switch-to-buffer (marker-buffer marker)) + (goto-char marker)))) + ;; + ((name . "Bad ref links") + (candidates . ,bad-refs) + (action . (lambda (marker) + (switch-to-buffer (marker-buffer marker)) + (goto-char marker)))) + ;; + ((name . "Utilities") + (candidates . (("Check buffer again" . org-ref) + ("Insert citation" . helm-bibtex) + ("Insert label link" . org-ref-helm-insert-label-link) + ("Insert ref link" . org-ref-helm-insert-ref-link) + ("List of figures" . org-ref-list-of-figures) + ("List of tables" . org-ref-list-of-tables) + ("Table of contents" . nil) + )) + (action . (lambda (x) + (switch-to-buffer ,cb) + (funcall x)))) + ;; + ((name . "Export functions") + (candidates . (("Extract cited entries" . org-ref-extract-bibtex-entries) + ("Export to html and open" . (lambda () (org-open-file (org-html-export-to-html)))) + ("Export to pdf and open" . (lambda () + (org-open-file (org-latex-export-to-pdf)))) + ("Export to manuscript pdf and open" . ox-manuscript-export-and-build-and-open) + ("Export submission manuscript pdf and open" . ox-manuscript-build-submission-manuscript-and-open) + + )) + (action . (lambda (x) + (switch-to-buffer ,cb) + (funcall x)))) + )))) +#+END_SRC + + ** Finding non-ascii characters I like my bibtex files to be 100% ascii. This function finds the non-ascii characters so you can replace them. @@ -2648,6 +3220,151 @@ Sometimes it may be helpful to manually change the order of citations. These fun (add-hook 'org-shiftright-hook (lambda () (org-ref-swap-citation-link 1))) (add-hook 'org-shiftleft-hook (lambda () (org-ref-swap-citation-link -1))) #+END_SRC + +** 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. @@ -2692,56 +3409,129 @@ I like convenience. Here are some aliases for faster typing. 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." - (let* ((object (org-element-context))) + "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))) - (goto-char link-string-end) - ;; sometimes there are spaces at the end of the link - ;; this code moves point pack until no spaces are there - (while (looking-back " ") (backward-char)) - (insert (concat "," (mapconcat 'identity keys ",")))) + (-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))))) + (-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 org-ref-default-citation-link + (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 ","))))))) + (s-join "," keys)))))) + ;; return empty string for helm + "") (setq helm-bibtex-format-citation-functions '((org-mode . helm-bibtex-format-org-ref))) +(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)))) + (require 'helm-bibtex) + +;; 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" (interactive) (let* ((results (org-ref-get-bibtex-key-and-file)) (key (car results)) (bibfile (cdr results))) - (save-excursion - (with-temp-buffer - (insert-file-contents bibfile) - (bibtex-search-entry key) - (org-ref-bib-citation))))) + (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. -Check for pdf and doi, and add appropriate functions." + "Generate the list of possible candidates for click actions on a cite link. +Checks for pdf and doi, and add appropriate functions." (interactive) (let* ((results (org-ref-get-bibtex-key-and-file)) (key (car results)) @@ -2759,23 +3549,35 @@ Check for pdf and doi, and add appropriate functions." ;; I like this better than bibtex-url which does not always find ;; the urls (bibtex-autokey-get-field "doi")))) - (candidates `( ;;the first candidate is a brief summary - ("Quit" . org-ref-citation-at-point) + (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)) - - (when (file-exists-p pdf-file) + ;; 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 - '("Open pdf" . org-ref-open-pdf-at-point) - t - )) + '("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 @@ -2860,20 +3662,6 @@ This is a list of cons cells '((\"description\" . action)). The action function #+RESULTS: : org-ref-cite-click-helm -To get a lighter weight message about the cite link, we define a function that gives us the minibuffer message, without the menu. We add it to a hook that updates after every command, including cursor movements. - -#+BEGIN_SRC emacs-lisp :tangle org-ref.el -(defun org-ref-cite-link-p () (interactive) - (let* ((object (org-element-context)) - (type (org-element-property :type object))) - ;; We only want this to work on citation links - (when (-contains? org-ref-cite-types type) - (message (org-ref-get-citation-string-at-point))))) - -(when org-ref-show-citation-on-enter - (add-hook 'post-command-hook 'org-ref-cite-link-p)) -#+END_SRC - * End of code #+BEGIN_SRC emacs-lisp :tangle org-ref.el (provide 'org-ref)