About
“I’m rarely happier than when spending an entire day programming my computer to perform automatically a task that would otherwise take me a good ten seconds to do by hand.” - Douglas Adams
This is the .doom.d
submodule of neurosys, my complete computing environment.
I use Emacs as the primary interface to my machine and remote machines. In many situations, it replaces CLIs or GUIs with well-designed keyboard-driven interfaces (e.g. magit). There’s no going back.
I use Doom Emacs as a base Emacs configuration. This is a literate org file, which specifies and documents the entirety of my Doom configuration. If you link this file to ~/.doom.d/config.org
, Doom will automatically tangle it on startup and whenever it changes.
If you’re reading this in a browser, consider opening it in Emacs Org mode for the full experience.
Inspirations:
- Jethro Kuan’s dotfiles
- bauer: an Emacs+Nix IDE
- Pierre Neidhardt: Emacs Everywhere
- Ryan Rix’s Complete Computing Environment (CCE)
- LemonBreezes’ Literate Doom Config
- Diego Zamboni’s Literate Emacs Config
- Justin Abrahms’ Literate Emacs Config
Table of Contents QUOTE TOC_3
- About
- Doom Module Declarations
- Package Configuration
- Header
- Global Constants
- Load helper functions
- Visual Settings
- Key Chord Config
- Hardware Settings
- Org
- org-noter: Syncing notes to PDFs
- org-recoll: Interface to Recoll - PDF content search
- org-ref: Managing citations
- org-journal: Managing daily journal files
- org-roam: Graph layer on top of Org
- org-download: Inserting images into org-mode
- org-cliplink: Better external links
- org-drill: Spaced Repetition
- Org Agenda
- org-sidebar
- TRAMP
- Effective Editing
- Julia
- Haskell
- Rust
- Jupyter
- Ivy
- Dired
- Search Utilities
- Version Control
- Searching + Annotating PDFs
- Lauching External Programs
- Jumping between windows
- Saving Window Configurations
- Autosave
- Additional Web Dev Tooling
- Google Translate
- Performance Tweaks
- Extra Load Files
- Misc Global Keybindings
- Misc
- Utility functions.
- Neurosys Module
- Package declarations
Doom Module Declarations
This file controls what Doom modules are enabled and what order they load in. Remember to run doom sync
or doom/reload
after modifying it.
;;; init.el -*- lexical-binding: t; -*-
(doom! :input
;; :desktop
;; exwm
:personal
neurosys
:completion
(company)
(ivy +prescient +childframe)
:ui
deft
doom
hl-todo
modeline
nav-flash
zen
:editor
;; Nice, but messes with org-journal
lispy
multiple-cursors
word-wrap
format
:emacs
dired
electric
vc
:term
vterm
:checkers
syntax
:tools
direnv
(eval +overlay)
docker
lookup
(magit +forge)
lsp
pass
pdf
:lang
common-lisp
data
emacs-lisp
javascript
(haskell +dante)
;; (julia +lsp)
;; julia
(latex +latexmk +cdlatex)
markdown
nix
(org
+journal
+hugo
+jupyter
+roam
)
python
(rust +lsp)
sh
:app
calendar
:config
literate
(default +bindings)
;; (default +bindings +smartparens)
)
Package Configuration
Header
Set lexical-binding
for this file.
Global Constants
Contact info
Some functionality uses this to identify you, e.g. GPG configuration, email clients, file templates and snippets.
Directories
(setq my/home-dir "/home/dan/")
(setq my/sync-base-dir (concat my/home-dir "Sync/"))
(setq my/work-base-dir (concat my/home-dir "Work/"))
(setq my/media-base-dir (concat my/home-dir "Media/"))
Org
Load helper functions
Visual Settings
(setq doom-font (font-spec :family "Hack" :size 18)
doom-variable-pitch-font (font-spec :family "Libre Baskerville")
doom-serif-font (font-spec :family "Libre Baskerville"))
(when (file-exists-p "~/.doom.d/banners")
(setq +doom-dashboard-banner-padding '(0 . 2)
+doom-dashboard-banner-file "deepfield-window.png"
+doom-dashboard-banner-dir "~/.doom.d/banners"))
(setq display-line-numbers-type nil)
;; Thin grey line separating windows
(set-face-background 'vertical-border "grey")
(set-face-foreground 'vertical-border (face-background 'vertical-border))
Theme
(use-package! doom-themes
:config
;; Global settings (defaults)
(setq doom-themes-enable-bold t ; if nil, bold is universally disabled
doom-themes-enable-italic t) ; if nil, italics is universally disabled
;; (load-theme 'doom-acario-light t)
;; (load-theme 'leuven t)
;; (load-theme 'doom-dark+ t)
;; (load-theme 'doom-solarized-light t)
;; (load-theme 'doom-one-light t)
(load-theme 'doom-nord-light t)
;; Enable flashing mode-line on errors
(doom-themes-visual-bell-config)
;; Corrects (and improves) org-mode's native fontification.
(doom-themes-org-config))
;; Waiting on https://github.com/hlissner/emacs-doom-themes/issues/252
;; Currently, some things like italics and some links in org fail to render correctly.
;; (use-package! poet-theme
;; :config
;; (load-theme 'poet))
;; (use-package almost-mono-themes
;; :config
;; ;; (load-theme 'almost-mono-black t)
;; (load-theme 'almost-mono-white t))
Key Chord Config
I don’t use Evil (Vim emulation), which would add an extra layer of complexity to everything. Instead, I heavily leverage key-chord.el, which enables binding simultaneous key presses (chords) to commands.
I have some custom code to bind chords to Doom’s leaders. Many commonly used commands are bound in these “key chord maps”.
Enable the key chord package
Set hardware-specific delay. Tweak this if:
- there are false keychords triggered when typing fast (delay too large)
- if expected keychords don’t register (delay too small)
- there’s a noticable lag when typing normally (delay too large)
(use-package! key-chord
:config
(key-chord-mode 1)
(setq key-chord-one-keys-delay 0.02
key-chord-two-keys-delay 0.03))
Setup for binding chords as leaders
(defun simulate-seq (seq)
(setq unread-command-events (listify-key-sequence seq)))
(defun send-doom-leader ()
(interactive)
(simulate-seq "\C-c"))
(setq doom-localleader-alt-key "M-c")
(defun send-doom-local-leader ()
(interactive)
(simulate-seq "\M-c"))
Define global key-chords
One of my proudest moments…. https://gist.github.com/dangirsh/86c001351c02b42321d20f462a66da6b
(after! key-chord
(key-chord-define-global "fj" 'send-doom-leader)
(key-chord-define-global "gh" 'send-doom-local-leader)
(setq dk-keymap (make-sparse-keymap))
(setq sl-keymap (make-sparse-keymap))
(key-chord-define-global "dk" dk-keymap)
(key-chord-define-global "sl" sl-keymap)
(defun add-to-keymap (keymap bindings)
(dolist (binding bindings)
(define-key keymap (kbd (car binding)) (cdr binding))))
(defun add-to-dk-keymap (bindings)
(add-to-keymap dk-keymap bindings))
(defun add-to-sl-keymap (bindings)
(add-to-keymap sl-keymap bindings))
(add-to-dk-keymap
'(("." . pop-global-mark)
("/" . org-recoll-search)
("<SPC>" . rgrep)
("b" . my/set-brightness)
("c" . my/open-literate-private-config-file)
("d" . dired-jump)
("k" . doom/kill-this-buffer-in-all-windows)
("m" . my/mathpix-screenshot-to-clipboard)
("n" . narrow-or-widen-dwim)
("o" . ibuffer)
("p" . my/publish-dangirsh.org)
("r" . my/edit-resume)
("s" . save-buffer)
("t" . +vterm/here)
("T" . google-translate-at-point)
("v" . neurosys/open-config-file)
("w" . google-this-noconfirm)
("x" . sp-splice-sexp)))
(key-chord-define-global ",." 'end-of-buffer)
;; FIXME: accidentally triggered too often
(key-chord-define-global "zx" 'beginning-of-buffer)
(key-chord-define-global "qw" 'delete-window)
(key-chord-define-global "qp" 'delete-other-windows)
(key-chord-define-global "fk" 'other-window)
(key-chord-define-global "jd" 'rev-other-window)
(key-chord-define-global "JJ" 'previous-buffer)
(key-chord-define-global "KK" 'next-buffer)
(key-chord-define-global "hh" 'helpful-at-point)
(key-chord-define-global "hk" 'helpful-key)
(key-chord-define-global "hv" 'helpful-variable)
;; no bueno: e.g. "pathfinder", "highfidelity"
;; (key-chord-define-global "hf" 'helpful-function)
(key-chord-define-global "vn" 'split-window-vertically-and-switch)
(key-chord-define-global "vm" 'split-window-vertically-and-switch) ; ergodox
(key-chord-define-global "hj" 'split-window-horizontally-and-switch)
(key-chord-define-global "jm" 'my/duplicate-line-or-region)
(key-chord-define-global "fv" 'comment-line)
(key-chord-define-global "kl" 'er/expand-region)
(key-chord-define-global "xx" 'execute-extended-command)
(key-chord-define-global "xf" 'find-file)
(key-chord-define-global "l;" 'repeat))
Hardware Settings
Keyboard
Sets caps to control and sets a snappy key repeat / delay.
xset r rate <delay> <rate>
(defun fix-keyboard ()
(interactive)
(shell-command "setxkbmap -option 'ctrl:nocaps'")
(shell-command "xset r rate 160 60"))
Toggle Touchpad
Occassionally, the touchpad gets triggered accidentally while typing. This is a quick way to disable/enable it.
(defun toggle-touchpad ()
(interactive)
(shell-command "/home/dan/my-config/scripts/toggle_trackpad.sh"))
Display Brightness
Set brightness by writing directly to system brightness file.
(defun my/set-brightness (brightness)
(interactive "nBrightness level: ")
(save-window-excursion
(find-file "/sudo:root@localhost:/sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/intel_backlight/brightness")
(kill-region
(point-min)
(point-max))
(insert
(format "%s" brightness))
(save-buffer)
(kill-buffer)))
TODO Switch to interfacing with a brightness manager.
Had issues the first time, but that was years ago.
Bluetooth
(defun my/connect-to-bose-700s ()
(interactive)
(shell-command "bluetoothctl -- connect 4C:87:5D:27:B8:63"))
(defun my/disconnect-to-bose-700s ()
(interactive)
(shell-command "bluetoothctl -- disconnect 4C:87:5D:27:B8:63"))
Org
“Notes aren’t a record of my thinking process. They are my thinking process.” – Richard Feynman
I use org as a primary interface. It currently manages:
- My second brain with org-roam & org-journal
- literate programming with babel and emacs-jupyter (e.g. this file)
- tasks + calendar with org-agenda and calfw
- Writing / blogging with ox-hugo, pandoc, etc…
- Has nice inline rendering of LaTeX
- Managing references + pdfs with org-ref
- Annotating PDFs with notes via org-noter
(use-package! org
:mode ("\\.org\\'" . org-mode)
:init
(add-hook 'org-src-mode-hook #'(lambda () (flycheck-mode 0)))
(add-hook 'org-mode-hook #'(lambda () (flycheck-mode 0)))
(map! :map org-mode-map
"M-n" #'outline-next-visible-heading
"M-p" #'outline-previous-visible-heading
"C-c ;" nil)
(setq org-src-window-setup 'current-window
org-return-follows-link t
org-confirm-elisp-link-function nil
org-confirm-shell-link-function nil
org-use-speed-commands t
org-catch-invisible-edits 'show
;; Use with consel-org-goto (gh .)
org-goto-interface 'outline-path-completion
org-preview-latex-image-directory "/tmp/ltximg/")
(setq org-file-apps '((auto-mode . emacs)
(directory . emacs)
("\\.mm\\'" . default)
("\\.x?html?\\'" . default)
("\\.pdf\\'" . (lambda (file link) (org-pdftools-open link))))))
(after! org
;; FIXME: Don't know why this isn't loaded automatically...
(require 'ob-async)
;; Clear Doom's default templates
(setq org-capture-templates '())
(add-to-list 'org-capture-templates `("l" "Listen" entry (file ,(concat org-directory "listen.org"))
"* TODO %?\n%i"))
(add-to-list 'org-latex-packages-alist "\\usepackage{braket}")
;; http://kitchingroup.cheme.cmu.edu/blog/2015/01/04/Redirecting-stderr-in-org-mode-shell-blocks/
(setq org-babel-default-header-args:sh
'((:prologue . "exec 2>&1") (:epilogue . ":")))
(setq org-babel-default-header-args:jupyter-julia '((:kernel . "julia-1.6")
(:display . "text/plain")
(:async . "yes")))
(setq org-confirm-babel-evaluate nil
org-use-property-inheritance t
org-export-with-sub-superscripts nil
org-export-use-babel nil
org-startup-indented t
org-pretty-entities nil
org-use-speed-commands t
org-return-follows-link t
org-outline-path-complete-in-steps nil
org-ellipsis ""
org-html-htmlize-output-type 'css
org-fontify-whole-heading-line t
org-fontify-done-headline t
org-fontify-quote-and-verse-blocks t
org-image-actual-width nil
org-src-fontify-natively t
org-src-tab-acts-natively t
org-src-preserve-indentation t
org-edit-src-content-indentation 0
org-adapt-indentation nil
org-hide-emphasis-markers t
org-special-ctrl-a/e t
org-special-ctrl-k t
org-export-with-broken-links t
org-yank-adjusted-subtrees t
org-src-window-setup 'reorganize-frame
org-src-ask-before-returning-to-edit-buffer nil
org-insert-heading-respect-content nil)
(add-hook 'org-babel-after-execute-hook 'org-display-inline-images 'append)
(add-hook 'org-babel-after-execute-hook 'org-toggle-latex-fragment 'append)
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("sh" . "src sh"))
(add-to-list 'org-structure-template-alist '("jl" . "src jupyter-julia"))
(add-to-list 'org-structure-template-alist '("py" . "src jupyter-python"))
(setq org-refile-use-outline-path 'file
org-outline-path-complete-in-steps nil
org-refile-allow-creating-parent-nodes 'confirm)
(setq org-format-latex-options
(quote (:foreground default
:background default
:scale 2.0
:matchers ("begin" "$1" "$" "$$" "\\(" "\\["))))
(setq org-todo-keywords
'((sequence "TODO(t)" "WIP(p)" "WAITING(w)" "SOMEDAY(s)" "QUESTION(q)" "|" "DONE(d)" "CANCELLED(c)")))
;; Colorize org babel output. Without this color codes are left in the output.
(defun my/display-ansi-colors ()
(interactive)
(let ((inhibit-read-only t))
(ansi-color-apply-on-region (point-min) (point-max))))
(add-hook 'org-babel-after-execute-hook #'my/display-ansi-colors)
(advice-add 'org-meta-return :override #'my/org-meta-return)
(setq org-tags-match-list-sublevels 'indented)
(setq org-image-actual-width nil)
(setq org-agenda-files '())
(setq org-agenda-prefix-format '((agenda . " %i %-12:c%?-12t% s")
(todo . " %i %b")
(tags . " %i %-12:c %b")
(search . " %i %-12:c %b")))
(setq org-agenda-category-icon-alist
`(("Personal" ,(list (all-the-icons-material "home" :height 1.2)) nil nil :ascent center)
("Incoming" ,(list (all-the-icons-material "move_to_inbox" :height 1.2)) nil nil :ascent center)))
)
(use-package! toc-org
:hook (org-mode . toc-org-mode))
org-noter: Syncing notes to PDFs
(use-package! org-noter
:after org
:config
;; helpful in EXWM, where there are no frames
(customize-set-variable 'org-noter-always-create-frame t)
(customize-set-variable 'org-noter-notes-window-behavior '(start))
(customize-set-variable 'org-noter-notes-window-location 'vertical-split)
(setq org-noter-notes-window-location 'other-frame
org-noter-notes-search-path '("~/Sync")
org-noter-auto-save-last-location t
org-noter-default-notes-file-names '("~/Sync/pdf_notes.org"))
;; This works for assigning PDF paths, but then breaks when trying to find the tpath later.
;; (defadvice! better-org-noter--get-or-read-document-property (orig-fn &rest args)
;; :around 'org-noter--get-or-read-document-property
;; (let ((default-directory (if (boundp 'my/noter-default-directory)
;; my/noter-default-directory
;; default-directory) ))
;; (apply orig-fn args)))
)
org-recoll: Interface to Recoll - PDF content search
GitHub - alraban/org-recoll: A lightweight emacs org-mode wrapper for the rec…
org-ref: Managing citations
;; Note that this pulls in Helm :/
;; https://github.com/jkitchin/org-ref/issues/202
(use-package! org-ref
:after (org bibtex)
:init
(setq org-ref-default-bibliography '("~/Sync/references.bib"))
(setq bibtex-completion-bibliography org-ref-default-bibliography)
:config
(setq org-latex-pdf-process
'("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"bibtex %b"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")
org-ref-bibliography-notes "~/Sync/pdf_notes.org"
org-ref-pdf-directory "~/Sync/pdf/"
org-ref-notes-function #'org-ref-notes-function-one-file)
(add-to-list 'org-latex-default-packages-alist "\\PassOptionsToPackage{hyphens}{url}")
(setq org-latex-listings 'minted
org-latex-packages-alist '(("" "minted")))
(defun get-pdf-filename (key)
(let ((results (bibtex-completion-find-pdf key)))
(if (equal 0 (length results))
(org-ref-get-pdf-filename key)
(car results))))
(add-hook 'org-ref-create-notes-hook
(lambda ()
(org-entry-put
nil
"NOTER_DOCUMENT"
(get-pdf-filename (org-entry-get
(point) "Custom_ID")))) )
(defun my/org-ref-noter-at-point ()
(interactive)
(let* ((results (org-ref-get-bibtex-key-and-file))
(key (car results))
(pdf-file (funcall org-ref-get-pdf-filename-function key))
(orig-bibtex-dialect bibtex-dialect))
(if (file-exists-p pdf-file)
(save-window-excursion
;; using the local flag for bibtex-set-dialect doesn't work
;; likely because org-ref-open-notes-at-point loses the buffer context
(bibtex-set-dialect 'BibTeX)
(org-ref-open-notes-at-point)
(bibtex-set-dialect orig-bibtex-dialect)
(find-file-other-window pdf-file)
(org-noter))
(message "no pdf found for %s" key))))
(map! :leader
:map org-mode-map
:desc "org-noter from ref"
"n p" 'my/org-ref-noter-at-point))
org-journal: Managing daily journal files
(use-package! org-journal
:after org
:config
(customize-set-variable 'org-journal-dir (concat org-roam-directory "journal"))
(customize-set-variable 'org-journal-file-format "private-%Y-%m-%d.org")
(customize-set-variable 'org-journal-date-prefix "#+TITLE: ")
(customize-set-variable 'org-journal-time-prefix "* ")
(customize-set-variable 'org-journal-time-format "")
(customize-set-variable 'org-journal-carryover-items "TODO=\"TODO\"")
(customize-set-variable 'org-journal-date-format "%Y-%m-%d")
(map! :leader
(:prefix-map ("n" . "notes")
(:prefix ("j" . "journal")
:desc "Today" "t" #'org-journal-today)))
(defun org-journal-today ()
(interactive)
(org-journal-new-entry t)))
org-roam: Graph layer on top of Org
aka my exocortex
(after! org-roam
(add-hook 'org-journal-mode 'org-roam-mode)
;; Globally accessible commands
(map! :leader
:prefix "n"
:desc "org-roam-find-file" "f" #'org-roam-find-file)
(set-company-backend! 'org-roam-mode 'company-capf)
(setq org-roam-db-location "/home/dan/Sync/org-roam/org-roam.db"
+org-roam-open-buffer-on-find-file nil
org-id-link-to-org-use-id t
org-roam-graph-exclude-matcher '("todo" "private")))
org-roam-server: Graph visualization / navigation
(use-package! org-roam-server :config (setq org-roam-server-host "127.0.0.1" org-roam-server-port 8080 org-roam-server-authenticate nil org-roam-server-export-inline-images t org-roam-server-serve-files nil org-roam-server-served-file-extensions '("pdf" "mp4" "ogv") org-roam-server-network-poll t org-roam-server-network-arrows nil org-roam-server-network-label-truncate t org-roam-server-network-label-truncate-length 60 org-roam-server-network-label-wrap-length 20))
org-roam-bibtex: Manage PDFs, notes, & citations.
(use-package! org-roam-bibtex :after org-roam :hook (org-roam-mode . org-roam-bibtex-mode) :bind (:map org-mode-map (("C-c n a" . orb-note-actions))) :config (setq bibtex-completion-library-path "~/Sync/pdf/") (setq orb-preformat-keywords '(("citekey" . "=key=") "title" "url" "file" "author-or-editor" "keywords")) (setq orb-note-actions-interface 'ivy) (setq orb-templates '(("r" "ref" plain (function org-roam-capture--get-point) "" :file-name "${citekey}" :head "#+TITLE: ${citekey}: ${title}\n#+ROAM_KEY: ${ref} - tags :: * ${title} :PROPERTIES: :Custom_ID: ${citekey} :URL: ${url} :AUTHOR: ${author-or-editor} :NOTER_DOCUMENT: %(orb-process-file-field \"${citekey}\") :NOTER_PAGE: :END:")))) (unpin! org-roam company-org-roam)
TODOs + org-agenda integration
In real Roam, TODO tags can be conveniently interspersed in any file. Then, filtering backlinks on the TODO page is the agenda view.
Unfortunately, this workflow doesn’t work for org-roam, since org-agenda is implemented too ineffeciently to handle thousands of agenda files.
My fix, as recommended here, is to put capture todos to a single file, but auto-insert links back to the context of the todo. Then, any TODOs for a page should be visible in the backlinks of that page. This is an inversion of the setup available in Roam.
The
org-capture-templates
templates used here:Template Doc %? Initial cursor position %F File path of original buffer %i Body %a Link back to context (after! org-roam (setq my/org-roam-todo-file (concat org-roam-directory "orgzly/todo.org")) (setq org-refile-targets `((,(append (my/open-org-files-list) (directory-files org-directory t ".*.org")) :maxlevel . 7))) (add-to-list 'org-agenda-files my/org-roam-todo-file) (add-to-list 'org-capture-templates '("t" "Todo" entry (file my/org-roam-todo-file) "* TODO %?")) (add-to-list 'org-capture-templates '("T" "Todo with Context" entry (file my/org-roam-todo-file) "* TODO %? #[[%F][%(my/org-roam-get-title \"%F\")]]\n%i\n%a")))
org-download: Inserting images into org-mode
(use-package! org-download
:config
;; take an image that is already on the clipboard
(customize-set-variable 'org-download-screenshot-method "xclip -selection clipboard -t image/png -o > %s"))
org-cliplink: Better external links
Automatically pulls the titles from pages from a URL, then inserts a corresponding org-link.
org-drill: Spaced Repetition
I tried integrating with Anki first, since I thought it would be useful to go over the cards on mobile. It was a mess, so now I’m trying the native Org approach.
(use-package! org-drill
:after org
:config
(add-to-list 'org-capture-templates
`("d" "Drill" entry
(file ,(concat org-directory "drill.org"))
"* %^{Heading} :drill:\n\n%^{Question}\n\n** Answer\n\n%^{Answer}")))
Org Agenda
(setq org-agenda-start-day "+0d" ; start today
org-agenda-show-current-time-in-grid t
org-agenda-timegrid-use-ampm t
org-agenda-use-time-grid nil ; Toggle it with 'G' in agenda view
org-agenda-span 3)
(add-to-list 'org-agenda-files "~/Sync/org-roam/orgzly/boox-incoming.org")
(add-to-list 'org-agenda-files "~/Sync/org-roam/orgzly/pixel-incoming.org")
org-super-agenda: Better Org Agenda
(use-package! org-super-agenda :after org-agenda :config (setq org-super-agenda-groups '((:discard (:todo "SOMEDAY")) (:discard (:todo "QUESTION")) (:name "WIP" :todo "WIP") (:name "High Priority" :priority "A") (:name "Med Priority" :priority "B") (:name "Low Priority" :priority "C") (:name "Today" ;; :time-grid t :scheduled today :deadline today) (:auto-todo t))) (org-super-agenda-mode)) (defun my/open-questions () (interactive) (let ((org-super-agenda-groups '((:discard (:not (:todo "QUESTION"))) (:auto-todo t)))) (org-agenda nil "t")))
TODO org-sidebar
TRAMP
Effective Editing
Structure Editing
(use-package! lispy
:config
(advice-add 'delete-selection-pre-hook :around 'lispy--delsel-advice)
;; FIXME: magit-blame still fails to all "ret" when lispy is on
;; the compat code isn't even getting hit!
(setq lispy-compat '(edebug magit-blame-mode))
;; this hook leaves lispy mode off, but that's not as bad as breaking blame!
(add-hook 'magit-blame-mode-hook #'(lambda () (lispy-mode 0)))
:hook
((emacs-lisp-mode common-lisp-mode lisp-mode) . lispy-mode)
:bind (:map lispy-mode-map
("'" . nil) ; leave tick behaviour alone
("M-n" . nil)
("C-M-m" . nil)))
;; (use-package! smartparens
;; :init
;; (map! :map smartparens-mode-map
;; "C-M-f" #'sp-forward-sexp
;; "C-M-b" #'sp-backward-sexp
;; "C-M-u" #'sp-backward-up-sexp
;; "C-M-d" #'sp-down-sexp
;; "C-M-p" #'sp-backward-down-sexp
;; "C-M-n" #'sp-up-sexp
;; "C-M-s" #'sp-splice-sexp
;; ;; conflicts with mc
;; ;; "C-)" #'sp-forward-slurp-sexp
;; "C-}" #'sp-forward-barf-sexp
;; ;; conflicts with mc
;; ;; "C-(" #'sp-backward-slurp-sexp
;; "C-M-)" #'sp-backward-slurp-sexp
;; "C-M-)" #'sp-backward-barf-sexp))
(use-package! wrap-region
:hook
(org-mode . wrap-region-mode)
(latex-mode . wrap-region-mode)
:config
(wrap-region-add-wrappers
'(("*" "*" nil (org-mode))
("~" "~" nil (org-mode))
("/" "/" nil (org-mode))
("=" "=" nil (org-mode))
("_" "_" nil (org-mode))
("$" "$" nil (org-mode latex-mode)))))
(use-package! aggressive-indent
:hook
(emacs-lisp-mode . aggressive-indent-mode)
(common-lisp-mode . aggressive-indent-mode))
Mathpix: OCR LaTeX From Images
The mathpix.el package is failing when using the standard package!
/ use-package!
setup.
Loading manually from file:mathpix.el for now.
(defun setup-mathpix ()
(load-file (concat doom-private-dir "mathpix.el"))
(require 'mathpix)
(customize-set-variable 'mathpix-app-id "dan_girsh_gmail_com_5d68dc")
(customize-set-variable 'mathpix-app-key "600336b7b2b932549ce4")
(customize-set-variable 'mathpix-screenshot-method "scrot -s %s"))
(defun my/mathpix-screenshot-to-clipboard ()
(interactive)
(with-temp-buffer
(mathpix-screenshot)
(kill-new
(format "$$\n%s\n$$" (buffer-string)))))
Multiple Cursors
(use-package! multiple-cursors
:init
(setq mc/always-run-for-all t)
:config
(add-to-list 'mc/unsupported-minor-modes 'lispy-mode)
:bind (("C-S-c" . mc/edit-lines)
("C-M-g" . mc/mark-all-like-this-dwim)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-)" . mc/skip-to-next-like-this)
("C-M->" . mc/skip-to-next-like-this)
("C-(" . mc/skip-to-previous-like-this)
("C-M-<" . mc/skip-to-previous-like-this)))
(use-package! iedit
:init
(map! "C-;" 'company-complete)
(map! "M-i" 'iedit-mode))
Undo Tree
(use-package undo-tree
:init
(setq undo-tree-visualizer-timestamps t
undo-tree-visualizer-diff t)
:config
;; stolen from layers/+spacemacs/spacemacs-editing/package.el
(progn
;; restore diff window after quit. TODO fix upstream
(defun my/undo-tree-restore-default ()
(setq undo-tree-visualizer-diff t))
(advice-add 'undo-tree-visualizer-quit :after #'my/undo-tree-restore-default))
(global-undo-tree-mode 1))
Julia
Doom’s Julia module is opinionated. I’d like full control, so I’m configuring Julia myself here.
(defvar inferior-julia-program-name "julia")
(use-package! julia
:interpreter "julia"
:hook (julia-mode . julia-repl-mode))
;; (defun my/julia-repl-hook ()
;; (setq julia-repl-terminal-backend (make-julia-repl--buffer-vterm)))
(use-package! julia-repl
:config
; See: https://github.com/tpapp/julia-repl/pull/84
;; (require 'vterm)
;; (setq julia-repl-terminal-backend (make-julia-repl--buffer-vterm))
)
;; https://github.com/gcv/julia-snail
;; (use-package julia-snail
;; :hook (julia-mode . julia-snail-mode))
;; (use-package eglot-jl
;; :hook (julia-mode . eglot)
;; :config
;; (eglot-jl-init))
Convert Julia Markdown Blocks to Org Blocks
Put cursor at beginning of ```julia
line and run jmd-block-to-jupyter-julia
.
(defun jmd-block-to-jupyter-julia ()
(interactive)
(kmacro-lambda-form [?\C- ?\C-e backspace ?\C-c ?\C-, ?j down ?\C- ?\C-s ?` return left ?\C-w up ?\C-y down ?\C-k] 0 "%d"))
Haskell
Rust
Enabled the rust
module.
Racer Setup
rustup component add rust-src
rustup toolchain add nightly
cargo +nightly install racer
Org-babel setup
cargo-script
required for org-babel blocks (otherwise each requires a main
function)
cargo install cargo-script
Language Server
sudo curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-linux -o /usr/local/bin/rust-analyzer
sudo chmod +x /usr/local/bin/rust-analyzer
(defun start-file-process-shell-command@around (start-file-process-shell-command name buffer &rest args)
"Start a program in a subprocess. Return the process object for it. Similar to `start-process-shell-command', but calls `start-file-process'."
;; On remote hosts, the local `shell-file-name' might be useless.
(let ((command (mapconcat 'identity args " ")))
(funcall start-file-process-shell-command name buffer command)))
(advice-add 'start-file-process-shell-command :around #'start-file-process-shell-command@around)
TRAMP
GitHub - brotzeit/rustic: Rust development environment for Emacs
(with-eval-after-load "lsp-rust" (lsp-register-client (make-lsp-client :new-connection (lsp-stdio-connection (lambda () `(,(or (executable-find (cl-first lsp-rust-analyzer-server-command)) (lsp-package-path 'rust-analyzer) "rust-analyzer") ,@(cl-rest lsp-rust-analyzer-server-args)))) :remote? t :major-modes '(rust-mode rustic-mode) :initialization-options 'lsp-rust-analyzer--make-init-options :notification-handlers (ht<-alist lsp-rust-notification-handlers) :action-handlers (ht ("rust-analyzer.runSingle" #'lsp-rust--analyzer-run-single)) :library-folders-fn (lambda (_workspace) lsp-rust-library-directories) :after-open-fn (lambda () (when lsp-rust-analyzer-server-display-inlay-hints (lsp-rust-analyzer-inlay-hints-mode))) :ignore-messages nil :server-id 'rust-analyzer-remote)))
Jupyter
(use-package! jupyter
:init
(setq jupyter-eval-use-overlays t)
(map!
:map org-mode-map
:localleader
(:desc "Jupyter Org Hydra" "j" #'jupyter-org-hydra/body))
(defun my/insert-julia-src-block ()
(interactive)
(jupyter-org-insert-src-block t current-prefix-arg))
;; I locally modified jupyter-completion-at-point to check for this,
;; since completions regularly crash the julia kernel for me :/
(setq my/jupyter-enable-completions nil)
;; Better than `M-c C-, j` or `M-c j =`
(key-chord-define-global "jq" #'my/insert-julia-src-block)
(map!
:map julia-mode-map
:localleader
(:prefix ("j" . "jupyter")
:desc "Run REPL" "o" #'jupyter-run-repl
:desc "Eval function" "f" #'jupyter-eval-defun
:desc "Eval buffer" "b" #'jupyter-eval-buffer
:desc "Eval region" "r" #'jupyter-eval-region
:desc "Restart REPL" "R" #'jupyter-repl-restart-kernel
:desc "Interrupt REPL" "i" #'jupyter-repl-interrup-kernel
:desc "Scratch buffer" "s" #'jupyter-repl-scratch-buffer
:desc "Remove overlays" "O" #'jupyter-eval-remove-overlays
:desc "Eval string" "w" #'jupyter-eval-string
:desc "Inspect at point" "d" #'jupyter-inspect-at-point)))
Ivy
Ivy allows you to find the input to a command by incrementally searching the space of all valid inputs. It’s well-supported in Doom.
(after! ivy
;; Causes open buffers and recentf to be combined in ivy-switch-buffer
(setq ivy-use-virtual-buffers t
counsel-find-file-at-point t
ivy-wrap nil
ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-top-center))
ivy-posframe-height-alist '((t . 20))
ivy-posframe-parameters '((internal-border-width . 1))
ivy-posframe-width 100)
(add-hook 'eshell-mode-hook
(lambda ()
(eshell-cmpl-initialize)
(define-key eshell-mode-map (kbd "M-r") 'counsel-esh-history)))
(add-to-dk-keymap
'(("g" . +ivy/project-search)
("h" . +ivy/projectile-find-file)
("i" . counsel-semantic-or-imenu)
("j" . ivy-switch-buffer))))
Dired
(after! dired
(setq dired-listing-switches "-aBhl --group-directories-first"
dired-dwim-target t
dired-recursive-copies (quote always)
dired-recursive-deletes (quote top)
;; Directly edit permisison bits!
wdired-allow-to-change-permissions t))
(use-package! dired-narrow
:commands (dired-narrow-fuzzy)
:init
(map! :map dired-mode-map
:desc "narrow" "/" #'dired-narrow-fuzzy))
;; Directly edit permisison bits!
(setq wdired-allow-to-change-permissions t)
Search Utilities
Better Grep
Scanning occurances within a buffer
This is one of my primary ways of navigating next: jump through other occurances of the text currently under the cursor.
(use-package! smartscan
:init (global-smartscan-mode 1)
:bind (("M-N" . smartscan-symbol-go-forward)
("M-P" . smartscan-symbol-go-backward)
:map smartscan-map
("M-p" . nil)
("M-n" . nil)))
Version Control
Disable version control when using TRAMP to avoid extra delays
(setq vc-ignore-dir-regexp
(format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp))
Magit
Stunningly useful.
(use-package! magit
:config
(set-default 'magit-stage-all-confirm nil)
(set-default 'magit-unstage-all-confirm nil)
(remove-hook 'magit-mode-hook 'turn-on-magit-gitflow)
;; Restores "normal" behavior in branch view (when hitting RET)
(setq magit-visit-ref-behavior '(create-branch checkout-any focus-on-ref))
(setq git-commit-finish-query-functions nil)
(setq magit-visit-ref-create 1)
(setq magit-revision-show-gravatars nil))
(after! (magit key-chord)
(add-to-sl-keymap
'(("k" . magit-dispatch-popup)
("s" . magit-status)
("o" . magit-log)
("u" . magit-submodule-update)
("l" . magit-show-refs-head))))
Searching + Annotating PDFs
pdf-tools
(after! pdf-tools
;;swiper doesn't trigger the pdf-isearch
(map! :map pdf-isearch-minor-mode-map
"C-s" 'isearch-forward-regexp))
Lauching External Programs
Jumping between windows
Here we set the window labels to homerow keys (they are numbers by default)
Would use the window-select Doom module, but that (unwantedly in EXWM) binds other-window to ace-window.
(use-package! ace-window
:config
(map! "C-M-SPC" #'ace-window)
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))
Saving Window Configurations
Autosave
(use-package! real-auto-save
:hook
(prog-mode . real-auto-save-mode)
(org-mode . real-auto-save-mode))
Additional Web Dev Tooling
Including the Doom javascript
module does most of the work…
Google Translate
(use-package google-translate
:custom
(google-translate-backend-method 'curl)
:config
(defun google-translate--search-tkk () "Search TKK." (list 430675 2721866130))
(setq google-translate-output-destination 'kill-ring))
Performance Tweaks
Swiper
Seems like a good solution to swiper being slow is to just use swiper-isearch, but I find swiper’s handling of multiple results on a line more convenient most of the time.
Instead, I follow advice from this Reddit comment to make swiper ignore visual line mode. Seems to help for now.
Extra Load Files
Misc Global Keybindings
(map!
"M-p" (lambda () (interactive) (scroll-down 4))
"M-n" (lambda () (interactive) (scroll-up 4))
"C-h h" 'helpful-at-point
"C-h f" 'helpful-function
"C-h v" 'helpful-variable
"C-h k" 'helpful-key
"M-SPC" 'avy-goto-word-or-subword-1
"C-s" 'swiper-isearch
;; "C-M-s" 'swiper-isearch
"C-S-d" 'my/duplicate-line-or-region
"C-c <left>" 'winner-undo
"C-c <right>" 'winner-redo
"C-+" 'text-scale-increase
"C--" 'text-scale-decrease
;; FIXME: This currently relies on Helm as an undeclared dep!
"M-y" 'helm-show-kill-ring
"<f5>" 'my/night-mode
"<f6>" 'my/day-mode
"C-z" 'undo-fu-only-undo
"C-S-z" 'undo-fu-only-redo
"C-/" 'undo-fu-only-undo
"C-?" 'undo-fu-only-redo)
(global-set-key [remap goto-line] 'goto-line-with-feedback)
(global-set-key [remap goto-line] 'goto-line-with-feedback)
Misc
(flycheck-mode 0)
(setq direnv-always-show-summary nil)
(add-to-list 'auto-mode-alist '("\\.eps\\'" . doc-view-minor-mode))
;; all backup and autosave files in the tmp dir
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))
;; Coordinate between kill ring and system clipboard
(setq save-interprogram-paste-before-kill t)
(setq eshell-history-file-name (concat doom-private-dir "eshell-history"))
;; This is dangerous, but reduces the annoying step of confirming local variable settings each time
;; a file with a "Local Variables" clause (like many Org files) is opened.
(setq enable-local-variables :all)
;; This is usually just annoying
(setq compilation-ask-about-save nil)
;; No confirm on exit
(setq confirm-kill-emacs nil)
;; Alternative to calling save-buffers-kill-emacs, since
;; a) Muscle memory sends me to "kill-emacs" via fj-q-q
;; b) save-buffers-kill-emacs sometimes fails
;; This way, we try to save things, but quit in any case.
(defun my/save-ignore-errors ()
(ignore-errors
(save-some-buffers)))
(add-hook 'kill-emacs-hook 'my/save-ignore-errors)
;; Help out Projectile for remote files via TRAMP
;; https://sideshowcoder.com/2017/10/24/projectile-and-tramp/
(defadvice projectile-on (around exlude-tramp activate)
"This should disable projectile when visiting a remote file"
(unless (--any? (and it (file-remote-p it))
(list
(buffer-file-name)
list-buffers-directory
default-directory
dired-directory))
ad-do-it))
(setq projectile-mode-line "Projectile")
(setq password-store-password-length 20)
;; Truncate compiilation buffers, otherwise Emacs gets slow
;; https://stackoverflow.com/questions/11239201/can-i-limit-the-length-of-the-compilation-buffer-in-emacs
(add-hook 'compilation-filter-hook 'comint-truncate-buffer)
(setq comint-buffer-maximum-size 2000)
(setq recentf-max-saved-items 10000)
Utility functions.
;;; ~/.doom.d/funcs.el -*- lexical-binding: t; -*-
(defun my/open-literate-private-config-file ()
"Open the private config.org file."
(interactive)
(find-file (expand-file-name "config.org" doom-private-dir)))
(defun my/rot13-and-kill-region ()
(interactive)
(kill-new (rot13
(buffer-substring (region-beginning) (region-end)))))
(defun my/org-export-subtree-as-markdown-and-copy ()
(interactive)
(save-window-excursion
(let ((export-buffer (org-md-export-as-markdown nil t nil)))
(with-current-buffer export-buffer
(clipboard-kill-ring-save (point-min) (point-max)))
(kill-buffer export-buffer))))
(defun goto-line-with-feedback ()
"Show line numbers temporarily, while prompting for the line number input"
(interactive)
(unwind-protect
(progn
(linum-mode 1)
(call-interactively 'goto-line))
(linum-mode -1)))
(defun split-window-horizontally-and-switch ()
(interactive)
(split-window-horizontally)
(other-window 1))
(defun split-window-vertically-and-switch ()
(interactive)
(split-window-vertically)
(other-window 1))
(defun my-increment-number-decimal
(&optional
arg)
"Increment the number forward from point by 'arg'."
(interactive "p*")
(save-excursion
(save-match-data
(let (inc-by field-width answer)
(setq inc-by
(if arg
arg
1))
(skip-chars-backward "0123456789")
(when (re-search-forward "[0-9]+" nil t)
(setq field-width (- (match-end 0)
(match-beginning 0)))
(setq answer (+ (string-to-number (match-string 0) 10) inc-by))
(when (< answer 0)
(setq answer (+ (expt 10 field-width) answer)))
(replace-match (format (concat "%0" (int-to-string field-width) "d") answer)))))))
(defun rev-other-window ()
(interactive)
(other-window -1))
(defun eshell-here ()
"Opens up a new shell in the directory associated with the
current buffer's file. The eshell is renamed to match that
directory to make multiple eshell windows easier."
(interactive)
(let* ((parent (if (buffer-file-name)
(file-name-directory (buffer-file-name))
default-directory))
(name (car (last (split-string parent "/" t)))))
(eshell "new")
(rename-buffer (concat "*eshell: " name "*"))
(insert (concat "ls"))
(eshell-send-input)))
;; https://www.emacswiki.org/emacs/CopyingWholeLines
(defun my/duplicate-line-or-region (&optional n)
"Duplicate current line, or region if active.
With argument N, make N copies.
With negative N, comment out original line and use the absolute value."
(interactive "*p")
(let ((use-region (use-region-p)))
(save-excursion
(let ((text (if use-region ; Get region if active, otherwise line
(buffer-substring (region-beginning) (region-end))
(prog1 (thing-at-point 'line)
(end-of-line)
(if (< 0 (forward-line 1)) ; Go to beginning of next line, or make a new one
(newline))))))
(dotimes (i (abs (or n 1))) ; Insert N times, or once if not specified
(insert text))))
(if use-region nil ; Only if we're working with a line (not a region)
(let ((pos (- (point) (line-beginning-position)))) ; Save column
(if (> 0 n) ; Comment out original with negative arg
(comment-region (line-beginning-position) (line-end-position)))
(forward-line 1)
(forward-char pos)))))
(defun my/org-ref-noter-link-from-arxiv (arxiv-number)
"Retrieve a pdf for ARXIV-NUMBER and save it to the default PDF dir.
Then, add a bibtex entry for the new file in the default bib
file. Then, create a new org-ref note heading for it (see
org-ref-create-notes-hook in packages.el to see it also creates
a property for org-noter). Finally, insert a descriptive link to
the note heading at point, using the paper title as the link
text.
"
(interactive "sarxiv number: ")
(let ((bibtex-dialect 'BibTeX))
(org-ref-save-all-bibtex-buffers)
(save-window-excursion
(arxiv-get-pdf-add-bibtex-entry arxiv-number
(car org-ref-default-bibliography)
org-ref-pdf-directory)
(org-ref-save-all-bibtex-buffers))
(let* ((parsed-entry (save-excursion
(with-temp-buffer
;; In case of dir-local path to references.bib
(hack-dir-local-variables-non-file-buffer)
(insert-file-contents (car org-ref-default-bibliography))
(bibtex-set-dialect (parsebib-find-bibtex-dialect) t)
(search-forward (format "{%s}" arxiv-number))
(bibtex-narrow-to-entry)
(bibtex-beginning-of-entry)
(bibtex-parse-entry)))))
(org-insert-heading)
(let* ((raw-ref-title (cdr (assoc "title" parsed-entry)))
(ref-title (s-replace-regexp (rx (sequence "\n" (+ space))) " "
(car (cdr (s-match (rx "{" (group (+ anything)) "}") raw-ref-title)))))
(ref-key (cdr (assoc "=key=" parsed-entry))))
(insert ref-title)
(insert "\n\n")
(insert (format "cite:%s" ref-key))))))
(defun my/set-redshift (level)
(interactive "nRedshift level: ")
(shell-command (format "redshift -P -O %s" level)))
(defun my/night-mode ()
(interactive)
(load-theme 'doom-one t)
(doom/reload-theme)
(my/set-brightness 10)
(my/set-redshift 1500))
(defun my/day-mode ()
(interactive)
(load-theme 'doom-solarized-light t)
(doom/reload-theme)
(my/set-brightness 1000)
(my/set-redshift 6000))
(defun narrow-or-widen-dwim (p)
"If the buffer is narrowed, it widens. Otherwise, it narrows intelligently.
Intelligently means: region, subtree, or defun, whichever applies
first.
With prefix P, don't widen, just narrow even if buffer is already
narrowed."
(interactive "P")
(declare (interactive-only))
(cond ((and (buffer-narrowed-p) (not p)) (widen))
((region-active-p)
(narrow-to-region (region-beginning) (region-end)))
((derived-mode-p 'org-mode) (org-narrow-to-subtree))
(t (narrow-to-defun))))
;; https://stackoverflow.com/questions/28727190/org-babel-tangle-only-one-code-block
(defun my/org-babel-tangle-block()
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively 'org-babel-tangle)))
(defun my/open-org-files-list ()
(delq nil
(mapcar (lambda (buffer)
(buffer-file-name buffer))
(org-buffer-list 'files t))))
(defun my/org-latex-toggle-recent ()
(when (looking-back (rx "$ "))
(save-excursion
(backward-char 1)
(org-toggle-latex-fragment))))
(add-hook 'org-mode-hook
(lambda ()
(org-cdlatex-mode)
(add-hook 'post-self-insert-hook #'my/org-latex-toggle-recent 'append 'local)))
(defun my/save-shebanged-file-as-executable ()
(and (save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(save-match-data
(looking-at "^#!"))))
(not (file-executable-p buffer-file-name))
(shell-command (concat "chmod +x " buffer-file-name))
(message
(concat "Saved as script: " buffer-file-name))))
(add-hook 'after-save-hook #'my/save-shebanged-file-as-executable)
;; https://llazarek.com/2018/10/images-in-org-mode.html
(defun my/org-link-file-path-at-point ()
"Get the path of the file referred to by the link at point."
(let* ((org-element (org-element-context))
(is-subscript-p (equal (org-element-type org-element) 'subscript))
(is-link-p (equal (org-element-type org-element) 'link))
(is-file-p (equal (org-element-property :type org-element) "file")))
(when is-subscript-p
(user-error "Org thinks you're in a subscript. Move the point and try again."))
(unless (and is-link-p is-file-p)
(user-error "Not on file link"))
(expand-file-name (org-element-property :path org-element))))
(defun my/org-resize-image-at-point (&optional arg)
"Resize the image linked at point."
(interactive)
(let ((img (my/org-link-file-path-at-point))
(percent (read-number "Resize to what percentage of current size? ")))
(start-process "mogrify" nil "/usr/bin/mogrify"
"-resize"
(format "%s%%" percent)
img)))
(defun my/run-in-fresh-compilation (cmd &optional dir)
(defun local-compile-buffer-namer (ignored)
(generate-new-buffer-name cmd))
(let* ((compilation-buffer-name-function #'local-compile-buffer-namer)
(compilation-ask-about-save nil)
(full-cmd (if dir (concat "cd " dir " && " cmd) cmd)))
(compile full-cmd)))
(defun my/publish-dangirsh.org ()
(interactive)
(let ((neurosys-org-file "/home/dan/repos/dangirsh.org/site/projects/neurosys.org")
(doom-org-file "/home/dan/repos/dangirsh.org/site/projects/doom-config.org"))
;; Hack: copy in the files - had issues hardlinking it.
(copy-file (concat neurosys/base-dir "README.org") neurosys-org-file t)
(copy-file (concat doom-private-dir "config.org") doom-org-file t)
(my/run-in-fresh-compilation "./publi.sh" "/home/dan/repos/dangirsh.org/")))
(defun my/org-roam-get-title (path)
(save-window-excursion
;; A simple find-file didn't work when the original was narrowed
(with-temp-buffer
(insert-file-contents path)
(org-mode)
(car (org-roam--extract-titles-title)))))
(defun my/set-timezone ()
(interactive)
(shell-command "sudo timedatectl set-timezone America/Los_Angeles")
;; (shell-command "sudo timedatectl set-timezone America/New_York")
;; (shell-command "sudo timedatectl set-timezone Europe/Paris")
)
(my/set-timezone)
Resume
Org
(defun my/org-split-block ()
"Sensibly split the current Org block at point."
(interactive)
(if (my/org-in-any-block-p)
(save-match-data
(save-restriction
(widen)
(let ((case-fold-search t)
(at-bol (bolp))
block-start
block-end)
(save-excursion
(re-search-backward "^\\(?1:[[:blank:]]*#\\+begin_.+?\\)\\(?: .*\\)*$" nil nil 1)
(setq block-start (match-string-no-properties 0))
(setq block-end (replace-regexp-in-string
"begin_" "end_" ;Replaces "begin_" with "end_", "BEGIN_" with "END_"
(match-string-no-properties 1))))
;; Go to the end of current line, if not at the BOL
(unless at-bol
(end-of-line 1))
(insert (concat (if at-bol "" "\n")
block-end
"\n\n"
block-start
(if at-bol "\n" "")))
;; Go to the line before the inserted "#+begin_ .." line
(beginning-of-line (if at-bol -1 0)))))
(message "Point is not in an Org block")))
(defun my/org-in-any-block-p ()
"Return non-nil if the point is in any Org block.
The Org block can be *any*: src, example, verse, etc., even any
Org Special block.
This function is heavily adapted from `org-between-regexps-p'."
(save-match-data
(let ((pos (point))
(case-fold-search t)
(block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$")
(limit-up (save-excursion (outline-previous-heading)))
(limit-down (save-excursion (outline-next-heading)))
beg end)
(save-excursion
;; Point is on a block when on BLOCK-BEGIN-RE or if
;; BLOCK-BEGIN-RE can be found before it...
(and (or (org-in-regexp block-begin-re)
(re-search-backward block-begin-re limit-up :noerror))
(setq beg (match-beginning 0))
;; ... and BLOCK-END-RE after it...
(let ((block-end-re (concat "^[[:blank:]]*#\\+end_"
(match-string-no-properties 1)
"\\( .*\\)*$")))
(goto-char (match-end 0))
(re-search-forward block-end-re limit-down :noerror))
(> (setq end (match-end 0)) pos)
;; ... without another BLOCK-BEGIN-RE in-between.
(goto-char (match-beginning 0))
(not (re-search-backward block-begin-re (1+ beg) :noerror))
;; Return value.
(cons beg end))))))
(defun my/org-meta-return (&optional arg)
"Insert a new heading or wrap a region in a table.
Calls `org-insert-heading', `org-insert-item',
`org-table-wrap-region', or `my/org-split-block' depending on
context. When called with an argument, unconditionally call
`org-insert-heading'."
(interactive "P")
(org-check-before-invisible-edit 'insert)
(or (run-hook-with-args-until-success 'org-metareturn-hook)
(call-interactively (cond (arg #'org-insert-heading)
((org-at-table-p) #'org-table-wrap-region)
((org-in-item-p) #'org-insert-item)
((my/org-in-any-block-p) #'my/org-split-block)
(t #'org-insert-heading)))))
;; https://emacs.stackexchange.com/questions/50649/jumping-from-a-source-block-to-the-tangled-file
(defun my/org-babel-tangle-jump ()
"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)))))
;; https://sachachua.com/blog/2019/07/tweaking-emacs-on-android-via-termux-xclip-xdg-open-syncthing-conflicts/
(defun my/org-archive-done-tasks (&optional scope)
"Archive finished or cancelled tasks.
SCOPE can be 'file or 'tree."
(interactive)
(org-map-entriesjjjp
(lambda ()
(org-archive-subtree)
(setq org-map-continue-from (outline-previous-heading)))
"TODO=\"DONE\"|TODO=\"CANCELLED\"" (or scope (if (org-before-first-heading-p) 'file 'tree))))
Neurosys Module
Elisp related to my neurosys.
Globals
Helpers
Deployment
(defun neurosys/deploy-to-host (host host-home-raw)
(interactive "sHost: \nsHost home: ")
(let ((host-root (format "/ssh:%s:/" host))
;; mind the trailing slash, since we're passing it to rsync
(host-home (file-name-as-directory host-home-raw)))
(save-window-excursion
(org-babel-tangle)
(my/run-in-fresh-compilation
(format (concat neurosys/base-dir "rsync.sh %s %s") host host-home))
;; TODO: Is there cleaner way to compile over TRAMP?
(find-file host-root)
(compile "nixos-rebuild switch --show-trace")))
(switch-to-buffer-other-window "*compilation*"))
(defun neurosys/deploy-to-nixos-dev ()
(interactive)
(neurosys/deploy-to-host "root@nixos-dev" "/home/dan/"))
TODO Upgrading [0/2]
- [ ] Update channels with
nix-channel --update
- [ ] Rebuild packages with
nixos-rebuild switch
NOTE: These can be combined with nixos-rebuild switch --update
Misc
(defun neurosys/open-config-file ()
(interactive)
(find-file (concat neurosys/base-dir "README.org")))
Keybindings
(map!
:leader
:prefix ("j" . "neurosys")
:desc "deploy" "D" #'neurosys/deploy-to-host
:desc "deploy to nixos-dev" "d" #'neurosys/deploy-to-nixos-dev)
Package declarations
Any desired package not declared in a Doom module must be declared here. This seems redundant given the corresponding use-package!
declarations, but required by Doom (presumably for lazy loading).
;; -*- no-byte-compile: t; -*-
;;; $DOOMDIR/packages.el
(package! ace-window)
(package! aggressive-indent)
(package! burly :recipe (:host github :repo "alphapapa/burly.el"))
;; (package! company-posframe)
(package! deadgrep)
(package! dired-narrow)
(package! dmenu)
(package! elegant-agenda-mode :recipe (:host github :repo "justinbarclay/elegant-agenda-mode"))
(package! google-this)
(package! google-translate)
(package! helpful)
(package! jest)
(package! key-chord)
;; (package! mathpix :recipe (:host github :repo "jethrokuan/mathpix"))
(package! nano-emacs
:recipe (:host github :repo "rougier/nano-emacs"))
(package! org-cliplink)
(package! org-download)
(package! org-drill)
(package! org-gcal)
(package! org-noter)
(package! org-recoll :recipe (:host github :repo "alraban/org-recoll"))
(package! org-ref)
(package! org-roam :recipe (:host github :repo "jethrokuan/org-roam"))
(package! org-roam-bibtex)
(package! org-roam-server)
(package! org-super-agenda)
(package! phi-search)
(package! ob-rust)
(package! real-auto-save)
(package! smartscan)
(package! toc-org)
(package! undo-tree)
(package! wrap-region)
(package! almost-mono-themes)
;; Julia
(package! julia-mode :pin "1c122f1dff")
(package! julia-snail)
;; Use the branch that supports the vterm backend
;; https://github.com/tpapp/julia-repl/pull/84
(package! julia-repl
:recipe (:host github :repo "tpapp/julia-repl" :branch "tp/terminal-backends"))
(package! julia-formatter
:recipe (:host github :repo "ki-chi/julia-formatter"))
;; (package! eglot-jl)