The descriptions of the variables should be self-explanatory.
(defcustom org-biber-show-compilation t
"Whether to show biber output buffer while compiling."
:group 'tlt)
(defvar org-biber-process nil
"Stores last process created by calling biber.
For internal use only.")
The main idea is the following:
- Get the name of the
.tex
-file - If it exists, use it, if not, create one by exporting the
.org
file. - Check whether the
.bcf
file needed to runbiber
exists – either in the parent or in the auxiliary folder.- If it does exist, return it.
- If it does not, ask for a
LaTeX
run.- If the user agrees, create the
.tex
file and check whether the.bcf
file exists now- If it does exist, return it.
- If it does not exist, leave a message.
- If the user does not agree, stop the command.
- If the user agrees, create the
Note:
biber
does not run if the.tex
extension still sticks to the file. It only runs if either a.bcf
extension is provided, or none at all.prin1-to-string
is needed to escape the quotation marks; else thebiber
command wouldn’t be able to run on files with spaces in their path.
(defun tex-file-get-create (&optional opt1 opt2)
"Get or create the `.tex' file corresponding to the current buffer's `.org' file.
If the search is unsuccessful, create a `.tex' file. If there
is no `.bcf' file, ask whether to run TeX to create it. Return the `.tex' file's name."
;; temporariy variables
(let* ((dir (file-name-directory (buffer-file-name)))
(texname (org-export-output-file-name ".tex")) ; without path
(texfile (expand-file-name texname dir))
(bound-aux (boundp 'tex-auxdir-name))
(bcf (org-export-output-file-name ".bcf")) ; without path
(TeX-command-sequence-max-runs-same-command 1)) ; do not repeat commands
;; check `.tex' file ;;
(if (file-exists-p texfile) texfile
(org-latex-export-to-latex))
;; check `.bcf' file ;;
(cond ((and bound-aux ; variable is bound
(file-exists-p (concat dir tex-auxdir-name bcf))) ; and bcf exists in auxdir
(prin1-to-string (concat dir tex-auxdir-name bcf))) ; first possible output: auxdir path bcf
((file-exists-p bcf) (prin1-to-string bcf)) ; second possible output: parent folder path to bcf
(t (if
(yes-or-no-p "Necessary .bcf file does not exist. Run LaTeX to create it first?") ; ask to create such a file.
(progn
(org-latex-export-to-pdf) ; third possible output: no `.bcf', so creat it!
(if (or
(when bound-aux (file-exists-p (concat dir tex-auxdir-name bcf)))
(file-exists-p bcf))
(prin1-to-string ; escape quotation marks for the biber command to work
(or (concat dir tex-auxdir-name bcf) bcf)) ; (TeX-strip-extension texfile) ; strip extension bc biber only handles`.bcf' or none
; `org-latex-export-to-latex' evaluates to the file name already
(error "No .bcf file was produced. Did you forget to include your .bib file?"))) ; else give out message
(error "Command aborted.")))))) ; else abort the compilation
The most important part about this function is that TeX-expand-list-builtin
is temporarily changed. The biber
command is given in abbreviated form (see TeX-command-list
) and afterwards expanded by TeX-expand-list
on the basis of TeX-expand-list-builtin
. Here, %s
, which takes the position of the file name in the biber
shell-command, is given by TeX-active-master-with-quotes
, which acts on the current buffer. But the current buffer is an .org
file, so biber
won’t run correctly. For this reason, the value of %s
is changed to that of tex-file-get-create
. In other words, it allows biber
to run on a file different from the one displayed in emacs.
In addition, the wrong bindings from TeX
are shown in the command line. This is because both TeX-command-run
and TeX-Biber-sentinel
use the command substitute-command-keys
on the TeX-mode-map
. Thus, in both, functions, we temporarily redefine substitute-command-keys
to return the bindings to show the biber
output defined in the tlt-TeX-utils-map
.
(defun org-call-biber ()
"Run biber on the `.tex' file corresponding to the current buffer's `.org' file and return process.
If there is no such file, create it. If there is no `.bcf' file, ask for a LaTeX run first;
see `tex-file-get-create'."
(interactive)
(let ((TeX-expand-list-builtin ; replace file name function in biber call
(add-to-list 'TeX-expand-list-builtin '("%s" tex-file-get-create nil t))) ; prepending to list works: old entry further down is ignored
(TeX-show-compilation org-biber-show-compilation) ; own variable whether to show output directly
(TeX-command-sequence-max-runs-same-command 1) ; only run once
(binding (substitute-command-keys ; store the command bindings
"\\<tlt-TeX-utils-map>\\[tlt-tex-switch-to-biber-ouput]"))) ; of the buffer switch command
(cl-letf (((symbol-function 'substitute-command-keys) (lambda (string &optional noface) binding))) ; and return it when `substitute-command-keys' is called
(setq org-biber-process (TeX-command "Biber" #'tex-file-get-create))))) ; store the process returned to make it accessible later on
org-call-biber
runs TeX-command
, which calls the function defined in TeX-command-list
. In the case of biber
, that is TeX-run-Biber
. TeX-run-Biber
now calls TeX-run-command
to create a process and execute the actual shell
command in order to then run the sentinel-function TeX-Biber-sentinel
on that process afterwards. TeX-Biber-sentinel
in turn provides information in the mini-buffer as to whether the run was successful or not, and how many errors or warnings there were.
The extra information is very nice, so we want that function, but it also uses substitute-command-keys
to display the key binding of a TeX-mode
-specific function. In order to get the bindings right, we will, as we have done above, temporarily adjust substitute-command-keys
. The resulting function is a wrapper for TeX-Biber-sentinel
, which will be advised if tlt-tex-utils-mode
is turned on.
(defun tlt-org-biber-sentinel-wrapper (fun process name)
"Show the correct key bindings to open the biber output buffer in org-mode.
Wrapper for `TeX-Biber-sentinel'."
(if (derived-mode-p 'org-mode)
(let ((binding (substitute-command-keys ; see above, same procedure
"\\<tlt-TeX-utils-map>\\[tlt-tex-switch-to-biber-ouput]")))
(cl-letf (((symbol-function 'substitute-command-keys) (lambda (string &optional noface) binding)))
(funcall fun process name)))
(funcall fun process name)))
If a biber
run did not go as expected, one might want to have a look at its .log
file, which can be found in the process buffer. The function below does just that: switch to the output buffer. Since org-call-biber
stores the process in org-biber-process
, we will only need to get the respective buffer from that process and show it.
(defun tlt-tex-switch-to-biber-output ()
(interactive)
"Display the buffer containing the output from biber.
Infom about the file on which biber ran."
(let* ((buf (process-buffer org-biber-process))
(bufname (buffer-name buf))
(name (nth 1 (split-string bufname "\""))))
(prog1
(message (format "Output from biber call on %s" name))
(display-buffer buf))))
(provide 'tlt-run-biber)
;; tlt-run-biber ends here ;;