Skip to content

Instantly share code, notes, and snippets.

Created November 24, 2021 16:11
What would you like to do?
Configuration I use in Doom Emacs as part of my academic reading/notetaking workflow
;; The following packages are needed:
;; 1. elfeed and elfeed-score (available from the rss doom module)
;; 2. citar
;; 3. org-ref
;; 4. org-roam and org-roam-bibtex
(defconst robo/bib-libraries (list "~/bib-lib/robo-lib.bib" "~/bib-lib/robo-temp-lib.bib")) ; All of my bib databases.
(defconst robo/main-bib-library (nth 0 robo/bib-libraries)) ; The main db is always the first
(defconst robo/main-pdfs-library-paths `("~/bib-lib/pdfs/" "/home/robo/bib-lib/temp-pdfs/")) ; PDFs directories in a list
(defconst robo/main-pdfs-library-path (nth 0 robo/main-pdfs-library-paths)) ; Main PDFs directory
(defconst robo/bib-notes-dir "~/bib-lib/roam") ; I use org-roam to manage all my notes, including bib notes.
(setq bibtex-completion-bibliography robo/bib-libraries ; My bibliography PDF location
bibtex-completion-library-path robo/main-pdfs-library-paths ; My PDF lib location
bibtex-completion-notes-path robo/bib-notes-dir
bibtex-completion-pdf-open-function (lambda (fpath)
(call-process "open" nil 0 nil fpath)))
(use-package! elfeed
(add-hook! 'elfeed-search-mode-hook 'elfeed-update)
(defun concatenate-authors (authors-list)
"Given AUTHORS-LIST, list of plists; return string of all authors concatenated."
(if (> (length authors-list) 1)
(format "%s et al." (plist-get (nth 0 authors-list) :name))
(plist-get (nth 0 authors-list) :name)))
(defun my-search-print-fn (entry)
"Print ENTRY to the buffer."
(let* ((date (elfeed-search-format-date (elfeed-entry-date entry)))
(title (or (elfeed-meta entry :title)
(elfeed-entry-title entry) ""))
(title-faces (elfeed-search--faces (elfeed-entry-tags entry)))
(entry-authors (concatenate-authors
(elfeed-meta entry :authors)))
(title-width (- (window-width) 10
(title-column (elfeed-format-column
title 100
(entry-score (elfeed-format-column (number-to-string (elfeed-score-scoring-get-score-from-entry entry)) 10 :left))
(authors-column (elfeed-format-column entry-authors 40 :left)))
(insert (propertize date 'face 'elfeed-search-date-face) " ")
(insert (propertize title-column
'face title-faces 'kbd-help title) " ")
(insert (propertize authors-column
'kbd-help entry-authors) " ")
(insert entry-score " ")))
(defun robo/elfeed-entry-to-arxiv ()
"Fetch an arXiv paper into the local library from the current elfeed entry."
(let* ((link (elfeed-entry-link elfeed-show-entry))
(match-idx (string-match "\\([0-9.]*\\)" link))
(matched-arxiv-number (match-string 1 link)))
(when matched-arxiv-number
(message "Going to arXiv: %s" matched-arxiv-number)
(arxiv-get-pdf-add-bibtex-entry matched-arxiv-number robo/main-bib-library robo/main-pdfs-library-path))))
(map! (:after elfeed
(:map elfeed-search-mode-map
:desc "Open entry" "m" #'elfeed-search-show-entry)
(:map elfeed-show-mode-map
:desc "Fetch arXiv paper to the local library" "a" #'robo/elfeed-entry-to-arxiv))
(setq elfeed-search-print-entry-function #'my-search-print-fn)
(setq elfeed-search-date-format '("%y-%m-%d" 10 :left))
(setq elfeed-search-title-max-width 110)
(setq elfeed-feeds '("" "" ""))
(setq elfeed-search-filter "@2-week-ago +unread"))
(use-package! elfeed-score
:after elfeed
(elfeed-score-load-score-file "~/.doom.d/elfeed.score") ; See the elfeed-score documentation for the score file syntax
(setq elfeed-score-serde-score-file "~/.doom.d/elfeed.serde.score")
(define-key elfeed-search-mode-map "=" elfeed-score-map))
(use-package! org-ref
:after org
(defun robo/reformat-bib-library (&optional filename)
"Formats the bibliography using biber & rebiber and updates the PDF -metadata."
(interactive "P")
(or filename (setq filename robo/main-bib-library))
(let ((cmnd (concat
(format "rebiber -i %s &&" filename) ; Get converence versions of arXiv papers
(format "biber --tool --output_align --output_indent=2 --output_fieldcase=lower --configfile=~/bib-lib/biber-myconf.conf --output_file=%s %s && " filename filename) ; Properly format the bibliography
(format "sed -i -e 's/arxiv/arXiv/gI' -e 's/journaltitle/journal /' -e 's/date /year /' %s &&" filename) ; Some replacements
(format "git commit -m \"Updating bibliography..\" %s && git push" filename) ; Commit and push the updated bib
(async-shell-command cmnd)))
(defun robo/reformat-bib-lib-hook ()
"Reformat the main bib library whenever it is saved.."
(when (equal (buffer-file-name) robo/main-bib-library) (robo/reformat-bib-library)))
(add-hook 'after-save-hook 'robo/reformat-bib-lib-hook)
(setq bibtex-dialect 'biblatex))
(use-package! oc
(require 'oc-biblatex)
(require 'oc-csl)
(require 'citar)
(setq org-cite-global-bibliography robo/bib-libraries
org-cite-insert-processor 'citar
org-cite-follow-processor 'citar
org-cite-activate-processor 'citar
org-cite-export-processors '((latex biblatex)
(t csl))))
(use-package! citar
:hook (doom-after-init-modules . citar-refresh)
;; This will add watches for the global bib files and in addition add a hook to LaTeX-mode-hook and org-mode-hook to add watches for local bibliographic files.
(citar-filenotify-setup '(LaTeX-mode-hook org-mode-hook))
(require 'citar-org)
(setq citar-bibliography robo/bib-libraries
citar-library-paths robo/main-pdfs-library-paths
citar-file-extensions '("pdf" "org" "md")
citar-file-open-function #'find-file)
(defun robo/citar-full-names (names)
"Transform names like LastName, FirstName to FirstName LastName."
(when (stringp names)
(lambda (name)
(if (eq 1 (length name))
(split-string name " ")
(let ((split-name (split-string name ", ")))
(cl-concatenate 'string (nth 1 split-name) " " (nth 0 split-name)))))
(split-string names " and ") ", ")))
(setq citar-display-transform-functions
'((t . citar-clean-string)
(("author" "editor") . robo/citar-full-names)))
(setq citar-templates
'((main . "${author editor:55} ${date year issued:4} ${title:55}")
(suffix . " ${tags keywords keywords:40}")
(preview . "${author editor} ${title}, ${journal publisher container-title collection-title booktitle} ${volume} (${year issued date}).\n")
(note . "#+title: Notes on ${author editor}, ${title}")))
;; use consult-completing-read for enhanced interface
(advice-add #'completing-read-multiple :override #'consult-completing-read-multiple))
(map! :leader
:desc "arXiv paper to library" "n x" #'arxiv-get-pdf-add-bibtex-entry
:desc "Elfeed" "n e" #'elfeed)
(after! org-roam
(setq org-roam-v2-ack t
+org-roam-open-buffer-on-find-file nil
org-roam-node-display-template "${title:80} ${tags:80}"
org-roam-completion-everywhere nil
org-roam-directory robo/bib-notes-dir))
(use-package! org-roam-bibtex
(setq org-roam-capture-templates
'(("d" "default" plain "%?"
:if-new (file+head "${slug}.org"
"#+title: ${title}\n#+SETUPFILE: ~/bib-lib/\n* References :ignore:\n#+print_bibliography:")
:unnarrowed t)
;; capture to inbox
("i" "inbox" entry "* TODO %?\n"
:target (node "45acaadd-02fb-4b93-a741-45d37ff9fd5e")
:unnarrowed t
:empty-lines-before 1
:empty-lines-after 1
:prepend t)
;; bibliography note template
("r" "bibliography reference" plain "%?"
:if-new (file+head "references/notes_${citekey}.org"
"#+title: Notes on ${title}\n#+SETUPFILE: ~/bib-lib/\n* References :ignore:\n#+print_bibliography:")
:unnarrowed t)
;; for my annotated bibliography needs
("s" "short bibliography reference (no id)" entry "* ${title} [cite:@%^{citekey}]\n%?"
:target (node "01af7246-1b2e-42a5-b8e7-68be9157241d")
:unnarrowed t
:empty-lines-before 1
:prepend t)))
(defun robo/capture-to-inbox ()
"Capture a TODO straight to the inbox."
(org-roam-capture- :goto nil
:keys "i"
:node (org-roam-node-from-id "45acaadd-02fb-4b93-a741-45d37ff9fd5e")))
(require 'org-roam-bibtex)
(setq citar-open-note-function 'orb-citar-edit-note
citar-notes-paths '("~/bib-lib/roam/references/")
orb-preformat-keywords '("citekey" "title" "url" "author-or-editor" "keywords" "file")
orb-process-file-keyword t
orb-file-field-extensions '("pdf")))
Copy link

Hi, I watch your representation on youtube, it's really nice.

But it seems to lack some part of code in this gist, like arxiv-get-pdf-add-bibtex-entry.
Could you add this function or tell me where to find it? It will help me a lot. Thanks

Copy link

rka97 commented Dec 24, 2021

Hi nicehiro, this function is part of the org-ref package used in the configuration. Here's a link to the relevant file in that project:

Copy link

@rka97 Thanks!

Copy link

tobocsic commented Jan 18, 2022

Thanks for the demo! I found it very insightful! Wanted to ask if you could share:

  1. Setup files used in the org-roam-bibtex section
  2. The configuration file for the biber-myconf.conf?

Wanted to have a look and tweak for my needs.

Also, did you ever consider using org-noter in addition/replacement to what you have above? I'm still trying to figure out the best way to keep hierarchical notes on a document-basis where I can take notes with properties pinpointing/linking to different pages of a paper/book.

Copy link

@rka97 thanks for the awesome talk Ahmed. Any chance of you sharing your elfeed and elfeed score config? Would help me hit the ground running getting some of that started :)

Copy link

bdarcus commented Feb 25, 2023

FYI, I don't think orb is anymore needed for this; see:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment