literate-dotfiles/doom.org

115 KiB
Raw Blame History

Doom Emacs Configuration

Useful Bindings

  • align-regexp: equivalent of vim-easy-align. Very useful to align tables and stuff

General Bindings

spc : Execute command
spc < Switch to buffer
spc X org-capture
spc s s Search in buffer with swiper
spc s p Search in project
spc p p Switch project
spc p t TODOs in project
spc o f Create frame
spc o e Toggle Eshell
spc n l Store link
spc g g Magit status
spc f r Open recent file
spc b B Switch to buffer
spc b d Kill current buffer
spc b i ibuffer
spc tab . Switch to workspace
spc tab n New workspace
spc tab r Rename workspace
spc m A org-archive-subtree
spc m I org-toggle-inline-images
spc m d org-deadline
spc m e org-export-dispatch
spc m o org-set-property
spc m s org-schedule
spc m t org-todo

Org-Babel Bindings

C-c C-v p org-babel-previous-src-block
C-c C-v n org-babel-next-src-block
C-c C-v e org-babel-execute-maybe
C-c C-v o org-babel-open-src-block-result
C-c C-v v org-babel-expand-src-block
C-c C-v u org-babel-goto-src-block-head
C-c C-v g org-babel-goto-named-src-block
C-c C-v r org-babel-goto-named-result
C-c C-v b org-babel-execute-buffer
C-c C-v s org-babel-execute-subtree
C-c C-v d org-babel-demarcate-block
C-c C-v t org-babel-tangle
C-c C-v f org-babel-tangle-file
C-c C-v c org-babel-check-src-block
C-c C-v j org-babel-insert-header-arg
C-c C-v l org-babel-load-in-session
C-c C-v i org-babel-lob-ingest
C-c C-v I org-babel-view-src-block-info
C-c C-v z org-babel-switch-to-session-with-code
C-c C-v a org-babel-sha1-hash
C-c C-v h org-babel-describe-bindings
C-c C-v x org-babel-do-key-sequence-in-edit-buffer

Evil bindings

gv Selects the previous visual
o in visual mode go to the `Other` end of the selection
g= and g- Increase/decrement number
t followed by any char goes "till" next character (not included)
f followed by any char "find" next character (included)
yt. yank until next .

Basic Configuration

Personal Information

;; These are used for a number of things, particularly for GPG configuration,
;; some email clients, file templates and snippets.
(setq user-full-name "Dehaeze Thomas"
      user-mail-address "dehaeze.thomas@gmail.com")

Doom Config

  (setq doom-font (font-spec :family "Hack Nerd Font Mono" :size 12 :weight 'semi-light)
        doom-variable-pitch-font (font-spec :family "Hack Nerd Font Mono")
        doom-unicode-font (font-spec :family "Hack Nerd Font Mono" :size 12)
        doom-big-font (font-spec :family "Hack Nerd Font Mono" :size 19))
 (setq doom-theme 'doom-gruvbox)
  (setq display-line-numbers-type t)
  (use-package doom-modeline
    :hook (after-init . doom-modeline-mode)
    :custom
    (doom-modeline-height 25)
    (doom-modeline-bar-width 1)
    (doom-modeline-icon t)
    (doom-modeline-major-mode-icon t)
    (doom-modeline-major-mode-color-icon t)
    (doom-modeline-buffer-file-name-style 'truncate-upto-project)
    (doom-modeline-buffer-state-icon t)
    (doom-modeline-buffer-modification-icon t)
    (doom-modeline-minor-modes nil)
    (doom-modeline-enable-word-count nil)
    (doom-modeline-buffer-encoding t)
    (doom-modeline-indent-info nil)
    (doom-modeline-checker-simple-format t)
    (doom-modeline-vcs-max-length 12)
    (doom-modeline-env-version t)
    (doom-modeline-irc-stylize 'identity)
    (doom-modeline-github-timer nil)
    (doom-modeline-gnus-timer nil))

Evil

  (after! evil
    (map! :m  "-"  #'dired-jump))

Make movement keys work like they should

  (define-key evil-normal-state-map (kbd "<remap> <evil-next-line>") 'evil-next-visual-line)
  (define-key evil-normal-state-map (kbd "<remap> <evil-previous-line>") 'evil-previous-visual-line)
  (define-key evil-motion-state-map (kbd "<remap> <evil-next-line>") 'evil-next-visual-line)
  (define-key evil-motion-state-map (kbd "<remap> <evil-previous-line>") 'evil-previous-visual-line)

Make horizontal movement cross lines

  (setq-default evil-cross-lines t)

Which Key

(after! which-key
  (setq which-key-idle-delay 0.5
        which-key-idle-secondary-delay 0.01
        which-key-sort-order 'which-key-key-order-alpha))

Visual

Automatic line wrap.

  (global-visual-line-mode nil)

Turn off auto-fill mode that add line breaks.

  (auto-fill-mode -1)
  (remove-hook 'text-mode-hook #'turn-on-auto-fill)
  (after! org
    ;; turn off auto-fill for org-mode
    (add-hook 'org-mode-hook 'turn-off-auto-fill))
  (after! auctex
    (add-hook 'latex-mode-hook 'turn-off-auto-fill))

Useful General Functions

  (defun tdh-matlab-work ()
    "Setup Matlab Work Windows"
    (interactive)
    (delete-other-windows)
    (evil-window-vsplit)
    (evil-window-right 1)
    (switch-to-buffer "*MATLAB*")
    (evil-window-left 1)
    )

Change default alert backend

  (setq alert-default-style 'libnotify)

Lockfiles

  (setq create-lockfiles nil)

Disable highlight of current line

  (global-hl-line-mode -1)
  (after! org
    (add-hook 'org-mode-hook
        (lambda()
            (hl-line-mode -1)
            (global-hl-line-mode -1))
        't
    ))

Remap jump-forward key binding

  (with-eval-after-load 'better-jumper
    (map!
    :desc "Jump Forward"
    "C-i" #'better-jumper-jump-forward))

Magit

  (setenv "GIT_ASKPASS" "git-gui--askpass")

  (after! magit
    (setq magit-diff-refine-hunk 'all)
    (setq magit-repository-directories `(("~/Cloud/thesis/matlab/" . 1)
                                         ("~/Cloud/thesis/papers/" . 1)))
    (setq magit-repolist-columns '(("Name" 25 magit-repolist-column-ident nil)
                                   ("Status" 7 magit-repolist-column-flag)
                                   ("B<U" 3 magit-repolist-column-unpulled-from-upstream
                                    ((:right-align t)
                                     (:help-echo "Upstream changes not in branch")))
                                   ("B>U" 3 magit-repolist-column-unpushed-to-upstream
                                    ((:right-align t)
                                     (:help-echo "Local changes not in upstream")))
                                   ("Path" 99 magit-repolist-column-path nil)))
    )

Dired

  • C-c C-e Writable Dired mode, when changes are done C-c C-c. This works also with C-x C-q
  • C-c C-r use rsync to copy file in the background
  • + Create a directory
  • R Rename / move
  • C Copy
  • d Delete
  • m Mark
  • U unmark all marked
  • t invert the selection
  • u unmark / undelete
  • x actually delete files/directories marked for deletion
  • ! Execute shell command on this file, or currently marked files
  • %m mark by pattern
  • o sort by time/name
  • ( Hide details
  • ) Show git infos
  (use-package! dired-narrow
    :config
    (map! :map dired-mode-map
          :n "f"
          'dired-narrow-fuzzy))

PDF-Tools

  (use-package! pdf-tools
    :config
    (add-hook 'pdf-view-mode-hook (lambda() (linum-mode -1)))
    )

Yassnippets

  (push "~/.config/doom/snippets" yas-snippet-dirs)
  (yas-global-mode 1)

Ox-Hugo

  (defun tdh-export-everything-to-hugo ()
    "Export all the .org files in the specified directory to markdown using Hugo"
    (interactive)
    (setq org-files (directory-files (read-directory-name "Directory:" "/home/thomas/Cloud/brain/") t "org$" t))

    (while org-files
      (setq current-org-file (car org-files))
      (message "Exporting %s" current-org-file)
      (find-file current-org-file)
      (org-hugo-export-to-md)
      (setq org-files (cdr org-files))))

Org Mode

Org General Config

  (setq org-directory "~/Cloud/org/")
  (after! org
    (setq org-directory "~/Cloud/org/")

    ;; Replace the content marker, “⋯”, with a nice unicode arrow.
    (setq org-ellipsis " ⤵")

    (setq org-default-notes-file "~/Cloud/org/refile.org")

    ;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”.
    (setq org-catch-invisible-edits 'show)

    ;; The following setting hides blank lines between headings which keeps folded view nice and compact.
    (setq org-cycle-separator-lines 0)

    ;; Indent according to the outline structure
    (setq org-startup-indented t)
    (setq org-startup-folded t)

    ;; Record the information of when the task was marked as DONE
    (setq org-log-done 'time)

    ;; begining of line on heading behavior
    (setq org-special-ctrl-a/e nil)
  )

TAB was changed to toggle only the visibility state of the current subtree, rather than cycle through it recursively. This can be reversed with:

  (after! evil-org
    (remove-hook 'org-tab-first-hook #'+org-cycle-only-current-subtree-h))

Org Inline Images

Display the real size of images and not the one set with attr_latex: :width \linewidth for instance.

  (after! org
    (setq org-image-actual-width t))

Org Links

  (after! org
    (setq org-link-abbrev-alist
      '(("bib" . "~/Cloud/brain/biblio/references.bib::%s")
        ("notes" . "~/Cloud/brain/%s.org")
        ("papers" . "~/Cloud/pdfs/%s.pdf")))
    )

Org Tagging

  (after! org
    ;; Align Tags and flush right
    (setq org-tags-column -78)
    ;; Tags with fast selection keys
    (setq org-tag-alist (quote (("@home" . ?h)
                                ("@work" . ?w)
                                ("@christophe" . ?c)
                                ("@veijo" . ?v))))
    )

Org Refile

  (after! org
    (setq org-refile-targets '((org-agenda-files . (:maxlevel . 6))))
    )

Org TODO

  (after! org
    ;; Tags with fast selection keys
    (setq org-todo-keywords '(
                              (sequence "TODO(t)" "NEXT(n)" "MAIL(m)" "|" "DONE(d)")
                              (sequence "READ(r)" "BKMK(b)" "EXER(x)" "|" "DONE(d)")
                              (sequence "WAIT(w@/!)" "SDAY(s)" "|" "CANC(c@/!)")
                              (sequence "QUES(q)" "|" "ANSW(a)")
                              (sequence "EXAM(e)" "IDEA(i)" "|")
                              ))

    ;; Display of the keywords
    (setq org-todo-keyword-faces
          '(("TODO" . (:foreground "#cc241d" :weight bold)) ;; red
            ("EXER" . (:foreground "#cc241d" :weight bold)) ;; red
            ("NEXT" . (:foreground "#cc241d" :weight bold)) ;; red
            ("MAIL" . (:foreground "#cc241d" :weight bold)) ;; red
            ("READ" . (:foreground "#cc241d" :weight bold)) ;; red
            ("ANSW" . (:foreground "#689d6a" :weight bold)) ;; aqua
            ("DONE" . (:foreground "#689d6a" :weight bold)) ;; aqua
            ("WAIT" . (:foreground "#d65d0e" :weight bold)) ;; orange
            ("QUES" . (:foreground "#d79921" :weight bold)) ;; yellow
            ("CANC" . (:foreground "#a89984" :weight bold)) ;; grey
            ("SDAY" . (:foreground "#98971a" :weight bold)) ;; green
            ("BKMK" . (:foreground "#98971a" :weight bold)) ;; green
            ("IDEA" . (:foreground "#98971a" :weight bold)) ;; green
            ("EXAM" . (:foreground "#98971a" :weight bold)))) ;; green
    )

Org Archive

https://gist.github.com/Fuco1/e86fb5e0a5bb71ceafccedb5ca22fcfb Archive subtrees under the same hierarchy as original in the archive files

  (after! org
    (defadvice org-archive-subtree (around fix-hierarchy activate)
      (let* ((fix-archive-p (and (not current-prefix-arg)
                                 (not (use-region-p))))
             (location (org-archive--compute-location org-archive-location))
             (afile (car location))
             (offset (if (= 0 (length (cdr location)))
                         1
                       (1+ (string-match "[^*]" (cdr location)))))
             (buffer (or (find-buffer-visiting afile) (find-file-noselect afile))))
        ad-do-it
        (when fix-archive-p
          (with-current-buffer buffer
            (goto-char (point-max))
            (while (> (org-current-level) offset) (org-up-heading-safe))
            (let* ((olpath (org-entry-get (point) "ARCHIVE_OLPATH"))
                   (path (and olpath (split-string olpath "/")))
                   (level offset)
                   tree-text)
              (when olpath
                (org-mark-subtree)
                (setq tree-text (buffer-substring (region-beginning) (region-end)))
                (let (this-command) (org-cut-subtree))
                (goto-char (point-min))
                (save-restriction
                  (widen)
                  (-each path
                    (lambda (heading)
                      (if (re-search-forward
                           (rx-to-string
                            `(: bol (repeat ,level "*") (1+ " ") ,heading)) nil t)
                          (org-narrow-to-subtree)
                        (goto-char (point-max))
                        (unless (looking-at "^")
                          (insert "\n"))
                        (insert (make-string level ?*)
                                " "
                                heading
                                "\n"))
                      (cl-incf level)))
                  (widen)
                  (org-end-of-subtree t t)
                  (org-paste-subtree level tree-text))))))))
    )

Org Agenda

General configuration

  (after! org
    ;; File to save todo items
    (setq org-agenda-files (list "~/Cloud/org/"))

    ;; Include archived files
    (setq org-agenda-archives-mode nil)

    ;; Set priority range from A to C with default A
    (setq org-highest-priority ?A)
    (setq org-lowest-priority ?C)
    (setq org-default-priority ?C)

    ;; Set colours for priorities
    (setq org-priority-faces '((?A . (:foreground "#FB4934"))
                               (?B . (:foreground "#FABD2F"))
                               (?C . (:foreground "#98971A"))))

    ;; Open agenda in current window
    (setq org-agenda-window-setup 'current-window)

    (setq org-agenda-prefix-format
          '((agenda  . "  %?-12t% s")
            (todo  . "") ;; Don't show the filename for reading agenda
            (tags  . " %-12:c")
            (search . " %-12:c"))
          )
    )

Org Agenda Custom Views

  (after! org-agenda
    (defun tdh-org-agenda-skip-scheduled ()
      (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>"))

    (setq org-agenda-custom-commands
          '(("w" "Work"
             ((org-ql-block '(and (tags "@work")
                                  (todo "TODO")
                                  (priority "A"))
                            ((org-ql-block-header "Important TODOs")))
              (org-ql-block '(and (tags "@work")
                                  (todo "TODO")
                                  (priority "B"))
                            ((org-ql-block-header "TODOs")))
              (org-ql-block '(and (tags "@work")
                                  (todo "TODO")
                                  (priority "C"))
                            ((org-ql-block-header "Not important TODOs")))))
            ("h" "Home"
             ((org-ql-block '(and (tags "@home")
                                  (todo "TODO")
                                  (priority "A"))
                            ((org-ql-block-header "Things to do")))
              (org-ql-block '(and (tags "@home")
                                  (todo "TODO")
                                  (priority "B"))
                            ((org-ql-block-header "Things to do")))
              (org-ql-block '(and (tags "@home")
                                  (todo "TODO")
                                  (priority "C"))
                            ((org-ql-block-header "Things to do")))))
            ("q" "Questions to ask"
             ((org-ql-block '(and (todo "QUES")
                                  (tags "@christophe"))
                            ((org-ql-block-header "Questions to Christophe")))
              (org-ql-block '(and (todo "QUES")
                                  (tags "@veijo"))
                            ((org-ql-block-header "Questions to Veijo")))
              (org-ql-block '(and (todo "QUES")
                                  (not (tags "@veijo" "@christophe")))
                            ((org-ql-block-header "Other Questions")))))
            ("R" "Already read Articles and Books"
             ((org-ql-block '(and (todo "DONE")
                                  (level 1)
                                  (tags "article" "inproceedings" "techreport" "inbook"))
                            ((org-ql-block-header "Articles")))
              (org-ql-block '(and (todo "DONE")
                                  (level 1)
                                  (tags "book"))
                            ((org-ql-block-header "Books")))
              (org-ql-block '(and (todo "DONE")
                                  (level 1)
                                  (tags "phdthesis"))
                            ((org-ql-block-header "Phd Thesis")))
              (org-ql-block '(and (todo "DONE")
                                  (level 1)
                                  (not (tags "article" "inproceedings" "techreport" "inbook" "book" "phdthesis")))
                            ((org-ql-block-header "Other Things"))))
             ((org-agenda-files '("~/Cloud/brain/"))))
            ("r" "Articles and Books to read"
             ((org-ql-block '(and (todo "READ")
                                  (level 1)
                                  (tags "article" "inproceedings" "techreport" "inbook"))
                            ((org-ql-block-header "Article to Read")))
              (org-ql-block '(and (todo "READ")
                                  (level 1)
                                  (tags "book"))
                            ((org-ql-block-header "Books to Read")))
              (org-ql-block '(and (todo "READ")
                                  (level 1)
                                  (tags "phdthesis"))
                            ((org-ql-block-header "Phd Thesis to Read")))
              (org-ql-block '(and (todo "READ")
                                  (level 1)
                                  (not (tags "article" "inproceedings" "techreport" "inbook" "book" "phdthesis")))
                            ((org-ql-block-header "Other Things to Read"))))
             ((org-agenda-files '("~/Cloud/brain/")))))
          )
    )

Org Fancy Priority

  (use-package! org-fancy-priorities ; priority icons
    :hook (org-mode . org-fancy-priorities-mode)
    :config (setq org-fancy-priorities-list '("■" "■" "■")))

Org Notification based on calendar event

https://emacs.stackexchange.com/questions/3844/good-methods-for-setting-up-alarms-audio-visual-triggered-by-org-mode-events

  (after! org-agenda
    (setq appt-message-warning-time 5)
    (defun tdh-org-agenda-to-appt ()
      (interactive)
      (setq appt-time-msg-list nil)
      (org-agenda-to-appt))

    (tdh-org-agenda-to-appt)
                                          ; Display appointments as a window manager notification
    (setq appt-disp-window-function 'tdh-appt-display)
    (setq appt-delete-window-function (lambda () t))

    (setq tdh-appt-notification-app (concat (getenv "HOME") "/bin/appt-notification"))
    (defun tdh-appt-display (min-to-app new-time msg)
      (if (atom min-to-app)
          (start-process "tdh-appt-notification-app" nil tdh-appt-notification-app min-to-app msg)
        (dolist (i (number-sequence 0 (1- (length min-to-app))))
          (start-process "tdh-appt-notification-app" nil tdh-appt-notification-app (nth i min-to-app) (nth i msg)))))
    )

appt-notification script

  TIME="$1"TODO
  MSG="$2"

  dunstify --replace=85401 "Event in $TIME minutes" "$MSG"

Org Structure Template

  (after! org
    (require 'org-tempo)
    (setq org-structure-template-alist
      '(("c" . "center")
        ("C" . "comment")
        ("mm" . "src matlab")
        ("mf" . "src matlab :exports none")
        ("mv" . "src matlab :results value replace :exports none :tangle no")
        ("l" . "src emacs-lisp")
        ("q" . "quote")
        ("s" . "src")
        ("ba" . "seealso")
        ("bd" . "definition")
        ("be" . "exampl")
        ("bq" . "question")
        ("bs" . "summary")
        ("bh" . "hint")
        ("bi" . "important")
        ("bc" . "caution")
        ("bw" . "warning")
        ))
  )

Org Capture

Documentation:

  (after! org
    (setq org-capture-templates
          (quote (("t"    ; key
                   "Todo" ; name
                   entry  ; type
                   (file+headline "~/Cloud/org/work-notebook.org" "Inbox") ; target
                   "** TODO %?\n%U\n" ; template
                   )
                  ("M"    ; key
                   "Meeting" ; name
                   entry  ; type
                   (file+headline "~/Cloud/org/work-notebook.org" "Meetings") ; target
                   "** %?\n%(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n" ; template
                   )
                  ("m"    ; key
                   "Mail" ; name
                   entry  ; type
                   (file+headline "~/Cloud/org/work-notebook.org" "Mails") ; target
                   "** TODO [#A] %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n%a\n" ; template
                   )
                  ("r"    ; key
                   "Reference" ; name
                   entry  ; type
                   (file+headline "~/Cloud/org/inbox-ereader.org" "Things to Read") ; target
                   "** TODO [#B] %?\n" ; template
                   )
                  ("pm"
                   "Org-Protocol Mail"
                   entry
                   (file+headline "~/Cloud/org/work-notebook.org" "Mails")
                   "* MAIL %:description [[message:%:link][link]]\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n\n"
                   :immediate-finish t
                   )
                  ("pu"
                   "Org-Protocol Url"
                   entry
                   (file+headline "~/Cloud/org/work-notebook.org" "Inbox")
                   "* [[%:link][%:description]]\nCaptured On: %U\n\n"
                   :immediate-finish t
                   )
                  ("pt"
                   "Org-Protocol text"
                   entry
                   (file+headline "~/Cloud/org/work-notebook.org" "Inbox")
                   "* %:description\nSource: %:link\nCaptured On: %U\n\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n"
                   :immediate-finish t
                   )
                  )))
    )

Org Export

Basic configuration:

  (after! org
    ;; How many levels of headline to export
    (setq org-export-headline-levels 4)

    ;; Authorize BIND to set local variables
    (setq org-export-allow-bind-keywords t)

    ;; Use doc instead of odt
    (setq org-odt-preferred-output-format "doc")
    )

Some defaults:

  (after! org
    (setq org-export-with-author t)
    (setq org-export-with-creator nil)
    (setq org-export-with-date t)
    (setq org-export-with-toc t)
    (setq org-export-with-drawers nil)
    (setq org-export-with-sub-superscripts nil)
    (setq org-export-with-todo-keywords nil)
    )

Do not export headline with the :ignore: tag:

  ;; Used to not export headings with :ignore: tag
  (after! org
    (require 'ox-extra)
    (ox-extras-activate '(ignore-headlines)))

HTML Export

HTML Defaults

  (after! org
    ;; (setq org-html-head-extra (concat
    ;;                            "<link rel='stylesheet' href='../css/htmlize.css'>\n"
    ;;                            "<link rel='stylesheet' href='../css/readtheorg.css'>\n"
    ;;                            "<link rel='stylesheet' href='../css/zenburn.css'>\n"
    ;;                            "<link rel='text/javascript' href='../js/bootstrap.min.js'>\n"
    ;;                            "<link rel='text/javascript' href='../js/jquery.min.js'>\n"
    ;;                            "<link rel='text/javascript' href='../js/jquery.stickytableheaders.min.js'>\n"
    ;;                            "<link rel='text/javascript' href='../js/readtheorg.js'>\n"))

    ;; cleans up anything that would have been in there.
    (setq org-html-head nil)
    (setq org-html-head-include-default-style nil)
    (setq org-html-head-include-scripts nil)

    (setq org-html-viewport nil)

    (setq org-html-html5-fancy t)
    (setq org-html-doctype "xhtml-strict")

    (setq org-html-wrap-src-lines nil)

    ;; Export with css class instead of inline css
    (setq org-html-htmlize-output-type 'css)
    )

MathJax

  (after! org
    (setq org-html-mathjax-template
          "<script>MathJax = {
            tex: {
              tags: 'ams',
              macros: {bm: [\"\\\\boldsymbol{#1}\",1],}
              }
            };
            </script>
            <script type=\"text/javascript\" src=\"%PATH\"></script>")

    (setq org-html-mathjax-options
          '((path "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js")
            (scale "100")
            (align "center")
            (font "TeX")
            (linebreaks "false")
            (autonumber "AMS")
            (indent "0em")
            (multlinewidth "85%")
            (tagindent ".8em")
            (tagside "right")))
    )

MP4 Video - video link

  (defun org-video-link-export (path desc backend)
    (let ((ext (file-name-extension path)))
      (cond
       ((eq 'html backend)
        (format "<video preload='metadata' controls='controls'><source type='video/%s' src='%s' /></video>" ext path))
       ;; fall-through case for everything else
       (t
        path))))

  (org-link-set-parameters "video" :export 'org-video-link-export)

  (org-export-string-as "video:xxx.mp4" 'html t)

TODO Ensuring useful HTML Anchors

This is not working

  (define-minor-mode unpackaged/org-export-html-with-useful-ids-mode
    "Attempt to export Org as HTML with useful link IDs.
  Instead of random IDs like \"#orga1b2c3\", use heading titles,
  made unique when necessary."
    :global t
    (if unpackaged/org-export-html-with-useful-ids-mode
        (advice-add #'org-export-get-reference :override #'unpackaged/org-export-get-reference)
      (advice-remove #'org-export-get-reference #'unpackaged/org-export-get-reference)))

  (defun unpackaged/org-export-get-reference (datum info)
    "Like `org-export-get-reference', except uses heading titles instead of random numbers."
    (let ((cache (plist-get info :internal-references)))
      (or (car (rassq datum cache))
          (let* ((crossrefs (plist-get info :crossrefs))
                 (cells (org-export-search-cells datum))
                 ;; Preserve any pre-existing association between
                 ;; a search cell and a reference, i.e., when some
                 ;; previously published document referenced a location
                 ;; within current file (see
                 ;; `org-publish-resolve-external-link').
                 ;;
                 ;; However, there is no guarantee that search cells are
                 ;; unique, e.g., there might be duplicate custom ID or
                 ;; two headings with the same title in the file.
                 ;;
                 ;; As a consequence, before re-using any reference to
                 ;; an element or object, we check that it doesn't refer
                 ;; to a previous element or object.
                 (new (or (cl-some
                           (lambda (cell)
                             (let ((stored (cdr (assoc cell crossrefs))))
                               (when stored
                                 (let ((old (org-export-format-reference stored)))
                                   (and (not (assoc old cache)) stored)))))
                           cells)
                          (when (org-element-property :raw-value datum)
                            ;; Heading with a title
                            (unpackaged/org-export-new-title-reference datum cache))
                          ;; NOTE: This probably breaks some Org Export
                          ;; feature, but if it does what I need, fine.
                          (org-export-format-reference
                           (org-export-new-reference cache))))
                 (reference-string new))
            ;; Cache contains both data already associated to
            ;; a reference and in-use internal references, so as to make
            ;; unique references.
            (dolist (cell cells) (push (cons cell new) cache))
            ;; Retain a direct association between reference string and
            ;; DATUM since (1) not every object or element can be given
            ;; a search cell (2) it permits quick lookup.
            (push (cons reference-string datum) cache)
            (plist-put info :internal-references cache)
            reference-string))))

  (defun unpackaged/org-export-new-title-reference (datum cache)
    "Return new reference for DATUM that is unique in CACHE."
    (cl-macrolet ((inc-suffixf (place)
                               `(progn
                                  (string-match (rx bos
                                                    (minimal-match (group (1+ anything)))
                                                    (optional "--" (group (1+ digit)))
                                                    eos)
                                                ,place)
                                  ;; HACK: `s1' instead of a gensym.
                                  (-let* (((s1 suffix) (list (match-string 1 ,place)
                                                             (match-string 2 ,place)))
                                          (suffix (if suffix
                                                      (string-to-number suffix)
                                                    0)))
                                    (setf ,place (format "%s--%s" s1 (cl-incf suffix)))))))
      (let* ((title (org-element-property :raw-value datum))
             (ref (url-hexify-string (substring-no-properties title)))
             (parent (org-element-property :parent datum)))
        (while (--any (equal ref (car it))
                      cache)
          ;; Title not unique: make it so.
          (if parent
              ;; Append ancestor title.
              (setf title (concat (org-element-property :raw-value parent)
                                  "--" title)
                    ref (url-hexify-string (substring-no-properties title))
                    parent (org-element-property :parent parent))
            ;; No more ancestors: add and increment a number.
            (inc-suffixf ref)))
        ref)))

TODO Folded Drawers

Adapt this from https://github.com/alhassy/emacs.d to do something similar for source blocks.

  (defun my/org-drawer-format (name contents)
    "Export to HTML the drawers named with prefix fold_, ignoring case.

  The resulting drawer is a code-details and so appears folded;
  the user clicks it to see the information therein.
  Henceforth, these are called fold drawers.

  Drawers without such a prefix may be nonetheless exported if their
  body contains :export: t ---this switch does not appear in the output.
  Thus, we are biased to generally not exporting non-fold drawers.

  One may suspend export of fold drawers by having :export: nil
  in their body definition.

  Fold drawers naturally come with a title.
  Either it is specfied in the drawer body by :title: ⋯’,
  or otherwise the drawer's name is used with all underscores replaced
  by spaces.
  "
    (let* ((contents (replace-regexp-in-string ":export:.*\n?" "" contents))
           (fold? (s-prefix? "fold_" name 'ignore-case))
           (export? (string-match ":export:\s+t" contents))
           (not-export? (string-match ":export:\s+nil" contents))
           (title (and (string-match ":title:\\(.*\\)\n" contents)
                        (match-string 1 contents))))

      ;; Ensure we have a title.
      (unless title (setq title (s-join " " (cdr (s-split "_" name)))))

      ;; Output
      (cond
       ((and export? (not fold?)) contents)
       (not-export? nil)
       (fold?
        (thread-last contents
          (replace-regexp-in-string ":title:.*\n" "")
          (format "<details class=\"code-details\"> <summary> <strong>
              <font face=\"Courier\" size=\"3\" color=\"green\"> %s
              </font> </strong> </summary> %s </details>" title))))))

  (setq org-html-format-drawer-function 'my/org-drawer-format)

Org LaTeX

LaTeX Fragments

  (after! org
    ;; Highligh latex parts in org mode
    (setq org-highlight-latex-and-related '(latex script entities))

    ;; Use F9 to globally generate all the latex fragments
    (map! :map org-mode-map
          :n "<f9>"
          (lambda () (interactive) (org-preview-latex-fragment 16)))

    ;; Put all the preview images in some directory
    (setq org-preview-latex-image-directory "~/.ltximg/")

    ;; Define backends to preview LaTeX fragments
    (setq org-preview-latex-process-alist '((imagemagick
                                             :programs ("pdflatex" "convert")
                                             :description "pdf > png"
                                             :message "you need to install the programs: pdflatex and imagemagick."
                                             :image-input-type "pdf"
                                             :image-output-type "png"
                                             :image-size-adjust (1.0 . 1.0)
                                             :latex-compiler ("pdflatex -interaction nonstopmode -output-directory %o %f")
                                             :image-converter ("convert -density %D -trim -antialias %f -quality 100 %O"))
                                            (dvipng
                                             :programs ("latex" "dvipng")
                                             :description "dvi > png"
                                             :message "you need to install the programs: latex and dvipng."
                                             :image-input-type "dvi"
                                             :image-output-type "png"
                                             :image-size-adjust (0.4 . 0.4)
                                             :latex-compiler ("latex -interaction nonstopmode -output-directory %o %f")
                                             :image-converter ("dvipng -D %D -T tight -o %O %f"))
                                            (dvisvgm
                                             :programs ("pdflatex" "dvisvgm")
                                             :description "dvi > svg"
                                             :message "you need to install the programs: latex and dvisvgm."
                                             :image-input-type "dvi"
                                             :image-output-type "svg"
                                             :image-size-adjust (0.6 . 0.6)
                                             :latex-compiler ("latex -interaction nonstopmode -output-directory %o %f")
                                             :image-converter ("dvisvgm %f -n -b min -c %S -o %O"))
                                            ))

    ;; Use imagemagick/dvisvgm to generate png from pdf
    (setq org-preview-latex-default-process 'dvisvgm)

    ;; Don't change the font size for subscripts and superscripts in latex fragments.
    ;; This cause the orgmode tables not to be well aligned.
    (setq font-latex-fontify-script nil)

    ;; Colors of latex fragments
    (setq org-format-latex-options (plist-put org-format-latex-options :foreground 'default))
    (setq org-format-latex-options (plist-put org-format-latex-options :background 'default))
    )

LaTeX Classes

  (after! org
  ;; Custom classes to use when exporting to latex
  (add-to-list 'org-latex-classes
            '("beamer"
              ,(concat "\\documentclass[presentation]{beamer}\n"
                       "[DEFAULT-PACKAGES]"
                       "[PACKAGES]"
                       "[EXTRA]\n")
              ("\\section{%s}" . "\\section*{%s}")
              ("\\subsection{%s}" . "\\subsection*{%s}")
              ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))
  (add-to-list 'org-latex-classes
               '("clean-cheatsheet"
                 "\\documentclass{clean-cheatsheet}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )
  (add-to-list 'org-latex-classes
               '("clean-beamer"
                 "\\documentclass{clean-beamer}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )
  (add-to-list 'org-latex-classes
               '("cleanreport"
                 "\\documentclass{cleanreport}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )
  (add-to-list 'org-latex-classes
               '("scrartcl"
                 "\\documentclass{scrartcl}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )
  (add-to-list 'org-latex-classes
               '("scrreprt"
                 "\\documentclass{scrreprt}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )
  (add-to-list 'org-latex-classes
               '("biblioreport"
                 "\\documentclass{biblioreport}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )
  (add-to-list 'org-latex-classes
               '("moderncv"
                 "\\documentclass{moderncv}"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
               )

  )

Ox Latex Subfigure package

  ;; (use-package! ox-latex-subfigure
  ;;   :after org
  ;;   :config (require 'ox-latex-subfigure))
  (use-package! ox-latex-subfigure
    :load-path "~/.config/doom/packages/ox-latex-subfigure/"
    :config (require 'ox-latex-subfigure))

Clear page before heading

https://emacs.stackexchange.com/questions/30575/adding-latex-newpage-before-a-heading/30892

  (after! org
    (defun tdh-get-headline-string-element  (headline backend info)
      (let ((prop-point (next-property-change 0 headline)))
        (if prop-point (plist-get (text-properties-at prop-point headline) :parent))))

    (defun tdh-ensure-latex-clearpage (headline backend info)
      (when (org-export-derived-backend-p backend 'latex)
        (let ((elmnt (tdh-get-headline-string-element headline backend info)))
          (when (and elmnt (org-element-property :CLEARPAGE elmnt))
            (concat "\\clearpage\n" headline)))))

    (add-to-list 'org-export-filter-headline-functions
                 'tdh-ensure-latex-clearpage)
    )

Default added packages

  (after! org
    (add-to-list 'org-latex-packages-alist '("" "siunitx" t))
    (add-to-list 'org-latex-packages-alist '("" "array" t))
    (add-to-list 'org-latex-packages-alist '("" "tabularx" t))
    (add-to-list 'org-latex-packages-alist '("" "booktabs" t))
    (add-to-list 'org-latex-packages-alist '("" "bm" t))
    (add-to-list 'org-latex-packages-alist '("most" "tcolorbox" t))
    )

Some configurations

  (after! org
    ;; Setup default option for image size when exporting to LaTeX
    (setq org-latex-image-default-scale "")
    (setq org-latex-image-default-width "\\linewidth")
    (setq org-latex-image-default-height "")
    (setq org-latex-image-default-option "")

    ;; Use define labels instead of automatic generated ones
    (setq org-latex-prefer-user-labels t)

    ;; Captions above the table
    (setq org-latex-caption-above '(table))

    ;; Settings to export code with `minted' instead of `verbatim'.
    (setq org-latex-listings 'minted)

    ;; Set the following as images
    (setq org-latex-inline-image-rules '(("file" . "\\.\\(pdf\\|jpeg\\|jpg\\|png\\|ps\\|eps\\|tikz\\|pgf\\|svg\\|gif\\)\\'")))

    ;; Command used when exporting to pdf
    (setq org-latex-pdf-process
          '("latexmk -cd -pdflatex=\"pdflatex -synctex=1 -shell-escape -interaction nonstopmode -output-directory %o\" -pdf -bibtex -f %f"))
    )

Beamer

Bold Text

  (after! org
    (defun tdh-my-beamer-bold (contents backend info)
      (when (eq backend 'beamer)
        (replace-regexp-in-string
         (concat "\\`\\\\" "[A-Za-z0-9]+") ;; If not, orgmode is crazy...
         "\\\\textbf"
         contents)))

    (add-to-list 'org-export-filter-bold-functions 'tdh-my-beamer-bold)
    )

Special Environments

  • Make some comment those special environments
  (after! org
    (add-to-list 'org-beamer-environments-extra
                 '("cbox" ;; Name of environment
                   "m" ;; Selection key
                   "\\onslide%a{\\begin{cbox}[%h]%O"
                   "\\end{cbox}}\\vspace{0.5em}"))
    (add-to-list 'org-beamer-environments-extra
                 '("csubbox" ;; Name of environment
                   "M" ;; Selection key
                   "\\onslide%a{\\tcbsubtitle{%h}"
                   "}"))
    )

TODO Custom Export - Add Page and Label for LaTeX export

https://emacs.stackexchange.com/questions/156/emacs-function-to-convert-an-arbitrary-org-property-into-an-arbitrary-string-na?rq=1

  (defcustom tdh-org-property-mapping
    '((latex ("CUSTOM_PAGE" . tdh-insert-org-page-latex)
             ("CUSTOM_LABEL" . tdh-insert-org-label-latex)))
    "List of mappings from org property to arbitrary strings.
  Each element is a list:
    (BACKEND (PROPERTY1 . FUNCTION1) (PROPERTY2 . FUNCTION2) ...)
  FUNCTION are functions which get called with a single
  argument (the value of PROPERTY) and are responsible for doing
  whatever should be done."
    :type '(repeat (cons symbol (repeat (cons string string)))))
  (defun tdh-replace-org-property (backend)
    "Convert org properties using `tdh-org-property-mapping'.
  Lookup BACKEND in `tdh-org-property-mapping' for a list of
  (PROPERTY REPLACEMENT). For each healine being exported, if it has a
  PROPERTY listed insert a string immediately after the healine given by
      (format REPLACEMENT PROPERTY-VALUE)"
    (let ((map (cdr (assoc backend tdh-org-property-mapping)))
          value replacement)
      (when map
        (org-map-entries
         (lambda ()
           (dolist (it map)
             (save-excursion
               (when (setq value (org-entry-get (point) (car it)))
                 (funcall (cdr it) value)))))))))

  (add-hook 'org-export-before-processing-hook #'tdh-replace-org-property)
  (defun tdh-insert-org-label-latex (label)
    "Insert \"\\\\label{LABEL}\\n\" after the :PROPERTY: drawer."
    (search-forward-regexp org-property-end-re)
    (forward-char 1)
    (insert (format "\\label{%s}\n" label)))
  (defun tdh-insert-org-page-latex (page)
    "Insert \"\\\\page{PAGE}\\n\" after the :PROPERTY: drawer."
    (search-forward-regexp org-property-end-re)
    (forward-char 1)
    (insert (format "\\page{%s}\n" page)))

Number Equations

https://kitchingroup.cheme.cmu.edu/blog/2016/11/07/Better-equation-numbering-in-LaTeX-fragments-in-org-mode/

  (after! org
    (defun tdh-org-renumber-environment (orig-func &rest args)
      "A function to inject numbers in LaTeX fragment previews."
      (let ((results '())
            (counter -1)
            (numberp))

        (setq results (loop for (begin .  env) in
                            (org-element-map (org-element-parse-buffer) 'latex-environment
                              (lambda (env)
                                (cons
                                 (org-element-property :begin env)
                                 (org-element-property :value env))))
                            collect
                            (cond
                             ((and (string-match "\\\\begin{equation}" env)
                                   (not (string-match "\\\\tag{" env)))
                              (incf counter)
                              (cons begin counter))
                             ((string-match "\\\\begin{align}" env)
                              (prog2
                                  (incf counter)
                                  (cons begin counter)
                                (with-temp-buffer
                                  (insert env)
                                  (goto-char (point-min))
                                  ;; \\ is used for a new line. Each one leads to a number
                                  (incf counter (count-matches "\\\\$"))
                                  ;; unless there are nonumbers.
                                  (goto-char (point-min))
                                  (decf counter (count-matches "\\nonumber")))))
                             (t
                              (cons begin nil)))))

        (when (setq numberp (cdr (assoc (point) results)))
          (setf (car args)
                (concat
                 (format "\\setcounter{equation}{%s}\n" numberp)
                 (car args)))))

      (apply orig-func args))

    (advice-add 'org-create-formula-image :around #'tdh-org-renumber-environment)
    )

LaTeX macro both for LaTeX and HTML export

https://www.reddit.com/r/orgmode/comments/7u2n0h/tip_for_defining_latex_macros_for_use_in_both/

  (after! org
    (add-to-list 'org-src-lang-modes '("latex-macros" . latex))

    (defvar org-babel-default-header-args:latex-macros
      '((:results . "raw")
        (:exports . "results")))

    (defun prefix-all-lines (pre body)
      (with-temp-buffer
        (insert body)
        (string-insert-rectangle (point-min) (point-max) pre)
        (buffer-string)))

    (defun org-babel-execute:latex-macros (body _params)
      (concat
       "\n#+begin_export html\n<div style=\"display: none\"> \\(\n"
       body
       "\n\\)</div>\n#+end_export\n"))
    )

Org Projects

  (setq org-publish-project-alist
        '(("config"
           :base-directory "~/.config/literate-dotfiles/"
           :publishing-directory "~/.config/literate-dotfiles/docs/"
           :recursive nil
           :publishing-function org-html-publish-to-html
           :auto-sitemap nil
           :section-numbers nil
           :table-of-contents nil)
          ("stewart-simscape"
           :base-directory "~/Cloud/thesis/matlab/stewart-simscape/org/"
           :base-extension "org"
           :publishing-directory "~/Cloud/thesis/matlab/stewart-simscape/docs/"
           :author "Dehaeze Thomas"
           :email "dehaeze.thomas@gmail.com/"
           :recursive nil
           :publishing-function org-html-publish-to-html
           :auto-preamble t
           :auto-sitemap nil
           :html-link-up "index.html"
           :html-link-home "index.html"
           :with-todo-keywords nil
           :html-wrap-src-lines nil
           :table-of-contents nil)
          ("nass-simscape"
           :base-directory "~/Cloud/thesis/matlab/nass-simscape/org/"
           :base-extension "org"
           :publishing-directory "~/Cloud/thesis/matlab/nass-simscape/docs/"
           :author "Dehaeze Thomas"
           :email "dehaeze.thomas@gmail.com/"
           :recursive nil
           :publishing-function org-html-publish-to-html
           :auto-preamble t
           :auto-sitemap nil
           :html-link-up "index.html"
           :html-link-home "index.html"
           :with-todo-keywords nil
           :html-wrap-src-lines nil
           :table-of-contents nil)
          ("tikz-maker"
           :base-directory "~/Cloud/tikz/org/"
           :base-extension "org"
           :publishing-directory "~/Cloud/tikz/docs/"
           :author "Dehaeze Thomas"
           :email "dehaeze.thomas@gmail.com/"
           :recursive nil
           :publishing-function org-html-publish-to-html
           :auto-preamble t
           :auto-sitemap nil
           :html-link-up "index.html"
           :html-link-home "index.html"
           :with-todo-keywords nil
           :html-wrap-src-lines nil
           :table-of-contents nil)))

Automatically run startblock when opening org-mode files

  (after! org
    (defun tdh-eval-startblock ()
      (if (member "startblock" (org-babel-src-block-names))
          (save-excursion
            (org-babel-goto-named-src-block "startblock")
            (org-babel-execute-src-block))
        nil
        )
      )

    (add-hook 'org-mode-hook 'tdh-eval-startblock)
    )

TODO Insert ScreenShot or Picture from Phone

http://pragmaticemacs.com/emacs/a-workflow-to-quickly-add-photos-to-org-mode-notes/

  • One function to move file from ~/Picture/ folder (where the screenshots are taken) to current directory and then insert and org link to the picture. Maybe ask if it should be copied in a sub directory (figs folder for instance).
  • One function to copy file from ~/Cloud/Photos/ folder (where the pictures from phone are taken) to current directory (and ask for the new name of the picture) and insert org link.
  ;; required libraries
  (require 'dash)
  ;; (require 'swiper)
  (require 's)

  ;; start directory
  (defvar tdh-image-dir (expand-file-name "/home/thomas/Pictures"))

  (defun tdh-insert-conference-image ()
    "Insert image from conference directory, rename and add link in current file.

  The file is taken from a start directory set by `tdh-image-dir' and moved to the current directory, renamed and embedded at the point as an org-mode link. The user is presented with a list of files in the start directory, from which to select the file to move, sorted by most recent first."
    (interactive)
    (let (file-list target-dir file-list-sorted start-file start-file-full file-ext end-file end-file-base end-file-full file-number)
      ;; Clean directories from list but keep times
      (setq file-list
            (-remove (lambda (x) (nth 1 x))
                     (directory-files-and-attributes tdh-image-dir)))

      ;; Get target directory
      (setq target-dir (file-name-directory (buffer-file-name)))

      ;; Sort list by most recent
      (setq file-list-sorted
            (mapcar #'car
                    (sort file-list
                          #'(lambda (x y) (time-less-p (nth 6 y) (nth 6 x))))))

      ;; Use ivy to select start-file
      (setq start-file (ivy-read
                        (concat "Move selected file to " target-dir ":")
                        file-list-sorted
                        :re-builder #'ivy--regex
                        :sort nil
                        :initial-input nil))

      ;; add full path to start file and end-file
      (setq start-file-full
            (expand-file-name start-file tdh-image-dir))

      ;; final file name including path
      (setq end-file-full
            (expand-file-name start-file target-dir))

      ;; rename file
      (rename-file start-file-full end-file-full)
      (message "moved %s to %s" start-file-full start-file)

      ;; insert link
      (insert (org-make-link-string (format "file:%s" start-file)))

      ;; display image
      (org-display-inline-images t t)))

Render Tables

https://www.reddit.com/r/emacs/comments/d3a8or/pretty_org_tables_in_the_buffer_chapter_2_it/

  (after! org
    (defun tdh-render-org-table-at-point ()
      (interactive)
      (save-excursion
        (beginning-of-line)
        ;; removes the overlay is already there
        (if (overlays-at (point))
            (delete-overlay (car (overlays-at (point))))

          (let* ((element-type (org-element-type (org-element-at-point))))
            (if (and (not (eq element-type 'table))
                     (not (eq element-type 'table-row)))
                (error "not at an org table")

              (while (not (eq 'table (org-element-type (org-element-at-point))))
                (forward-line -1))
              (tdh-render-org-table (org-element-at-point))
              )))))


    (defun tdh-render-org-table (table)
      (interactive)
      (let* ((begin (org-element-property :begin table))
             (end (let ((pos (org-element-property :end table)))
                    (goto-char pos)
                    (beginning-of-line)
                    ;; skip possible space after table
                    (while (not (looking-at " *[|#]"))
                      (setq pos (point))
                      (forward-line -1))
                    pos))
             (tabletxt (buffer-substring-no-properties begin end))
             (img (with-temp-buffer
                    (insert tabletxt)
                    (mark-whole-buffer)
                    (org-latex-convert-region-to-latex)
                    (org-latex-preview)
                    (goto-char (point-min))
                    (overlay-get  (car (overlays-at (point))) 'display)))
             (overlay (make-overlay begin end)))
        (overlay-put overlay 'display img)
        (forward-line -1))
      )


    (defun tdh-render-org-tables-in-buffer ()
      (save-excursion
        (org-element-map (org-element-parse-buffer) 'table 'tdh-render-org-table)))

    ;; Use F9 to globally generate tables
    (map! :map org-mode-map :n "<f8>" (lambda () (interactive) (tdh-render-org-table-at-point)))
    )

Org Links

Youtube Links

  (setq yt-iframe-format
    ;; You may want to change your width and height.
    (concat "<iframe width=\"1280\""
            " height=\"720\""
            " src=\"https://www.youtube.com/embed/%s\""
            " frameborder=\"0\""
            " allowfullscreen>%s</iframe>"))

  (org-add-link-type
   "yt"
   (lambda (handle)
     (browse-url
      (concat "https://www.youtube.com/embed/"
              handle)))
   (lambda (path desc backend)
     (cl-case backend
       (html (format yt-iframe-format
                     path (or desc "")))
       (md (format "{{< youtube %s >}}"
                     path))
       (latex (format "\href{%s}{%s}"
                      path (or desc "video"))))))

Org Gcal

  (use-package! org-gcal
    :after org
    :init
    :config
    (setq org-gcal-client-id "396102378658-dcmbcmrnthbe925519otsjbd921otq0v.apps.googleusercontent.com"
          org-gcal-client-secret "4M5PWrbhQjwYEMXGK85lDYX9"
          org-gcal-file-alist '(("dehaeze.thomas@gmail.com" . "~/Cloud/org/gcal.org")
                                ("8kjmhe2ar0abnm054ill1fb0gc@group.calendar.google.com" . "~/Cloud/org/gcal_phd.org")))
    ;; Automatic fetch of the new events
    ;; (add-hook 'org-agenda-mode-hook (lambda () (org-gcal-fetch) ))
  )

Citeproc-Org

(use-package! citeproc-org
  :ensure t
  :after ox-hugo
  :config
  (citeproc-org-setup))

Org Wild Notifier

  (use-package! org-wild-notifier
    :after org
    :init
    (setq alert-default-style 'libnotify)
    :config
  )

TODO Orch

  (use-package! web-server
    :init
    :config
  )
  (add-to-list 'load-path "~/.config/doom/packages/orch/")
  (autoload 'orch-toggle "orch" nil t)

TODO [A] Custom Keybindings - , leader key and C-c

C-c a Org Agenda
C-c n Narrow to Subtree
C-c l Org Link
  (after! org
    (map! :map org-mode-map
     (:desc "Org Agenda"
       :ni "C-c a" 'org-agenda)
     (:desc "Archive"
       :n "C-c A" 'org-archive-subtree)
     (:desc "Org Capture"
       :ni "C-c c" 'org-capture)
     (:desc "Store Link"
       :ni "C-c l" 'org-store-link)
     (:desc "Narrow Subtree"
       :ni "C-c n" 'org-toggle-narrow-to-subtree)
     (:desc "Org Noter"
       :ni "C-c N" 'org-noter)
     (:desc "Align Block"
       :ni "C-c =" 'tdh-align-src-block)
     (:desc "Insert Reference"
       :ni "C-c r" 'org-ref-insert-ref-link)
     (:desc "Insert Image"
       :ni "C-c i" 'tdh-insert-image-org-link)
     (:desc "Insert Image SXIV"
       :ni "C-c I" 'tdh-insert-image-org-link-sxiv)
     (:desc "Link to next Figure"
       :ni "C-c f" 'tdh-insert-link-to-next-figure)
     (:desc "Link to previous Figure"
       :ni "C-c F" 'tdh-insert-link-to-previous-figure)
     (:desc "Insert Screenshot"
       :ni "C-c s" 'tdh-insert-screenshot-org-link)
     (:desc "Find Roam"
       :ni "C-c r" 'orb-find-non-ref-file)
     (:desc "Insert Roam"
       :ni "C-c R" 'orb-insert-non-ref)
     ))

Insert Elements ,i

Insert Link to paper

  (defun tdh-insert-paper-org-link (paper)
    "Insert an org link to some paper, choosing the file with completion"
    (interactive
     (list (read-file-name "Paper: " "~/Cloud/pdfs/" nil t)))
    (insert (format "[[papers:%s]]" (file-name-base paper))))

Insert Link to notes

  (defun tdh-insert-note-org-link (note)
    "Insert an org link to some note, choosing the file with completion"
    (interactive
     (list (read-file-name "Note: " "~/Cloud/pdfs/" nil t)))
    (insert (format "[[notes:%s]]" (file-name-base note))))

Insert Image that is in the figs folder

  (defun tdh-insert-image-org-link (img)
    "Insert an org image link, choosing the file with completion
  and starting from `my-default-image-directory'."
    (interactive
     (list (file-relative-name (read-file-name "Image: " (concat default-directory "figs/")) default-directory)))
    (insert (format "[[file:%s]]" img)))

Insert Image that is in the figs folder using SXIV

  (defun tdh-insert-image-org-link-sxiv ()
    "Insert an org image link, choosing the file with completion
  and starting from `my-default-image-directory'."
    (interactive)
    (setq img (shell-command-to-string "ls figs/*.{jpg,jpeg,bmp,png,gif} 2> /dev/null | sxiv -i -t -o | tail -1 | tr -d '\n'"))
    (unless (equal "" img)
      (insert (format "[[file:%s]]" img)))
    )

Copy picture from phone folder using SXIV and insert it

  (defun tdh-insert-phone-picture ()
    (interactive)
    (setq img (shell-command-to-string "~/.config/doom/bin/copy-phone-picture.sh"))
    (unless (equal "" img)
      (insert (format "[[file:%s]]" img)))
    )

Bash script for copying pictures taken by phone.

  if [ -z "$1" ]; then
      oldpath=$(ls -t ~/Cloud/photos/phone/*.jpg | sxiv -i -t -o | tail -1);
  else
      oldpath=$(ls -t $1 | sxiv -i -t -o | tail -1);
  fi

  if [ -n "$oldpath" ]; then
      newfilename=$(basename $oldpath .jpg | rofi -i -dmenu -p "Filename")
      if [ -n "$newfilename" ]; then
          cp $oldpath "figs/$newfilename.jpg"
          printf "figs/$newfilename.jpg"
      fi
  fi

Take Screenshot and insert a link:

  • Ask for a name screenshot_name
  • use maim -s figs/screenshot_name.png to take a screenshot with selection
  • Then insert the following to the buffer
  (defun tdh-insert-screenshot-org-link ()
    "Capture screenshot and insert the resulting file.
  The screenshot tool is determined by `org-download-screenshot-method'."
    (interactive)
    (if (string-match "_" (file-name-base buffer-file-name))
        (setq filename (read-string "Enter file name:" (car (split-string (file-name-base buffer-file-name) "_"))))
      (setq filename (read-string "Enter file name:")))
    (setq filepath (concat "./figs/" filename ".png"))
    (shell-command (concat "maim -s " filepath))
    (insert (format "#+name: fig:%s\n#+caption:\n[[file:%s]]" filename filepath))
    (search-backward "caption")
    (end-of-line)
    )

Insert link to next figure:

  (defun tdh-insert-link-to-next-figure ()
    (interactive)
    (save-excursion
      (re-search-forward "^#\\+name:\s*\\(fig:.*\\)" nil t 1))
    (insert (concat "[[" (match-string 1) "]]"))
    )

Insert link to previous figure:

  (defun tdh-insert-link-to-previous-figure ()
    (interactive)
    (save-excursion
      (re-search-backward "^#\\+name:\s*\\(fig:.*\\)" nil t 1))
    (insert (concat "[[" (match-string 1) "]]"))
    )

Map Keys

  (after! org
    (map! :map org-mode-map
          (:prefix (",i" . "Insert")
          :n "p" 'tdh-insert-paper-org-link
          :n "n" 'tdh-insert-note-org-link
          :n "f" 'tdh-insert-image-org-link
          :n "F" 'tdh-insert-image-org-link-sxiv
          :n "i" 'tdh-insert-phone-picture
          :n "l" 'tdh-insert-link-to-next-figure
          :n "L" 'tdh-insert-link-to-previous-figure
          :n "s" 'tdh-insert-screenshot-org-link)))

LaTeX ,l

  (defun tdh-latex-watch ()
    "Watch LaTeX file using latexmk"
    (interactive)
    (start-process-shell-command "latexmk-watch" "*latexmk-watch-output*"
                                 "latexmk" (format "-pdflatex=\"xelatex -synctex=1 -shell-escape -interaction nonstopmode  -output-directory='%s'\" -pdf -pvc -bibtex -f %s.tex"
                                                   (file-name-directory buffer-file-name)
                                                   (file-name-base buffer-file-name))))
  (defun tdh-latex-watch-kill ()
    "Kill the currently running TeX job."
    (interactive)
    (delete-process "latexmk-watch")
    )
  (after! org
    (map! :map org-mode-map
          (:prefix (",l" . "LaTeX")
          :n "w" 'tdh-latex-watch
          :n "k" 'tdh-latex-watch-kill
          :n "l" 'org-latex-export-to-latex)))

Org LaTeX Automatic fragment

  (defun tdh-automatic-latex-fragment-activate ()
    (interactive)
    (add-hook 'org-mode-hook 'org-fragtog-mode))

  (defun tdh-automatic-latex-fragment-deactivate ()
    (interactive)
    (remove-hook 'org-mode-hook 'org-fragtog-mode))
  (after! org
    (map! :map org-mode-map
          (:prefix (",l" . "LaTeX")
          :n "f" 'tdh-automatic-latex-fragment-activate
          :n "F" 'tdh-automatic-latex-fragment-deactivate)))

Bibtex ,r

  (after! org
    (map! :map org-mode-map
          (:prefix (",r" . "References")
          :n "b" 'helm-bibtex
          :n "B" 'helm-bibtex-with-local-bibliography
          :n "f" 'tdh-helm-bibtex-favorites
          :n "r" 'helm-resume)))

Open ranger in current directory ,o

  (defun tdh-open-ranger-in-workdir ()
    (interactive)
    (call-process-shell-command
    (concat "termite --directory=" default-directory " --exec=ranger") nil 0))
  (after! org
    (map! :map org-mode-map
          :n ",o" 'tdh-open-ranger-in-workdir))

View in External programs ,v

Open PDF output with zathura

  (defun tdh-open-org-pdf-externally ()
    (interactive)
    (call-process "zathura" nil 0 nil (concat (file-name-sans-extension (buffer-file-name)) ".pdf"))
    )

Open HTML output externally

  (defun tdh-open-org-html-externally ()
      (interactive)
      (call-process "xdg-open" nil 0 nil (concat (file-name-sans-extension (buffer-file-name)) ".html"))
      )
  (after! org
    (map! :map org-mode-map
          (:prefix (",v" . "View")
          :n "p" 'tdh-open-org-pdf-externally
          :n "h" 'tdh-open-org-html-externally)))

Org Babel

Main configuration

Don't ask for confirmation when evaluating following blocs

  (defun tdh-org-confirm-babel-evaluate (lang body)
    (not (member lang '("emacs-lisp" "latex" "matlab" "sh" "latex-macros" "python"))))

  (after! org
    (setq org-confirm-babel-evaluate 'tdh-org-confirm-babel-evaluate))

Default header arguments.

  (after! org
    (setq org-babel-default-header-args '((:eval . "no-export"))))

Use the current window for C-c ' source editing

  (after! org
    (setq org-src-window-setup 'current-window))

Appearance of source blocks

  (defun tdh-org-prettify-symbols ()
    (mapc (apply-partially 'add-to-list 'prettify-symbols-alist)
          (cl-reduce 'append
                     (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
                             `(("#+begin_src" . ?✎)
                               ("#+end_src"   . ?□)
                               ("#+begin_quote" . )
                               ("#+end_quote" . )))))
    (turn-on-prettify-symbols-mode))
  (add-hook 'org-mode-hook #'tdh-org-prettify-symbols)

Indentation

  (after! org
    (setq org-edit-src-content-indentation 2
          org-src-tab-acts-natively nil
          org-src-preserve-indentation nil)
    )

Library of Babel

Add all named source blocks to org-babel-library-of-babel (link).

  (after! org
    (org-babel-lob-ingest "~/.config/literate-dotfiles/emacs-library-babel.org"))

Org-Babel Matlab

  (after! org
    (setq org-babel-matlab-shell-command "/home/thomas/.local/bin/matlab -softwareopengl -nodesktop -nosplash")
    )

Default options for Matlab code

  (after! org
    (setq org-babel-default-header-args:matlab
          '((:results . "none")
            (:session . "*MATLAB*")
            (:comments . "org")
            (:exports . "both")
            (:cache .   "no")
            (:noweb . "no")
            (:hlines . "no")
            (:tangle . "yes")
            (:mkdir . "yes")
            (:eval . "no-export")))
    )

Better format the output results for Matlab (link).

  (after! org
    (defun org-babel-octave-evaluate-session
        (session body result-type &optional matlabp)
      "Evaluate BODY in SESSION."
      (let* ((tmp-file (org-babel-temp-file (if matlabp "matlab-" "octave-")))
             (wait-file (org-babel-temp-file "matlab-emacs-link-wait-signal-"))
             (full-body
              (pcase result-type
                (`output
                 (mapconcat
                  #'org-babel-chomp
                  (list (if matlabp
                            (multi-replace-regexp-in-string
                             '(("%.*$"                      . "")    ;Remove comments
                               (";\\s-*\n+"                 . "; ")  ;Concatenate lines
                               ("\\(\\.\\)\\{3\\}\\s-*\n+"  . " ")   ;Handle continuations
                               (",*\\s-*\n+"                . ", ")) ;Concatenate lines
                             body)
                          body)
                        org-babel-octave-eoe-indicator) "\n"))
                (`value
                 (if (and matlabp org-babel-matlab-with-emacs-link)
                     (concat
                      (format org-babel-matlab-emacs-link-wrapper-method
                              body
                              (org-babel-process-file-name tmp-file 'noquote)
                              (org-babel-process-file-name tmp-file 'noquote) wait-file) "\n")
                   (mapconcat
                    #'org-babel-chomp
                    (list (format org-babel-octave-wrapper-method
                                  body
                                  (org-babel-process-file-name tmp-file 'noquote)
                                  (org-babel-process-file-name tmp-file 'noquote))
                          org-babel-octave-eoe-indicator) "\n")))))
             (raw (if (and matlabp org-babel-matlab-with-emacs-link)
                      (save-window-excursion
                        (with-temp-buffer
                          (insert full-body)
                          (write-region "" 'ignored wait-file nil nil nil 'excl)
                          (matlab-shell-run-region (point-min) (point-max))
                          (message "Waiting for Matlab Emacs Link")
                          (while (file-exists-p wait-file) (sit-for 0.01))
                          "")) ;; matlab-shell-run-region doesn't seem to
                    ;; make *matlab* buffer contents easily
                    ;; available, so :results output currently
                    ;; won't work
                    (org-babel-comint-with-output
                        (session
                         (if matlabp
                             org-babel-octave-eoe-indicator
                           org-babel-octave-eoe-output)
                         t full-body)
                      (insert full-body) (comint-send-input nil t)))) results)
        (pcase result-type
          (`value
           (org-babel-octave-import-elisp-from-file tmp-file))
          (`output
           (setq results
                 (if matlabp
                     (cdr (reverse (delete "" (mapcar #'org-strip-quotes
                                                      (mapcar #'org-trim (remove-car-upto-newline raw))))))
                   (cdr (member org-babel-octave-eoe-output
                                (reverse (mapcar #'org-strip-quotes
                                                 (mapcar #'org-trim raw)))))))
           (mapconcat #'identity (reverse results) "\n")))))

    (defun remove-car-upto-newline (raw)
      "Truncate each string in a list of strings up to the first newline"
      (cons (mapconcat #'identity
                       (cdr (split-string-and-unquote (car raw) "\n"))
                       "\n") (cdr raw)))

    (defun multi-replace-regexp-in-string (replacements-list string &optional rest)
      (interactive)
      "Replace multiple regexps in a string. Order matters."
      (if (null replacements-list)
          string
        (let ((regex (caar replacements-list))
              (replacement (cdar replacements-list)))
          (multi-replace-regexp-in-string (cdr replacements-list)
                                          (replace-regexp-in-string regex replacement
                                                                    string rest)))))
    )

Some functions for using Matlab with Org Babel ,m

whos matlab function

  (defun tdh-matlab-whos (&optional start end)
  "Get what is in the Matlab workspace"
    (interactive)
    (if (use-region-p)
      (let ((regionp (buffer-substring (region-beginning) (region-end))))
        (process-send-string "*MATLAB*" (concat "whosEmacs " regionp "\n")))
      (process-send-string "*MATLAB*" (concat "whosEmacs" "\n"))))

help matlab function

  (defun tdh-matlab-help (&optional start end)
    "Get help on the selected function"
    (interactive)
    (if (use-region-p)
      (let ((regionp (buffer-substring (region-beginning) (region-end))))
          (process-send-string "*MATLAB*" (concat "help " regionp "\n")))
      (process-send-string "*MATLAB*" (concat "help " (read-string "Matlab help:") "\n")))
    )

Specify a Matlab command to run

(defun tdh-matlab-run-command ()
  "Prompt user to enter a matlab command"
  (interactive)
  (process-send-string "*MATLAB*" (concat (read-string "Matlab Command: ") "\n")))

Specify a Matlab command to run and show output in mini-buffer

  (defun tdh-matlab-run-command-show-output ()
    "Prompt user to enter a matlab command"
    (interactive)
    (process-send-string "*MATLAB*" (concat "evalEmacs('" (read-string "Matlab Command: ") "')\n")))

Org-Babel Tangle File and Execute with Matlab

(defun tdh-matlab-tangle-and-execute ()
  "Jump to tangle file for the source block at point."
  (interactive)
  (let (file org-babel-pre-tangle-hook org-babel-post-tangle-hook)
    (cl-letf (((symbol-function 'write-region) (lambda (start end filename &rest _ignore)
                         (setq file filename)))
          ((symbol-function 'delete-file) #'ignore))
      (org-babel-tangle '(4)))
    (when file
      (setq file (expand-file-name file))
      (if (file-readable-p file)
      (process-send-string "*MATLAB*" (concat "run " file "\n"))
    (error "Cannot open tangle file %S" file)))))

Map Functions

  (after! org
    (map! :map org-mode-map
          (:prefix (",m" . "Matlab")
          :n  "e" 'tdh-matlab-run-command
          :n  "E" 'tdh-matlab-run-command-show-output
          :n  "T" 'tdh-matlab-tangle-and-execute
          :nv "h" 'tdh-matlab-help
          :nv "w" 'tdh-matlab-whos)))

Remap ctrl-ret used to execute the src block and go to the next one

https://emacs.stackexchange.com/questions/13869/how-to-toggle-org-mode-source-code-block-eval-no-status

Remap ctrl-ret to execute the source block and go to the next source block when inside a source block. Otherwise, keep the normal behavior for ctrl-ret.

  (defun tdh-ctrl-ret ()
    (interactive)
    (defun tdh-in-src-block-p ()
      "Returns t when the point is inside a source code block"
      (string= "src" (org-in-block-p '("src"))))

    (if (tdh-in-src-block-p)
        (progn
          (org-babel-execute-src-block)
          (org-babel-next-src-block))
      (+org--insert-item 'below)))
  (map! :after evil-org
        :map evil-org-mode-map
        :n "<C-return>" #'tdh-ctrl-ret)

Remap ctrl-shift-ret used to execute the (matlab) src block in the background and go to the next one

tdh-org-babel-execute-matlab-background

  (defun tdh-org-babel-execute-matlab-background (&optional arg info params)
    (interactive)
    (let* ((org-babel-current-src-block-location
        (or org-babel-current-src-block-location
            (nth 5 info)
            (org-babel-where-is-src-block-head)))
       (info (if info (copy-tree info) (org-babel-get-src-block-info))))
      ;; Merge PARAMS with INFO before considering source block
      ;; evaluation since both could disagree.
      (cl-callf org-babel-merge-params (nth 2 info) params)
      (when (org-babel-check-evaluate info)
        (cl-callf org-babel-process-params (nth 2 info))
        (let* ((params (nth 2 info))
           (cache (let ((c (cdr (assq :cache params))))
                (and (not arg) c (string= "yes" c))))
           (new-hash (and cache (org-babel-sha1-hash info :eval)))
           (old-hash (and cache (org-babel-current-result-hash)))
           (current-cache (and new-hash (equal new-hash old-hash))))
      (cond
       (current-cache
        (save-excursion		;Return cached result.
          (goto-char (org-babel-where-is-src-block-result nil info))
          (forward-line)
          (skip-chars-forward " \t")
          (let ((result (org-babel-read-result)))
            (message (replace-regexp-in-string "%" "%%" (format "%S" result)))
            result)))
       ((org-babel-confirm-evaluate info)
        (let* ((lang (nth 0 info))
           (result-params (cdr (assq :result-params params)))
           ;; Expand noweb references in BODY and remove any
           ;; coderef.
           (body
            (let ((coderef (nth 6 info))
              (expand
               (if (org-babel-noweb-p params :eval)
                   (org-babel-expand-noweb-references info)
                 (nth 1 info))))
              (if (not coderef) expand
                (replace-regexp-in-string
                 (org-src-coderef-regexp coderef) "" expand nil nil 1))))
           (dir (cdr (assq :dir params)))
           (mkdirp (cdr (assq :mkdirp params)))
           (default-directory
             (cond
              ((not dir) default-directory)
              ((member mkdirp '("no" "nil" nil))
               (file-name-as-directory (expand-file-name dir)))
              (t
               (let ((d (file-name-as-directory (expand-file-name dir))))
                 (make-directory d 'parents)
                 d))))
           (cmd (intern (concat "org-babel-execute:" lang)))
           result)
          (process-send-string "*MATLAB*" (concat body "\n"))
          result))
       )))
      )
    )

tdh-matlab-execute-selected

  (defun tdh-matlab-execute-selected (start end)
  "Execute selected text in the *MATLAB* buffer"
    (interactive "r")
    (let ((regionp (buffer-substring start end)))
        (process-send-string "*MATLAB*" (concat regionp "\n"))))

Remap ctrl-shift-ref

This function:

  • first check if inside a source block, if not does nothing
  • when check if the language is matlab

    • if it is not, it just runs the code and go to the next source block
    • if it is in a matlab block, it first check if a region if selected, if so it just runs the selected region. if no region is selected, it runs all the code blocks and goes to the next block
  (defun tdh-ctrl-shift-ret ()
    (interactive)
    (defun tdh-in-src-block-p ()
      "Returns t when the point is inside a source code block"
      (string= "src" (org-in-block-p '("src"))))

    (if (tdh-in-src-block-p)
        (let ((lang (nth 0 (org-babel-get-src-block-info))))
          (if (string= lang "matlab")
              (if (region-active-p)
                  (tdh-matlab-execute-selected (region-beginning) (region-end))
                (progn (tdh-org-babel-execute-matlab-background)
                       (org-babel-next-src-block)))
            (tdh-ctrl-ret))
          )
      )
    )
  (map! :after evil-org
        :map evil-org-mode-map
        :n "<C-S-return>" #'tdh-ctrl-shift-ret)

Align Source Blocks

  (defun tdh-align-src-block ()
    (interactive)
    (defun tdh-in-src-block-p ()
      "Returns t when the point is inside a source code block"
      (string= "src" (org-in-block-p '("src"))))

    (if (tdh-in-src-block-p)
        (progn
          (org-edit-special)
          (evil-indent (point-min) (point-max))
          (org-edit-src-exit))
      (org-table-eval-formula)))

Helping Functions - Tangling ,b

Org-Babel Tangle Sub-tree

  (defun tdh-org-babel-tangle-subtree ()
    "Tangle the current subtree"
    (interactive)
    (progn
      (org-narrow-to-subtree)
      (org-babel-tangle)
      (widen))
    )

Org-Tangle and Org-Babel Jump to Tangle File

  (defun tdh-org-babel-jump-to-tangle-file ()
    "Jump to tangle file for the source block at point."
    (interactive)
    (let (file org-babel-pre-tangle-hook org-babel-post-tangle-hook)
      (cl-letf (((symbol-function 'write-region) (lambda (start end filename &rest _ignore)
                                                   (setq file filename)))
                ((symbol-function 'delete-file) #'ignore))
        (org-babel-tangle '(4)))
      (when file
        (setq file (expand-file-name file))
        (if (file-readable-p file)
            (find-file file)
          (error "Cannot open tangle file %S" file)))))

Map Functions

  (after! org
    (map! :map org-mode-map
          (:prefix (",b" . "Tangle")
          :n  "F" 'tdh-org-babel-jump-to-tangle-file
          :n  "T" 'tdh-org-babel-tangle-subtree)))

Bibliography Management

My bibliography management is mainly based on the following packages:

  • org-ref for nice citations
  • org-noter to annotate documents
  • org-roam to manage and links all my notes
  • helm-bibtex as an interface to easily find references
  • org-roam-bibtex that connects all the above packages

Org Ref (link)

Nice Functions:

  • org-ref-insert-ref-link
  • org-ref-helm-insert-cite-link
  • org-ref-list-of-figures
  • org-ref-find-bad-citations
  • org-ref-clean-bibtex-entry
  (use-package! org-ref
    :after org
    :init
    :config
    ;; Folder where the notes files are located (or file if just one Note file)
    (setq org-ref-notes-directory "~/Cloud/brain")
    (setq org-ref-bibliography-notes "~/Cloud/brain")

    ;; Bibliography File
    (setq reftex-default-bibliography  '("~/Cloud/brain/biblio/references.bib"))
    (setq org-ref-default-bibliography '("~/Cloud/brain/biblio/references.bib"))

    ;; Folder where all the pdf are located
    (setq org-ref-pdf-directory "~/Cloud/pdfs")

    (setq org-ref-bibliography-entry-format
          '(("article" . "%a, %t, %j, v(%n), %p (%y).")
            ("book" . "%a, %t, %u (%y).")
            ("techreport" . "%a, %t, %i, %u (%y).")
            ("phdthesis" . "%a, %t (%y).")
            ("proceedings" . "%e, %t in %S, %u (%y).")
            ("inproceedings" . "%a, %t, %p, in %b, edited by %e, %u (%y)")))

    ;; Tell org-ref to let helm-bibtex find notes for it
    (setq org-ref-notes-function
          (lambda (thekey)
            (let ((bibtex-completion-bibliography (org-ref-find-bibliography)))
              (bibtex-completion-edit-notes
               (list (car (org-ref-get-bibtex-key-and-file thekey)))))))

    ;; Problem with speed: don't display broken links
    (setq org-ref-show-broken-links t)
    ;; Display information on the citation
    (setq org-ref-show-citation-on-enter t)

    (add-to-list 'org-ref-helm-user-candidates
                 '("Open pdf in Zathura" . (lambda () (call-process "zathura" nil 0 nil  (concat
                                                                                          (file-name-as-directory org-ref-pdf-directory)
                                                                                          (car (org-ref-get-bibtex-key-and-file))
                                                                                          ".pdf"))))
                 t)

    (add-to-list 'org-ref-helm-user-candidates
                 '("Drag and Drop" . (lambda () (call-process "/bin/bash" nil 0 nil "-c" (concat
                                                                                          "dragon-drag-and-drop "
                                                                                          (file-name-as-directory org-ref-pdf-directory)
                                                                                          (car (org-ref-get-bibtex-key-and-file))
                                                                                          ".pdf"))))
                 t)

    ;; Let Mathjax deals with equation reference
    (defun org-ref-eqref-export (keyword desc format)
      (cond
       ((eq format 'latex) (format "\\eqref{%s}" keyword))
       ((eq format 'html) (format "\\eqref{%s}" keyword))
       ((eq format 'md) (format "\\eqref{%s}" keyword))))
    )
  (defun tdh-org-ref-open-pdf-at-point ()
    "Open the pdf in external program for bibtex key under point if it exists."
    (interactive)
    (let* ((results (org-ref-get-bibtex-key-and-file))
           (key (car results))
           (pdf-file (funcall org-ref-get-pdf-filename-function key)))
      (if (file-exists-p pdf-file)
          (call-process "zathura" nil 0 nil pdf-file)
        (message "no pdf found for %s" key))))

Org Noter (link)

  (use-package! org-noter
    :defer t
    :after (:any org pdf-view)
    :config
    (setq org-noter-always-create-frame nil)

    (setq org-noter-kill-frame-at-session-end nil)

    ;; Fraction of the frame that the document window will occupy when split
    (setq org-noter-doc-split-fraction '(0.6 . 0.6))

    ;; Save the last visited location automatically; when starting a new session, go to that location
    (setq org-noter-auto-save-last-location nil)

    ;; Add an empty line between each note's heading and content
    (setq org-noter-separate-notes-from-heading t)

    ;; List of paths to check (non recursively) when searching for a notes file
    (setq org-noter-notes-search-path "~/Cloud/brain")

    (defun org-noter-init-pdf-view ()
      (pdf-view-fit-page-to-window)
      (pdf-view-auto-slice-minor-mode)
      (run-at-time "0.5 sec" nil #'org-noter))
    (add-hook 'pdf-view-mode-hook 'org-noter-init-pdf-view)

    (map!
     :map pdf-view-mode-map
     (:desc "Insert Note"
       :n "i" #'org-noter-insert-note))
    )

Org Roam (link)

  (use-package! org-roam
    :custom-face
    (org-roam-link ((t (:inherit org-link :foreground "#cc241d"))))
    :config
    (setq org-roam-directory "~/Cloud/brain/")
    (setq org-roam-completion-system 'helm)
    (setq org-roam-tag-sources '(prop last-directory))
    (setq org-roam-capture-templates
          `(("d" "default" plain (function org-roam--capture-get-point)
             "%?"
             :file-name "${slug}"
             :head ,(concat "#+TITLE: ${title}\n"
                            "#+SETUPFILE: ./setup/org-setup-file.org\n"
                            "#+HUGO_SECTION: zettels\n"
                            "\n"
                            "- Tags ::\n"
                            "\n"
                            "* Bibliography                                                        :ignore:\n"
                            "bibliography:./biblio/references.bib"
                            )
             :unnarrowed t)))
    (setq org-roam-capture-ref-templates
          `(("r" "ref" plain (function org-roam--capture-get-point)
             "%?"
             :file-name "${slug}"
             :head ,(concat "#+TITLE: ${title}\n"
                            "#+SETUPFILE: ./setup/org-setup-file.org\n"
                            "#+HUGO_SECTION: websites\n"
                            "#+ROAM_KEY: ${ref}\n"
                            "\n"
                            "- Tags ::\n"
                            )
             :unnarrowed t)))
    )

Automatic export of backlinks

  (after! (org org-roam)
    (defun tdh-org-roam--backlinks-list (file)
      (when (org-roam--org-roam-file-p file)
        (mapcar #'car (org-roam-db-query [:select :distinct [from]
                                          :from links
                                          :where (= to $s1)
                                          :and from :not :like $s2] file "%private%"))))
    (defun tdh-org-export-preprocessor (_backend)
      (when-let ((links (tdh-org-roam--backlinks-list (buffer-file-name))))
        (insert "\nBacklinks:\n")
        (dolist (link links)
          (insert (format "- [[file:%s][%s]]\n"
                          (file-relative-name link org-roam-directory)
                          (org-roam--get-title-or-slug link))))))
    (add-hook 'org-export-before-processing-hook #'tdh-org-export-preprocessor)


    (defun tdh-org-roam-export-all ()
      "Re-exports all Org-roam files to Hugo markdown."
      (interactive)
      (dolist (f (org-roam--list-all-files))
        (with-current-buffer (find-file f)
          (when (s-contains? "SETUPFILE" (buffer-string))
            (org-hugo-export-wim-to-md)))))
    )

Helm-Bibtex (link)

  (use-package! helm-bibtex
    :after-call helm-bibtex
    :init
    :config
    ;; Bibliography file
    (setq bibtex-completion-bibliography "~/Cloud/brain/biblio/references.bib")

    ;; Directory with all the pdfs
    (setq bibtex-completion-library-path "~/Cloud/pdfs/")

    ;; Directory with notes files
    (setq bibtex-completion-notes-path "~/Cloud/brain/")

    (setq bibtex-completion-notes-extension ".org")

    (setq bibtex-completion-pdf-extension '(".pdf" ".djvu"))

    (setq bibtex-completion-additional-search-fields '(keywords))

    ;; Use "keywords" field when looking for bib entries
    (setq helm-bibtex-additional-search-fields '(keywords))

    (setq helm-bibtex-full-frame nil)

    ;; Display of bibtex entries with helm
    (setq bibtex-completion-display-formats
          '((t . "${author:36} ${title:*} ${year:4} ${=type=:7} ${=has-note=:1}")))

    ;; Special symbols for notes and pdf
    (setq bibtex-completion-pdf-symbol "⌘")
    (setq bibtex-completion-notes-symbol "✎")

    ;; Template used when creating new Note file
    (setq bibtex-completion-notes-template-multiple-files (concat "#+TITLE: ${title}\n"
                                                                  "#+SETUPFILE: ./setup/org-setup-file.org\n"
                                                                  "#+HUGO_SECTION: ${=type=}\n"
                                                                  "#+ROAM_KEY: ${=key=}\n"
                                                                  "\n"
                                                                  "- Tags ::\n"
                                                                  "- Reference :: cite:${=key=}\n"
                                                                  "- Author(s) :: ${author}\n"
                                                                  "- Year :: ${year}\n"
                                                                  "\n"
                                                                  "* ${author-abbrev} (${year}): ${title} :${=type=}:ignore:\n"
                                                                  ":PROPERTIES:\n"
                                                                  ":NOTER_DOCUMENT: ../pdfs/${=key=}.pdf\n"
                                                                  ":END:\n"
                                                                  "\n"
                                                                  "* Bibliography                                                        :ignore:\n"
                                                                  "bibliography:./biblio/references.bib"
                                                                  ))

    ;; Make "Edit notes" the default action
    (helm-delete-action-from-source "Edit notes" helm-source-bibtex)
    (helm-add-action-to-source "Edit notes" 'helm-bibtex-edit-notes helm-source-bibtex 0)

    (helm-delete-action-from-source "Open PDF Externally" helm-source-bibtex)
    (helm-add-action-to-source "Open PDF Externally" 'tdh-open-pdf-externally helm-source-bibtex 1)

    (helm-add-action-to-source "Insert Link to Note" 'tdh-insert-link-to-note helm-source-bibtex 2)

    (helm-add-action-to-source "Insert E-Reader Link" 'tdh-insert-link-to-pdf-entry helm-source-bibtex 3)
    )
  (defun tdh-insert-link-to-pdf-entry (key)
    "Insert a link to a pdf associated with the bibtex entry."
    (let*
        ((entry (bibtex-completion-get-entry key))
         (title (bibtex-completion-get-value "title" entry)))
      (insert (concat "[[file:Download/" key ".pdf][" title "]] (cite:" key ")"))
      )
    )
  (defun tdh-insert-link-to-note (key)
    "Insert a link to a note associated with the bibtex entry."
    (if (and bibtex-completion-notes-path
             (f-directory? bibtex-completion-notes-path))
        (let* ((path (f-join bibtex-completion-notes-path
                             (s-concat key bibtex-completion-notes-extension))))
          (if (file-exists-p path)
              (insert (concat "[[file:" (file-relative-name path) "][Notes]]"))
            (message "No note file associated"))
          )))

Open pdf externally

  (defun tdh-open-pdf-externally (key)
    (call-process "zathura" nil 0 nil (nth 0 (-cons-to-list (bibtex-completion-find-pdf key)))))

Special Commands

  (defun tdh-helm-bibtex-favorites (&optional arg)
    "Search Favorite BibTeX entries"
    (interactive "P")
    (helm-bibtex arg nil "favorite "))

List all element of the bibliography without pdf associated

  (defun tdh-list-bib-without-pdf-associated ()
    (interactive)
    (bibtex-completion-init)
    (setq candidates (bibtex-completion-candidates))

    (defun canditate-is-pdf-present (candidate)
      (bibtex-completion-find-pdf-in-library (cdr (assoc "=key=" candidate)))
      )

    (setq candidates-without-pdf (remove-if #'canditate-is-pdf-present candidates))

    (setq candidate-without-pdf-names (mapcar
                                       (lambda (x) (cdr (assoc "title" x)))
                                       candidates-without-pdf))

    (with-output-to-temp-buffer "*bib-without-pdf*" (princ (string-join candidate-without-pdf-names "\n")))
    (switch-to-buffer-other-window "*bib-without-pdf*")
    )

Deft

  (use-package! deft
    :custom
    (deft-directory "~/Cloud/brain/"))

Org-Roam-Bibtex (link)

Provides nice functions such as:

  • orb-find-non-ref-file
  • orb-insert-non-ref
  • orb-note-action
  (use-package! org-roam-bibtex
    :hook (org-roam-mode . org-roam-bibtex-mode)
    :config
    (setq orb-preformat-keywords `("=key=" "title" "author" "year" "author-abbrev" "=type="))

    (setq orb-templates
          `(("r" "ref" plain (function org-roam-capture--get-point) ""
             :file-name "${=key=}"
             :head ,(concat "#+TITLE: ${title}\n"
                            "#+SETUPFILE: ./setup/org-setup-file.org\n"
                            "#+HUGO_SECTION: ${=type=}\n"
                            "#+ROAM_KEY: ${ref}\n"
                            "\n"
                            "- Tags ::\n"
                            "- Reference :: ${ref}\n"
                            "- Author(s) :: ${author}\n"
                            "- Year :: ${year}\n"
                            "\n"
                            "* ${author-abbrev} (${year}): ${title} :${=type=}:ignore:\n"
                            ":PROPERTIES:\n"
                            ":NOTER_DOCUMENT: ../pdfs/${=key=}.pdf\n"
                            ":END:\n"
                            "\n"
                            "* Bibliography                                                        :ignore:\n"
                            "bibliography:./biblio/references.bib"
                            )
             :unnarrowed t)))

    (setq orb-note-actions-user '(("Open with Zathura" . tdh-open-bib-with-zathura)))
    )
  (defun tdh-open-bib-with-zathura (key)
    "Open the pdf corresponding to the reference KEY with Zathura"
    (if (listp key)
        (setq key (car key)))
    (call-process "zathura" nil 0 nil (org-ref-get-pdf-filename key)))

Citeproc-org (link)

  (use-package! citeproc-org
    :after org
    :config
    (citeproc-org-setup))

LaTeX

Matlab

Setup Matlab Mode

  (setq matlab-shell-command "/home/thomas/.local/bin/matlab")
  (setq matlab-shell-command-switches (list "-softwareopengl -nodesktop -nosplash"))
  (setq matlab-indent-function t)
  (setq mlint-programs '("mlint" "/home/thomas/.local/bin/mlint"))

Setup Flycheck to work with mlint

  (defvar mlint-executable "/home/thomas/.local/bin/mlint")

  (flycheck-define-command-checker 'matlab-mlint
    "A Matlab checker based on mlint."
    :command `(,mlint-executable source)
    :error-patterns
    '((warning line-start "L " line " (C " (1+ digit)  "): " (message) line-end))
    :modes '(matlab-mode))

  (add-to-list 'flycheck-checkers 'matlab-mlint)

  ;; Automatic startup of flycheck for matlab
  (add-hook 'matlab-mode-hook 'flycheck-mode)

Completion in the Matlab Shell

  (map! :map matlab-shell-mode-map
        :i "<tab>" 'matlab-shell-tab)

Beautify code

  (defun tdh-matlab-beautify-buffer ()
    "Beautify Current Matlab Buffer"
    (interactive)
    ;; First verifies is the current file is a Matlab file
    (if (string= (file-name-extension (buffer-file-name)) "m")
        (progn
          (save-buffer)
          (matlab-shell-run-command (concat "MBeautify.formatFileNoEditor(\"" (buffer-file-name) "\", \"" (buffer-file-name) "\")"))
          (revert-buffer :ignore-auto :noconfirm))
      (message "Current buffer is not a matlab file")
      )
    )

Key Bindings

  (defun tdh-matlab-add-breakpoint ()
    (interactive)
    (matlab-shell-run-command (concat "dbstop in " (buffer-name) " at " (number-to-string (line-number-at-pos nil)))))

  (defun tdh-matlab-remove-breakpoint ()
    (interactive)
    (matlab-shell-run-command (concat "dbclear in " (buffer-name) " at " (number-to-string (line-number-at-pos nil)))))

  (defun tdh-matlab-list-breakpoints ()
    (interactive)
    (matlab-shell-run-command (concat "dbstatus " (buffer-name))))

  (defun tdh-matlab-clear-breakpoints ()
    (interactive)
    (matlab-shell-run-command (concat "dbclear in " (buffer-name))))

  (defun tdh-matlab-no-debug-on-error ()
    (interactive)
    (matlab-shell-run-command (concat "dbclear if error")))

  (defun tdh-matlab-debug-on-error ()
    (interactive)
    (matlab-shell-run-command (concat "dbstop if error")))

  (defun tdh-matlab-go-to-file-directory ()
    (interactive)
    (matlab-shell-run-command (concat "cd " (file-name-directory buffer-file-name))))
  (map! :map matlab-mode-map
        (:prefix ("," . "prefix")
         :n "g" 'tdh-matlab-go-to-file-directory
         (:prefix ("d" . "Debug")
          :n "de" 'tdh-matlab-debug-on-error
          :n "dE" 'tdh-matlab-no-debug-on-error
          :n "da" 'tdh-matlab-add-breakpoint
          :n "dr" 'tdh-matlab-remove-breakpoint
          :n "dL" 'tdh-matlab-list-breakpoints
          :n "dc" 'tdh-matlab-clear-breakpoints
          :n "dl" 'gud-cont
          :n "ds" 'gud-step
          :n "dn" 'gud-next
          :n "dq" 'gud-finish)))

Mu4e

Cheatsheet

Command Usage
C-j Next mail
C-k Previous mail
R/C/F Reply/Compose/Forward
t Move to Archive
d Move to Trash

Helping function

This is a helper to help determine which account context I am in based on the folder in my maildir the email (eg. ~/.mail/nine27) is located in.

  (defun mu4e-message-maildir-matches (msg rx)
    (when rx
      (if (listp rx)
          ;; If rx is a list, try each one for a match
          (or (mu4e-message-maildir-matches msg (car rx))
              (mu4e-message-maildir-matches msg (cdr rx)))
        ;; Not a list, check rx
        (string-match rx (mu4e-message-field msg :maildir)))))

Choose account label to feed msmtp -a option based on From header in Message buffer; This function must be added to message-send-mail-hook for on-the-fly change of From address before sending message since message-send-mail-hook is processed right before sending message.

  (defun choose-msmtp-account ()
    (if (message-mail-p)
        (save-excursion
          (let*
              ((from (save-restriction
                       (message-narrow-to-headers)
                       (message-fetch-field "from")))
               (account
                (cond
                 ((string-match "dehaeze.thomas@gmail.com" from) "gmail")
                 ((string-match "thomas.dehaeze@esrf.fr" from) "esrf"))))
            (setq message-sendmail-extra-arguments (list '"-a" account))))))

Basic Config

  (use-package! mu4e
    :config
    (setq mail-user-agent 'mu4e-user-agent
          mu4e-mu-binary "/usr/bin/mu"
          mu4e-maildir "~/.mail"
          mu4e-compose-format-flowed t
          mu4e-compose-in-new-frame nil
          mu4e-view-show-images t
          mu4e-html2text-command "w3m -dump -T text/html"
          mu4e-use-fancy-chars t
          mu4e-headers-include-related t
          mu4e-attachment-dir  "~/Downloads"
          message-kill-buffer-on-exit t
          mu4e-compose-signature-auto-include t
          mu4e-view-show-images t
          mu4e-view-show-addresses t)
    )

Additional config

  (use-package! mu4e
    :config
    ;; Use imagemagick, if available.
    (when (fboundp 'imagemagick-register-types)
      (imagemagick-register-types))

    ;; Sometimes html email is just not readable in a text based client, this lets me open the
    ;; email in my browser.
    (add-to-list 'mu4e-view-actions '("View in browser" . mu4e-action-view-in-browser) t)

    ;; Spell checking ftw.
    (add-hook 'mu4e-compose-mode-hook 'flyspell-mode)

    ;; Use Helm to select mailboxes
    (setq mu4e-completing-read-function 'completing-read)
    ;; Don't ask for a 'context' upon opening mu4e
    (setq mu4e-context-policy 'pick-first)
    ;; Don't ask to quit... why is this the default?
    (setq mu4e-confirm-quit nil)
    )

Provide Information

  (use-package! mu4e
    :config
    (setq mu4e-user-mail-address-list '("dehaeze.thomas@gmail.com" "thomas.dehaeze@esrf.fr")
          mu4e-compose-signature "Thomas Dehaeze\n"
          user-mail-address "dehaeze.thomas@gmail.com")
    ;; Default Folders
    (setq mu4e-sent-folder "/gmail/Sent"
          mu4e-drafts-folder "/gmail/Drafts"
          mu4e-trash-folder "/gmail/Trash"
          mu4e-refile-folder "/gmail/Archive")
    )

Receiving emails using mbsync

  (use-package! mu4e
    :config
    (setq mu4e-get-mail-command "checkmail"
          mu4e-update-interval nil
          mu4e-change-filenames-when-moving t) ;; Fix for mbsync
    )

Contexts

  (use-package! mu4e
    :config
    (setq mu4e-contexts
          `( ,(make-mu4e-context
               :name "gmail"
               :enter-func (lambda () (mu4e-message "Switch to the gmail context"))
               :match-func (lambda (msg) (when msg
                                           (string-prefix-p "/gmail" (mu4e-message-field msg :maildir))))
               :leave-func (lambda () (mu4e-clear-caches))
               :vars '(
                       (user-mail-address . "dehaeze.thomas@gmail.com")
                       (user-full-name        . "Thomas Dehaeze")
                       (mu4e-sent-folder . "/gmail/Sent")
                       (mu4e-trash-folder . "/gmail/Trash")
                       (mu4e-drafts-folder . "/gmail/Drafts")
                       (mu4e-refile-folder . "/gmail/Archive")
                       (mu4e-compose-signature  . "Thomas Dehaeze\n")
                       ))
             ,(make-mu4e-context
               :name "esrf"
               :enter-func (lambda () (mu4e-message "Switch to the esrf context"))
               :match-func (lambda (msg) (when msg
                                           (string-prefix-p "/esrf" (mu4e-message-field msg :maildir))))
               :leave-func (lambda () (mu4e-clear-caches))
               :vars '(
                       (user-mail-address . "thomas.dehaeze@esrf.fr")
                       (user-full-name        . "Thomas Dehaeze")
                       (mu4e-sent-folder . "/esrf/Sent")
                       (mu4e-trash-folder . "/esrf/Trash")
                       (mu4e-drafts-folder . "/esrf/Drafts")
                       (mu4e-refile-folder . "/esrf/Archive")
                       (mu4e-compose-signature  . "Thomas Dehaeze\n")
                       ))
             ))
    )

Sending mails

  (use-package! mu4e
    :config
    (setq message-send-mail-function 'message-send-mail-with-sendmail
          sendmail-program "/usr/bin/msmtp"
          user-full-name "Thomas Dehaeze")

    ;; This prevents saving the email to the Sent folder since gmail will do this for us on their end.
    (setq mu4e-sent-messages-behavior 'delete)

    ;; Use the correct account context when sending mail based on the from header.
    (setq message-sendmail-envelope-from 'header)
    (add-hook 'message-send-mail-hook 'choose-msmtp-account)
    )

Bookmarks

  (use-package! mu4e
    :config
    (setq mu4e-bookmarks `(,(make-mu4e-bookmark
                            :name "All Inboxes"
                            :query "maildir:/gmail/Inbox OR maildir:/esrf/Inbox"
                            :key ?i)
                           ("flag:unread" "Unread messages" ?u)
                           ("date:today..now" "Today's messages" ?t)
                           ("date:7d..now" "Last 7 days" ?w)))
    )

Doom init.el

  (when noninteractive
    (after! undo-tree
      (global-undo-tree-mode -1)))

  (doom! :completion
         company           ; the ultimate code completion backend
         helm              ; the *other* search engine for love and life
         ivy               ; a search engine for love and life

         :ui
         doom              ; what makes DOOM look the way it does
         deft
         hl-todo           ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
         hydra
         modeline
         ophints           ; highlight the region an operation acts on
         (popup            ; tame sudden yet inevitable temporary windows
          +all             ; catch all popups that start with an asterix
          +defaults)       ; default popup rules
         unicode           ; extended unicode support for various languages
         vc-gutter         ; vcs diff in the fringe
         vi-tilde-fringe   ; fringe tildes to mark beyond EOB
         workspaces        ; tab emulation, persistence & separate workspaces
         (emoji +unicode)

         :editor
         (evil +everywhere); come to the dark side, we have cookies
         fold              ; (nigh) universal code folding
         rotate-text       ; cycle region at point between text candidates
         snippets          ; my elves. They type so I don't have to
         word-wrap         ; soft wrapping with language-aware indent

         :emacs
         (dired +icons)    ; making dired pretty [functional]
         electric          ; smarter, keyword-based electric-indent
         (ibuffer +icons)  ; interactive buffer management
         vc                ; version-control and Emacs, sitting in a tree
         undo

         :term
         eshell            ; a consistent, cross-platform shell (WIP)
         vterm

         :tools
         debugger          ; Stepping through code, to help you add bugs
         (eval +overlay)     ; run code, run (also, repls)
         (lookup           ; helps you navigate your code and documentation
          +docsets)        ; ...or in Dash docsets locally
         lsp
         magit             ; a git porcelain for Emacs
         docker
         ;;pass              ; password manager for nerds
         pdf               ; pdf enhancements
         eval
         biblio
         (lookup +dictionary)

         :checkers
         syntax          ; tasing you for every semicolon you forget
         spell          ; tasing you for misspelling mispelling

         :lang
         data              ; config/data formats
         emacs-lisp        ; drown in parentheses
         go
         (javascript
          +lsp)
         (latex
          +latexmk
          +lsp)
         ;; markdown          ; writing docs for people to ignore
         (org              ; organize your plain life in plain text
          +dragndrop       ; drag & drop files/images into org buffers
          +hugo            ; use Emacs for hugo blogging
          +roam         ;
          +present)        ; using org-mode for presentations
         python            ; beautiful is better than ugly
         (sh                ; she sells {ba,z,fi}sh shells on the C xor
          +lsp)
         (web
          +lsp)
         yaml

         :email
         (mu4e +gmail)

         :app
         calendar
         ;;(rss +org)        ; emacs as an RSS reader
         ;;write             ; emacs for writers (fiction, notes, papers, etc.)

         :config
         literate
         (default +bindings)
         )

Doom packages.el

  (package! vimrc-mode)
  (package! poet-theme)
  (package! spice-mode)
  (package! org-pandoc-import
    :recipe (:host github
             :repo "tecosaur/org-pandoc-import"
             :files ("*.el" "filters" "preprocessors")))

  (package! citeproc-org)
  (package! org-wild-notifier)
  (package! org-gcal)
  ;; (package! ox-latex-subfigure
  ;;   :recipe (:host github :repo "linktohack/ox-latex-subfigure"))
  ;; (package! matlab-mode)
  (package! matlab-mode
    :recipe (:host github :repo "matlab-mode/mirror"))
  (package! org-ref)
  (package! citeproc-org)
  (package! org-ql)
  (package! org-fancy-priorities)
  (package! evil-escape :disable t)
  (package! dired-narrow)
  (package! web-server)