GNU Emacs is an extendable and highly customisable text editor. It is based on an Emacs Lisp interpreter with extensions for text editing. Emacs has been extended in essentially all areas of computing, giving rise to a vast array of packages supporting, e.g., email, IRC and XMPP messaging, spreadsheets, remote server editing, and much more. Emacs includes extensive documentation on all aspects of the system, from basic editing to writing large Lisp programs. It has full Unicode support for nearly all human languages.
cd ~/.doom.d/
- Install the necessary programs when you inevitably get lots of errors!
- Waste lots of time learning how to use emacs, tweaking the configuration yourself, get frustrated, and ultimately return to using vim or VScode
I think this has to go at the top of the file (almost like a shebang)
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
- I’d much rather have my new buffers in org-mode than fundamental-mode
- Nicer default buffer names
- Only include the buffer name in the title, and then if applicable, the project folder
- Also mark if the file has unsaved changes
;; (setq-default major-mode 'org-mode)
(setq doom-fallback-buffer-name "► Doom"
+doom-dashboard-name "► Doom")
(setq frame-title-format
'(""
(:eval
(if (s-contains-p org-roam-directory (or buffer-file-name ""))
(replace-regexp-in-string
".*/[0-9]*-?" "☰ "
(subst-char-in-string ?_ ? buffer-file-name))
"%b"))
(:eval
(let ((project-name (projectile-project-name)))
(unless (string= "-" project-name)
(format (if (buffer-modified-p) " ◉ %s" " ● %s") project-name))))))
Set a keybinding for the Emacs calculator
(map! :leader
(:prefix ("a" . "Calculator")
:desc "Calculator" "c" #'calc
:desc "Reset" "R" #'calc-reset))
For now the ChatGPT API is paid for so I cannot use these but I’ll keep the configuration here
Lazy load API key
(setq chatgpt-shell-openai-key
(lambda ()
(auth-source-pick-first-password :host "api.openai.com")))
Add a querying keybind for the chatgpt package I’m using
(use-package! org-ai
:commands
(org-ai-mode
org-ai-global-mode)
:init
(add-hook 'org-mode-hook #'org-ai-mode) ; enable org-ai in org-mode
(org-ai-global-mode) ; installs global keybindings on C-c M-a
:config
(setq org-ai-default-chat-model "gpt-4o") ; if you are on the gpt-4 beta:
(org-ai-install-yasnippets)) ; if you are using yasnippet and want `ai` snippets
;; Use the default bindings but change the leader
;; (map! :leader
;; :prefix ("a" . "ai")
;; :desc "Start on project" "p" #'org-ai-on-project
;; :desc "Open prompt" "P" #'org-ai-prompt-in-new-buffer
;; :desc "AI on region" "r" #'org-ai-on-region
;; :desc "Refactor code" "c" #'org-ai-refactor-code
;; :desc "Summarise marked text" "s" #'org-ai-summarize
;; :desc "Switch chat model" "m" #'org-ai-switch-chat-model
;; :desc "URL request buffer" "!" #'org-ai-open-request-buffer
;; :desc "Account usage" "$" #'org-ai-open-account-usage-page
;; :desc "Speech input" "t" #'org-ai-talk-input-toggle
;; :desc "Speech output" "T" #'org-ai-talk-output-toggle
;; :desc "Read region" "R" #'org-ai-talk-read-region
;; :desc "Mark prompt at point" "SPC" #'org-ai-mark-region-at-point))
I like to have custom delays for company mode, the leader key, and the spell checker:
(setq which-key-idle-delay 0.2)
(setq company-idle-delay 0.3
company-maximum-prefix-length 3)
(after! spell-fu
(setq spell-fu-idle-delay 0.5))
(setq deft-directory "~/Documents/deft/")
(use-package! ellama
:defer t
:init
(setopt ellama-keymap-prefix "C-c e"))
Enable inline evaluation of code, but use a nicer prefix
(setq eros-eval-result-prefix "⟹ ") ; default =>
When I want to make a substitution, I want it to be global more often than not — so let’s make that the default.
(after! evil
(setq evil-ex-substitute-global t ; I like my s/../.. to by global by default
evil-move-cursor-back nil ; Don't move the block cursor when toggling insert mode
evil-kill-on-visual-paste nil)) ; Don't put overwritten text in the kill ring
Integrate books into emacs
(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
- Let Emacs know I am using fish as my default shell
- Delete files to trash
- Stretch cursor to the glyph width
- Raise undo limit to 80MB
- Whether actions are undone in several steps
- Nobody likes to lose work
- How many seconds passwords are cached
- Controls if scroll commands move point to keep its screen position unchanged
- Number of lines of margin at the top and bottom of a window
- Show traceback on error
- Iterate through CamelCase words
- Replace I-search binding with swiper
- Include a ‘leader-undo’ button
- Visual fix for indent guides
- Disable massive toolbar on MacOS
(setq shell-file-name (executable-find "bash"))
(setq vterm-shell (executable-find "fish"))
(setq explicit-shell-file-name (executable-find "fish"))
(setq delete-by-moving-to-trash t
x-stretch-cursor t)
(setq undo-limit 80000000
evil-want-fine-undo t
auto-save-default t
password-cache-expiry 300
scroll-preserve-screen-position 'always
scroll-margin 4)
;; debug-on-error t)
(global-subword-mode t)
(map! "C-s" #'swiper)
(map! "C-M-s" #'swiper-thing-at-point)
(map! "C-S-s" #'isearch-forward-regexp)
(map! "C-S-r" #'isearch-backward-regexp)
;; TODO
;; (map! which-key-mode-map
;; "DEL" #'which-key-undo)
;; (after! highlight-indent-guides
;; (highlight-indent-guides-auto-set-faces))
(when (string= (system-name) "maccie")
(add-hook 'doom-after-init-hook (lambda () (tool-bar-mode 1) (tool-bar-mode 0))))
(use-package! gptel
:commands gptel gptel-menu gptel-mode gptel-send gptel-set-tpic
:config
;; (setq! gptel-api-key "your key"))
(setq gptel-model "zephyr:latest"
gptel-backend (gptel-make-ollama "Ollama"
:host "localhost:11434"
:stream t
:models '("zephyr:latest"))))
(add-hook 'gptel-post-stream-hook 'gptel-auto-scroll)
(add-hook 'gptel-post-response-functions 'gptel-end-oF-response) ; TODO Bind key to end of response
This determines the style of line numbers in effect. If set to `nil’, line numbers are disabled. For relative line numbers, set this to `relative’.
Automatically wrap text when it reaches the end of the screen
(setq display-line-numbers-type 'relative)
(add-hook 'text-mode-hook 'turn-on-visual-line-mode)
(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow))
;; (setq-default auto-fill-function 'do-auto-fill)
Some functionality uses this to identify you, e.g. GPG configuration, email clients, file templates, and snippets.
Set the GPG directories and increase the cache expiry
(setq user-full-name "Dylan Morgan"
user-mail-address "dbmorgan98@gmail.com")
(setq auth-sources '("~/.authinfo.gpg" "authinfo")
auth-source-cache-expiry 21600) ; Change default to 6 hours to get me through most of a work day
Change the default sort order so it lists the most recent files and directories opened first and enable project caching
(setq projectile-sort-order 'recentf
projectile-auto-discover t)
(setq projectile-enable-caching t)
(setq projectile-file-exists-remote-cache-expire (* 10 60))
(map! :leader
(:prefix-map ("p" . "project")
:desc "Search project rg" "h" #'counsel-projectile-rg))
(map! :leader
(:prefix-map ("p" . "project")
:desc "Search project ag" "H" #'counsel-projectile-ag))
My spelling is really bad so it needs checkling
(after! spell-fu
(setq ispell-personal-dictionary "~/.config/emacs/.local/etc/ispell/.pws")
(setq ispell-dictionary "en_GB"))
(use-package! jinx
:defer t
:init
(add-hook 'doom-init-ui-hook #'global-jinx-mode)
:config
(setq jinx-languages "en_GB")
;; Extra face(s) to ignore
(push 'org-inline-src-block
(alist-get 'org-mode jinx-exclude-faces)))
;; ;; Take over the relevant bindings.
;; (after! ispell
;; (global-set-key [remap ispell-word] #'jinx-correct))
;; (after! evil-commands
;; (global-set-key [remap evil-next-flyspell-error] #'jinx-next)
;; (global-set-key [remap evil-prev-flyspell-error] #'jinx-previous))
Use emacs as a client.
Setup the systemd file here
#+name emacsclient service
[Unit]
Description=Emacs server daemon
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
Wants=gpg-agent.service
[Service]
Type=forking
ExecStart=fish -c '/home/dylanmorgan/Applications/emacs-29.3/build/src/emacs --daemon && /home/dylanmorgan/Applications/emacs-29.3/build/lib-src/emacsclient -c --eval "(delete-frame)"'
ExecStop=/home/dylanmorgan/Applications/emacs-29.3/build/lib-src/emacsclient --no-wait --eval "(progn (setq kill-emacs-hook nil) (kill emacs))"
Environment=COLORTERM=truecolor
Restart=on-failure
[Install]
WantedBy=default.target
which is then enabled by
systemctl --user enable emacs.service
For some reason if a frame isn’t opened early in the initialisation process, the daemon doesn’t seem to like opening frames later — hence the && emacsclient
part of the ExecStart
value.
It can now be nice to use this as a ‘default app’ for opening files. If we add an appropriate desktop entry, and enable it in the desktop environment.
[Desktop Entry]
Name=Emacs client
GenericName=Text Editor
Comment=A flexible platform for end-user applications
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=/home/dylanmorgan/Applications/emacs-29.3/build/lib-src/emacsclient -create-frame --alternate-editor="" --no-wait %F
Icon=emacs
Type=Application
Terminal=false
Categories=TextEditor;Utility;
StartupWMClass=Emacs
Keywords=Text;Editor;
X-KDE-StartupNotify=false
Lastly, while I’m not sure quite why it happens, but after a bit it seems that new Emacsclient frames start on the *scratch*
buffer instead of the dashboard. I prefer the dashboard, so let’s ensure that’s always switched to in new frames.
To enable bidirectional synchronisation of LSP workspace folders and treemacs projects.
(lsp-treemacs-sync-mode 1)
;; (add-hook 'projectile-find-file-hook #'+treemacs/toggle 'append)
;; (add-hook 'projectile-find-file-hook #'treemacs-select-window 'append)
General settings
(use-package! treemacs
:defer t
:config
(progn
(setq treemacs-eldoc-display 'detailed
treemacs-find-workspace-method 'find-for-file-or-pick-first
treemacs-missing-project-action 'remove
treemacs-move-forward-on-expand t
treemacs-project-follow-cleanup t
treemacs-indent-guide-style 'line
treemacs-recenter-distance 0.2
treemacs-recenter-after-file-follow 'always
treemacs-recenter-after-tag-follow 'always
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'always
treemacs-project-follow-into-home t
treemacs-show-hidden-files nil
treemacs-sorting 'alphabetic-numeric-case-insensitive-asc
treemacs-select-when-already-in-treemacs 'next-or-back
treemacs-tag-follow-delay 1.0
treemacs-width-increment 5)
;; The default width and height of the icons is 22 pixels. If you are
;; using a Hi-DPI display, uncomment this to double the icon size.
;;(treemacs-resize-icons 44)
(treemacs-follow-mode t)
(treemacs-project-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
(treemacs-indent-guide-mode t)
(when treemacs-python-executable
(treemacs-git-commit-diff-mode t))
(pcase (cons (not (null (executable-find "git")))
(not (null treemacs-python-executable)))
(`(t . t)
(treemacs-git-mode 'deferred))
(`(t . _)
(treemacs-git-mode 'simple))))
;; :bind
(map! :nvi "M-0" nil) ; unbind from go to last workspace
(map! "M-0" #'treemacs-select-window))
;; ("C-x t 1" . treemacs-delete-other-windows)
;; ("C-x t t" . treemacs)
;; ("C-x t d" . treemacs-select-directory)
;; ("C-x t B" . treemacs-bookmark)
;; ("C-x t C-t" . treemacs-find-file)
;; ("C-x t M-t" . treemacs-find-tag)))
Faster than the default scp (for small files)
(setq tramp-default-method "ssh")
Improve tramp prompt recognition
(after! tramp
(setenv "SHELL" "/bin/bash")
(setq tramp-shell-prompt-pattern "\\(?:^\\|\n\\|\x0d\\)[^]#$%>\n]*#?[]#$%>] *\\(\e\\[[0-9;]*[a-zA-Z] *\\)*")) ;; default +
Default to opening links in emacs webkit
(setq browse-url-browser-function 'xwidget-webkit-browse-url)
Moom is a package for manipulating the size and location of the actual emacs window. This is particularly useful on my mac.
Firstly, set the default margin
;; (setq moom-user-margin '(50 50 50 50)) ; {top, bottom, left, right}
;; (moom-mode 1)
- Make Doom emacs ask which buffer to see after splitting a window.
- Take new window space from all other windows (not just current).
- Window rotation is nice, and can be found under SPC w r and SPC w R.
- Layout rotation is also nice though. Let’s stash this under SPC w a.
- We could also do with adding the missing arrow-key variants of the window navigation/swapping commands.
- I also like to be able to preview buffers when I switch them.
(setq evil-vsplit-window-right t
evil-split-window-below t)
(defadvice! prompt-for-buffer (&rest _)
:after '(evil-window-split evil-window-vsplit)
(counsel-buffer-or-recentf))
(setq window-combination-resize t)
(map! :map evil-window-map
"SPC" #'rotate-layout
;; Navigation
"<left>" #'evil-window-left
"<down>" #'evil-window-down
"<up>" #'evil-window-up
"<right>" #'evil-window-right
;; Swapping windows
"C-<left>" #'+evil/window-move-left
"C-<down>" #'+evil/window-move-down
"C-<up>" #'+evil/window-move-up
"C-<right>" #'+evil/window-move-right)
;; (map! :map switch-workspace-buffer)
;; (map! :leader
;; (:prefix-map ("," . "Switch buffer")
;; :desc "Search project rg" "h" #'counsel-projectile-rg))
(map! :leader
:desc "Switch buffer" "," #'counsel-switch-buffer
:desc "Switch workspace buffer" "\\" #'persp-switch-to-buffer)
Enable nested snippets
(setq yas-triggers-in-field t)
Smart parentheses
(sp-local-pair
'(org-mode)
"<<" ">>"
:actions '(insert))
Doom exposes five (optional) variables for controlling fonts in Doom. Here are the three important ones:
- `doom-font’
- `doom-variable-pitch-font’
- `doom-big-font’ – used for `doom-big-font-mode’
- use this for presentations or streaming.
They all accept either a font-spec, font string (“Input Mono-12”), or xlfd font string. You generally only need doom-font and doom-variable-pitch-font.
(setq doom-font (font-spec :family "FiraCode Nerd Font" :size 16)
doom-big-font (font-spec :family "FiraCode Nerd Font" :size 22))
;; doom-variable-pitch-font (font-spec :family "InputMonoNarrow Nerd Font" :size 18))
;; doom-serif-font (font-spec :family "Droid*Sans*M*" :size 16 :weight 'light))
Use LaTeX as the default input method to type special characters
(after! text-mode
(set-input-method 'TeX))
Disable prettify symbols globally
(setq global-prettify-symbols-mode nil)
Display the minimap (doesn’t work well with org files \therefore disabled)
;; (setq minimap-mode 0)
Adjust some settings
(display-time-mode 1) ; Show the time
(size-indication-mode 1) ; Info about what's going on
(setq display-time-default-load-average nil) ; Hide the load average
(setq all-the-icons-scale-factor 1.2) ; prevent the end of the modeline from being cut off
Alter the colour of the filename in the buffer when modifications have been made to a file
(custom-set-faces!
'(doom-modeline-buffer-modified :foreground "orchid2"))
Conditionally hide the encoding
(defun doom-modeline-conditional-buffer-encoding ()
"We expect the encoding to be LF UTF-8, so only show the modeline when this is not the case"
(setq-local doom-modeline-buffer-encoding
(unless (and (memq (plist-get (coding-system-plist buffer-file-coding-system) :category)
'(coding-category-undecided coding-category-utf-8))
(not (memq (coding-system-eol-type buffer-file-coding-system) '(1 2))))
t)))
(add-hook 'after-change-major-mode-hook #'doom-modeline-conditional-buffer-encoding)
Alter the modeline for viewing PDFs
(after! doom-modeline
(doom-modeline-def-segment buffer-name
"Display the current buffer's name, without any other information."
(concat
(doom-modeline-spc)
(doom-modeline--buffer-name)))
(doom-modeline-def-segment pdf-icon
"PDF icon from all-the-icons."
(concat
(doom-modeline-spc)
(doom-modeline-icon 'octicon "file-pdf" nil nil
:face (if (doom-modeline--active)
'all-the-icons-red
'mode-line-inactive)
:v-adjust 0.02)))
(defun doom-modeline-update-pdf-pages ()
"Update PDF pages."
(setq doom-modeline--pdf-pages
(let ((current-page-str (number-to-string (eval `(pdf-view-current-page))))
(total-page-str (number-to-string (pdf-cache-number-of-pages))))
(concat
(propertize
(concat (make-string (- (length total-page-str) (length current-page-str)) ? )
" P" current-page-str)
'face 'mode-line)
(propertize (concat "/" total-page-str) 'face 'doom-modeline-buffer-minor-mode)))))
(doom-modeline-def-segment pdf-pages
"Display PDF pages."
(if (doom-modeline--active) doom-modeline--pdf-pages
(propertize doom-modeline--pdf-pages 'face 'mode-line-inactive)))
(doom-modeline-def-modeline 'pdf
'(bar window-number pdf-pages pdf-icon buffer-name)
'(misc-info matches major-mode process vcs)))
- Change this to an SVG image
Not to toot my own trumpet, but I like this pretty cool splash screen that I made
(setq fancy-splash-image "~/.doom.d/splash/black-doom-hole.png")
(after! centaur-tabs
(centaur-tabs-mode -1)
(setq centaur-tabs-set-icons t
;; centaur-tabs-style "wave"
;; centaur-tabs-set-modified-marker t
centaur-tabs-modified-marker "o"
centaur-tabs-close-button "×"
centaur-tabs-set-bar 'left
centaur-tabs-gray-out-icons 'buffer))
;; (centaur-tabs-change-fonts "P22 Underground Book" 160))
;; (setq x-underline-at-descent-line t)
There are two ways to load a theme. Both assume the theme is installed and available. You can either set `doom-theme’ or manually load a theme with the `load-theme’ function. The default is doom-one.
I’ve found a few themes I like, so here we will load a random one on opening emacs
Also add blur and opacity (blur doesn’t work)
;; (use-package autothemer
(defun random-choice (items)
(let* ((size (length items))
(index (random size)))
(nth index items)))
(setq random-theme (random-choice '(doom-dracula doom-palenight doom-one)))
;; (setq random-theme (random-choice '(doom-dracula doom-snazzy doom-palenight doom-moonlight doom-vibrant doom-laserwave doom-horizon doom-one doom-city-lights doom-wilmersdorf catppuccin-1 catppuccin-2))) ; doom-tokyo-night)))
(cond ((string= random-theme "catppuccin-1") (setq doom-theme 'catppuccin-macchiato))
((string= random-theme "catppuccin-2") (setq doom-theme 'catppuccin-frappe))
(t (setq doom-theme random-theme)))
;; (set-frame-parameter (selected-frame) 'alpha '(85 . 50))
;; (add-to-list 'default-frame-alist '(alpha . (85 . 50)))
(doom/set-frame-opacity 100)
;; (doom/set-frame-opacity 95)
;; (doom/set-frame-opacity 85)
Firstly unbind aya-create from C-TAB
(map! :nvi "C-TAB" nil)
(map! :nvi "C-<tab>" nil)
Then define the keybindings to use for Github copilot
;; accept completion from copilot and fallback to company
(use-package! copilot
:defer t
:hook ((prog-mode . copilot-mode)
(sh-mode . copilot-mode))
:bind (:map copilot-completion-map
("C-S-<iso-lefttab>" . 'copilot-accept-completion-by-word)
("C-S-<tab>" . 'copilot-accept-completion-by-word)
("C-TAB" . 'copilot-accept-completion-by-line)
("C-<tab>" . 'copilot-accept-completion-by-line)
("C-M-TAB" . 'copilot-accept-completion)
("C-M-<tab>" . 'copilot-accept-completion)))
;; :config
;; (when (string= (system-name) "apollo")
;; (setq copilot-node-executable "~/.local/share/nvm/v17.9.1/bin/node"))
;; (when (string= (system-name) "maccie")
;; (setq copilot-node-executable "/Users/dylanmorgan/.local/share/nvm/v17.9.1/bin/node")))
(map! :leader
:desc "Toggle Copilot" "c g" #'copilot-mode)
Better syntax highlighting for code
(add-hook! 'prog-mode-hook #'rainbow-delimiters-mode)
(add-hook! 'sh-mode-hook #'rainbow-delimiters-mode)
Enable word wrapping almost everywhere
(+global-word-wrap-mode +1)
;; (add-hook! 'prog-mode-hook #'+word-wrap-mode)
;; (add-hook! 'sh-mode-hook #'+word-wrap-mode)
Setup lsp-docker
;; Uncomment the next line if you are using this from source
;; (add-to-list 'load-path "<path-to-lsp-docker-dir>")
;; (require 'lsp-docker)
;; (defvar lsp-docker-client-packages
;; '(lsp-css lsp-clients lsp-bash lsp-go lsp-html lsp-typescript ; ruff-lsp
;; lsp-terraform lsp-clangd))
;; (setq lsp-docker-client-configs
;; '((:server-id bash-ls :docker-server-id bashls-docker :server-command "bash-language-server start")
;; (:server-id clangd :docker-server-id clangd-docker :server-command "clangd")
;; (:server-id css-ls :docker-server-id cssls-docker :server-command "css-languageserver --stdio")
;; ;; (:server-id dockerfile-ls :docker-server-id dockerfilels-docker :server-command "docker-langserver --stdio")
;; (:server-id gopls :docker-server-id gopls-docker :server-command "gopls")
;; (:server-id html-ls :docker-server-id htmls-docker :server-command "html-languageserver --stdio")))
;; ;; (:server-id ruff-lsp :docker-server-id pyls-docker :server-command "pyls")))
;; ;; (:server-id ts-ls :docker-server-id tsls-docker :server-command "typescript-language-server --stdio")))
;; (require 'lsp-docker)
;; (lsp-docker-init-clients
;; :path-mappings '(("path-to-projects-you-want-to-use" . "~/Programming/projects /"))
;; :client-packages lsp-docker-client-packages
;; :client-configs lsp-docker-client-configs)
Add keybindings to push to remote and view diffs.
(map! :leader
:desc "Magit pull" "g p" #'magit-pull
:desc "Magit push" "g P" #'magit-push
:desc "Magit diff" "g d" #'magit-diff
:desc "Magit stash" "g z" #'magit-stash
:desc "Magit stage all" "g a" #'magit-stage-modified
:desc "Magit unstage all" "g A" #'magit-unstage-all)
Always use the bash shell for scripting
(after! sh-mode
(sh-set-shell "bash"))
;; (when (equal (string-match-p (regexp-quote "*PKGBUILD")
;; (buffer-file-name))
;; "PKGBUILD")
;; (sh-set-shell "bash")))
Set default tab width to 2:
(after! sh-mode
(setq sh-indentation
sh-basic-offset 2))
Set indentation for fortran and f90
(after! f90
(setq f90-do-indent 2)
(setq f90-if-indent 2)
(setq f90-type-indent 2)
(setq f90-program-indent 2)
(setq f90-continuation-indent 4)
(setq f90-smart-end 'blink)
;; TODO: copy rc params file from apollo to mac
(set-formatter! 'fprettify '("fprettify" "-i 2" "-l 88" "-w 4" "--whitespace-comma=true" "--whitespace-assignment=true" "--whitespace-decl=true" "--whitespace-relational=true" "--whitespace-plusminus=true" "--whitespace-multdiv=true" "--whitespace-print=true" "--whitespace-type=true" "--whitespace-intrinsics=true" "--strict-indent" "--enable-decl" "--enable-replacements" "--c-relations" "--case 1 1 1 1" "--strip-comments" "--disable-fypp") :modes '(f90-mode fortran-mode)))
(after! fortran
(setq fortran-continuation-string "&")
(setq fortran-do-indent 2)
(setq fortran-if-indent 2)
(setq fortran-structure-indent 2)
(set-formatter! 'fprettify '("fprettify" "-i 2" "-l 88" "-w 4" "--whitespace-comma=true" "--whitespace-assignment=true" "--whitespace-decl=true" "--whitespace-relational=true" "--whitespace-plusminus=true" "--whitespace-multdiv=true" "--whitespace-print=true" "--whitespace-type=true" "--whitespace-intrinsics=true" "--strict-indent" "--enable-decl" "--enable-replacements" "--c-relations" "--case 1 1 1 1" "--strip-comments" "--disable-fypp") :modes '(f90-mode fortran-mode)))
Set Fortran and Fortran 90 mode for appropriate extensions
(setq auto-mode-alist
(cons '("\\.F90$" . f90-mode) auto-mode-alist))
(setq auto-mode-alist
(cons '("\\.f90$" . f90-mode) auto-mode-alist))
(setq auto-mode-alist
(cons '("\\.pf$" . f90-mode) auto-mode-alist))
(setq auto-mode-alist
(cons '("\\.pf$" . f90-mode) auto-mode-alist))
(setq auto-mode-alist
(cons '("\\.fpp$" . f90-mode) auto-mode-alist))
(setq auto-mode-alist
(cons '("\\.F$" . fortran-mode) auto-mode-alist))
(setq auto-mode-alist
(cons '("\\.f$" . fortran-mode) auto-mode-alist))
(use-package! lsp-mode
:hook (f90-mode . lsp-deferred))
Automatically start when opening a julia file
;; (use-package! eglot-jl
;; :defer t)
(use-package! julia-mode
:defer t
:init
(setenv "JULIA_NUM_THREADS" "6")
:interpreter ("julia" . julia-mode))
;; :config
;; (add-hook 'julia-mode-hook 'eglot-jl-init)
;; (add-hook 'julia-mode-hook 'eglot-ensure))
(add-hook! 'julia-mode-hook #'lsp-mode)
Julia-lsp doesn’t work without this
(after! julia-mode
(add-hook 'julia-mode-hook #'rainbow-delimiters-mode-enable)
(add-hook! 'julia-mode-hook
(setq-local lsp-enable-folding t
lsp-folding-range-limit 100)))
Change directory for LanguageServer.jl and SymbolServer.jl
;; (use-package! lsp-julia
;; :config)
;; (setq lsp-julia-default-environment "~/.julia/environments/v1.8"))
(setq! bibtex-completion-bibliography '("~/Documents/warwick/thesus/references.bib"))
Set new environments for:
- Non-numbered equations
- Non-numbered equations with bmatrix
Then, set shortcuts for these environments
Also make some additions/modifications to the maths symbol alist
(eval-after-load 'latex
'(define-key LaTeX-mode-map [(tab)] 'cdlatex-tab))
(after! tex-mode
(setq cdlatex-env-alist
'(("non-numbered equation" "\\begin{equation*}\n ?\n\\end{equation*}" nil)
("equation" "\\begin{equation} \\label{?}\n \n\\end{equation}" nil) ; This might not work
("bmatrix" "\\begin{equation*}\n ?\n \\begin{bmatrix}\n \n \\end{bmatrix}\n\\end{equation*}" nil)
("vmatrix" "\\begin{equation*}\n ?\n \\begin{vmatrix}\n \n \\end{vmatrix}\n\\end{equation*}" nil)
("pmatrix" "\\begin{equation*}\n ?\n \\begin{pmatrix}\n \n \\end{pmatrix}\n\\end{equation*}" nil)
("split" "\\begin{equation} \\label{?}\n \\begin{split}\n \n \\end{split}\n\\end{equation}" nil)
("non-numbered split" "\\begin{equation*}\n \\begin{split}\n ?\n \\end{split}\n\\end{equation*}" nil)))
(setq cdlatex-command-alist
'(("neq" "Insert non-numbered equation env" "" cdlatex-environment ("non-numbered equation") t nil)
("equ" "Insert numbered equation env" "" cdlatex-environment ("equation") t nil) ; This might not work
("bmat" "Insert bmatrix env" "" cdlatex-environment ("bmatrix") t nil)
("vmat" "Insert vmatrix env" "" cdlatex-environment ("vmatrix") t nil)
("pmat" "Insert pmatrix env" "" cdlatex-environment ("pmatrix") t nil)
("spl" "Insert split env" "" cdlatex-environment ("split") t nil)
("nspl" "Insert non-numbered split env" "" cdlatex-environment ("non-numbered split") t nil)))
(setq cdlatex-math-symbol-alist
'((?= ("\\equiv" "\\leftrightarrow" "\\longleftrightarrow"))
(?! ("\\neq"))
(?+ ("\\cup" "\\pm"))
(?^ ("\\uparrow" "\\downarrow"))
(?: ("\\cdots" "\\vdots" "\\ddots"))
(?b ("\\beta" "\\mathbb{?}"))
(?i ("\\in" "\\implies" "\\imath"))
(?I ("\\int" "\\Im"))
(?F ("\\Phi"))
(?P ("\\Pi" "\\propto"))
(?Q ("\\Theta" "\\quad" "\\qquad"))
(?S ("\\Sigma" "\\sum" "\\arcsin"))
(?t ("\\tau" "\\therefore" "\\tan"))
(?T ("\\times" "" "\\arctan"))
(?V ())
(?/ ("\\frac{?}{}" "\\not")) ;; Normal fr command doesn't work properly
(?< ("\\leq" "\\ll" "\\longleftarrow"))
(?> ("\\geq" "\\gg" "\\longrightarrow"))
(?$ ("\\leftarrow" "" ""))
(?% ("\\rightarrow" "" "")))))
Enable a company completion back-end for LaTeX maths symbols
(add-to-list 'company-backends 'company-math-symbols-unicode)
Use LuaLaTeX with LaTeXMK
(setq TeX-command-extra-options "-lualatex -pdflua")
(after! tex-mode
(setq-default TeX-master nil))
Set the lsp servers for use in latex mode
(use-package! lsp-ltex
;; :hook (text-mode . (lambda ()
;; require 'lsp-ltex
;; (lsp)))
:hook (latex-mode . lsp-deferred)
:init
(setq lsp-ltex-version (gethash "ltex-ls" (json-parse-string (shell-command-to-string "ltex-ls -V")))
lsp-ltex-server-store-path nil
lsp-ltex-language "en-GB"
lsp-ltex-mother-tongue "en-GB"
lsp-ltex-completion-enabled t)
:config
(set-lsp-priority! 'ltex-ls 2))
(after! tex-mode
;; When on mac
(when (string= (system-name) "maccie")
(add-to-list 'load-path "/opt/homebrew/bin/texlab")
(setq lsp-latex-texlab-executable "/opt/homebrew/bin/texlab"))
;; When on arch
(when (string= (system-name) "arch")
(add-to-list 'load-path "/usr/bin/texlab")
(setq lsp-latex-texlab-executable "/usr/bin/texlab"))
(with-eval-after-load "tex-mode"
(add-hook 'tex-mode-hook 'lsp)
(add-hook 'latex-mode-hook 'lsp))
(with-eval-after-load "bibtex"
(add-hook 'bibtex-mode-hook 'lsp)))
Set the default bibliography location
(setq reftex-default-bibliography "~/Documents/warwick/thesus/references.bib")
Change the default method of adding/searching for citations with reftex
(map! :map reftex-mode-map
:localleader
:desc "reftex-cite" "r" #'reftex-citation
:desc "reftex-label" "l" #'reftex-label)
Use the zotra-server backend
(after! tex-mode
(setq zotra-backend 'zotra-server)
(setq zotra-local-server-directory "~/Applications/zotra-server/"))
Configure general settings for LSP
(after! lsp-mode
(setq lsp-enable-symbol-highlighting t
lsp-lens-enable t
lsp-headerline-breadcrumb-enable t
lsp-modeline-code-actions-enable t
lsp-modeline-diagnostics-enable t
lsp-diagnostics-provider :auto
lsp-eldoc-enable-hover t
lsp-completion-provider :auto
lsp-completion-show-detail t
lsp-completion-show-kind t
lsp-signature-mode t
lsp-signature-auto-activate t
lsp-signature-render-documentation t
lsp-idle-delay 1.0))
Configure lsp-ui settings
(after! lsp-mode
(setq lsp-ui-sideline-enable t
;; lsp-ui-sideline-mode 1
lsp-ui-sideline-delay 1
lsp-ui-sideline-show-symbol t
lsp-ui-sideline-show-diagnostics t
lsp-ui-sideline-show-hover t
lsp-ui-sideline-show-code-actions t
lsp-ui-sideline-update-mode 'point
lsp-ui-peek-enable t
lsp-ui-peek-show-directory t
lsp-ui-doc-enable t
;; lsp-ui-doc-frame-mode t ; This breaks 'q' for some reason
lsp-ui-doc-delay 1
lsp-ui-doc-show-with-cursor t
lsp-ui-doc-show-with-mouse t
lsp-ui-doc-header t
lsp-ui-doc-use-childframe t
lsp-ui-doc-position 'top
lsp-ui-doc-max-height 25
lsp-ui-doc-use-webkit t
lsp-ui-imenu-enable t
lsp-ui-imenu-kind-position 'left
lsp-ui-imenu-buffer-position 'right
lsp-ui-imenu-window-width 35
lsp-ui-imenu-auto-refresh t
lsp-ui-imenu-auto-refresh-delay 1.0)
(map! :map lsp-ui-mode-map "C-," #'lsp-ui-doc-focus-frame)
(map! :map lsp-ui-mode-map "C-;" #'lsp-ui-sideline-execute-code-action))
;; (map! :after lsp-mode
;; :map lsp-mode-map
;; :leader
;; :prefix ("#" . "custom")
;; :prefix ("# l" . "lsp")
;; :desc "open imenu"
;; "i" #'lsp-ui-imenu
;; "I" #'lsp-ui-imenu--refresh)
Enable the DAP debugger
(after! dap-mode
(setq dap-python-debugger 'debugpy))
(map! :after dap-mode
:map dap-mode-map
:leader
:prefix ("d" . "dap")
;; basics
:desc "dap next" "n" #'dap-next
:desc "dap step in" "i" #'dap-step-in
:desc "dap step out" "o" #'dap-step-out
:desc "dap continue" "c" #'dap-continue
:desc "dap hydra" "h" #'dap-hydra
:desc "dap debug restart" "r" #'dap-debug-restart
:desc "dap debug" "s" #'dap-debug
;; debug
:prefix ("dd" . "Debug")
:desc "dap debug recent" "r" #'dap-debug-recent
:desc "dap debug last" "l" #'dap-debug-last
;; eval
:prefix ("de" . "Eval")
:desc "eval" "e" #'dap-eval
:desc "eval region" "r" #'dap-eval-region
:desc "eval thing at point" "s" #'dap-eval-thing-at-point
:desc "add expression" "a" #'dap-ui-expressions-add
:desc "remove expression" "d" #'dap-ui-expressions-remove
:prefix ("db" . "Breakpoint")
:desc "dap breakpoint toggle" "b" #'dap-breakpoint-toggle
:desc "dap breakpoint condition" "c" #'dap-breakpoint-condition
:desc "dap breakpoint hit count" "h" #'dap-breakpoint-hit-condition
:desc "dap breakpoint log message" "l" #'dap-breakpoint-log-message)
Github has a rate limit, limiting how long grip-mode will work for. The following should get around this. This also uses a github authentication token and parses it from a file stored in this directory so it doesn’t get made public when I publish this to github.
(after! grip-mode
(setq grip-github-user "grip-github-user")
(setq grip-github-password (substring
(with-temp-buffer
(insert-file-contents "~/.doom.d/grip_pw.txt")
(buffer-string)) 0 -1)))
Use visual line wrapping
(add-hook! (gfm-mode markdown-mode) #'visual-line-mode #'turn-off-auto-fill)
Automatically open live preview when opening a markdown file
(after! markdown-mode
;; (add-hook! 'markdown-mode-hook #'grip-mode)
(setq grip-sleep-time 2
grip-preview-use-webkit t)
(when (string= (system-name) "arch")
(setq grip-binary-path "/usr/bin/grip"))
(when (string= (system-name) "maccie")
(setq grip-binary-path "/opt/homebrew/bin/grip")))
Mirror the style that markdown renders in
(custom-set-faces!
'(markdown-header-face-1 :height 1.25 :weight extra-bold :inherit markdown-header-face)
'(markdown-header-face-2 :height 1.15 :weight bold :inherit markdown-header-face)
'(markdown-header-face-3 :height 1.08 :weight bold :inherit markdown-header-face)
'(markdown-header-face-4 :height 1.00 :weight bold :inherit markdown-header-face)
'(markdown-header-face-5 :height 0.90 :weight bold :inherit markdown-header-face)
'(markdown-header-face-6 :height 0.75 :weight extra-bold :inherit markdown-header-face))
;; (use-package! obsidian
;; :ensure t
;; :demand t
;; :custom
;; ;; This directory will be used for `obsidian-capture' if set.
;; (obsidian-inbox-directory "inbox")
;; ;; Create missing files in inbox? - when clicking on a wiki link
;; ;; t: in inbox, nil: next to the file with the link
;; ;; default: t
;; ;(obsidian-wiki-link-create-file-in-inbox nil)
;; ;; The directory for daily notes (file name is YYYY-MM-DD.md)
;; (obsidian-daily-notes-directory "daily_notes")
;; ;; Directory of note templates, unset (nil) by default
;; ;(obsidian-templates-directory "Templates")
;; ;; Daily Note template name - requires a template directory. Default: Daily Note Template.md
;; ;(setq obsidian-daily-note-template "Daily Note Template.md")
;; :config
;; (obsidian-specify-path "~/Documents/obsidian/")
;; ;; Activate detection of Obsidian vault
;; (global-obsidian-mode t)
;; (map! :map obsidian-mode-map
;; :localleader
;; :prefix ("O" . "Obsidian")
;; ;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding.
;; :desc "follow link" "o" #'obsidian-follow-link-at-point
;; ;; Jump to backlinks
;; :desc "backlink jump" "b" #'obsidian-backlink-jump
;; :desc "insert link" "l" #'obsidian-insert-wikilink
;; ;; If you prefer you can use `obsidian-insert-link'
;; :desc "insert wikilink" "w" #'obsidian-insert-wikilink
;; ;; Open a note
;; :desc "jump" "j" #'obsidian-jump
;; ;; Capture a new note in the inbox
;; :desc "capture" "c" #'obsidian-capture
;; ;; Create a daily note
;; :desc "daily note" #'obsidian-daily-note)
Disable prettify-symbols in python modes
(after! python
(prettify-symbols-mode -1))
(use-package! python-black
:after python
:config
(add-hook! 'python-mode-hook #'python-black-on-save-mode)
(map! :map python-mode-map
:localleader
:prefix ("b" . "black")
:desc "blacken buffer" "b" #'python-black-buffer
:desc "blacken region" "r" #'python-black-region
:desc "blacken statement" "s" #'python-black-statement))
(setq-hook! 'python-mode-hook +format-with-lsp nil)
;; (use-package! lsp-mode
;; :hook (python-mode . lsp-deferred)
;; ;; :commands lsp-deferred
;; :custom
;; (lsp-ruff-lsp-ruff-path ["usr/bin/ruff server"])
;; (lsp-ruff-lsp-ruff-args ["–-config /home/dylanmorgan/.config/ruff/ruff.toml" "--preview"])
;; ;; (lsp-ruff-lsp-python-path "python")
;; (lsp-ruff-lsp-advertize-fix-all t)
;; (lsp-ruff-lsp-advertize-organize-imports t)
;; (lsp-ruff-lsp-log-level "info")
;; (lsp-ruff-lsp-show-notifications "onError"))
;; TODO when ruff formatting leaves alpha dev
;; (after! python
;; (setf (alist-get 'ruff apheleia-formatters) '("ruff format --config ~/.config/ruff/ruff.toml --target-version py39 -q"
;; (eval (when buffer-file-name
;; (concat "--stdin-filename=" buffer-file-name)))
;; "-"))
;; (setf (alist-get 'python-mode apheleia-mode-alist) '(ruff))
;; (add-hook! 'before-save-hook #'format-with-lsp t)
;; (add-hook! 'before-save-hook #'lsp-organize-imports))
Also add ruff to flycheck
;; (after! flycheck
;; ;; (require 'flycheck)
;; (flycheck-define-checker python-ruff
;; "A Python syntax and style checker using the ruff utility.
;; To override the path to the ruff executable, set
;; `flycheck-python-ruff-executable'.
;; See URL `http://pypi.python.org/pypi/ruff'."
;; :command ("ruff format --config /home/dylanmorgan/.config/ruff/ruff.toml --target-version py312 -q"
;; (eval (when buffer-file-name
;; (concat "--stdin-filename=" buffer-file-name)))
;; "-")
;; :standard-input t
;; :error-filter (lambda (errors)
;; (let ((errors (flycheck-sanitize-errors errors)))
;; (seq-map #'flycheck-flake8-fix-error-level errors)))
;; :error-patterns
;; ((warning line-start
;; (file-name) ":" line ":" (optional column ":") " "
;; (id (one-or-more (any alpha)) (one-or-more digit)) " "
;; (message (one-or-more not-newline))
;; line-end))
;; :modes python-mode)
;; (add-to-list 'flycheck-checkers 'python-ruff)
;; (provide 'flycheck-ruff))
Enable ruff over tramp
;; (lsp-register-client
;; (make-lsp-client
;; :new-connection (lsp-tramp-connection "ruff-lsp")
;; :activation-fn (lsp-activate-on "python")
;; :major-modes '(python-mode)
;; :remote? t
;; :add-on? t
;; :server-id 'ruff-lsp))
(after! lsp-mode
(setq lsp-pyright-disable-language-services nil
lsp-pyright-disable-organize-imports nil
lsp-pyright-auto-import-completions t
lsp-pyright-auto-search-paths t
lsp-pyright-diagnostic-mode "openFilesOnly"
lsp-pyright-log-level "info"
lsp-pyright-typechecking-mode "basic"
lsp-pyright-use-library-code-for-types t
lsp-completion-enable t))
Enable pyright over tramp
;; (lsp-register-client
;; (make-lsp-client
;; :new-connection (lsp-tramp-connection "pyright")
;; :activation-fn (lsp-activate-on "python")
;; :major-modes '(python-mode)
;; :remote? t
;; :add-on? t
;; :server-id 'pyright)
;; :tramp-remote-path )
Loading jupyter instead of emacs-jupyter
;; (use-package jupyter
;; :after (ob-jupyter ob-python)
;; :config
;; (setq jupyter-api-authentication-method 'password)
;; (setq jupyter-eval-use-overlays nil)
;; (setq org-babel-default-header-args:jupyter-python '((:session . "/jpy:localhost#8888:py")
;; (:kernel . "conda-env-edge-py")
;; (:async . "yes")
;; (:pandoc t)))
;; (add-to-list 'savehist-additional-variables 'jupyter-server-kernel-names)
;; (setq ob-async-no-async-languages-alist '("jupyter-python"))
;; (add-to-list 'org-structure-template-alist '("j" . "src jupyter-python")))
;; (advice-add 'request--netscape-cookie-parse :around #'fix-request-netscape-cookie-parse)
(after! ein
(when (string= (system-name) "arch")
(setq ein:jupyter-default-server-command "/home/dylanmorgan/.local/bin/jupyter-lab"))
(when (string= (system-name) "maccie")
(setq ein:jupyter-default-server-command "/opt/homebrew/bin/jupyter-lab")))
(use-package! numpydoc
:after python
:config
(map! :map python-mode-map
:localleader
:desc "numpydoc" "n" #'numpydoc-generate)
;; (setq numpydoc-template-long "")
(setq numpydoc-insertion-style 'yas))
Set keybindings for poetry and disable over tramp
(use-package! poetry
:after python
:hook (python-mode . (lambda ()
(interactive)
(if (file-remote-p default-directory)
(setq package-load-list '(all
(poetry nil))))))
:config
(map! :map python-mode-map
:localleader
:desc "poetry" "p" #'poetry))
(after! rustic
(setq rustic-format-on-save t)
(setq rustic-lsp-server 'rust-analyzer))
;; (add-hook! 'rust-mode-hook #'prettify-symbols-mode)
(after! rustic
(require 'dap-cpptools)
(dap-register-debug-template "Rust::GDB Run Configuration"
(list :type "gdb"
:request "launch"
:name "GDB::Run"
:gdbpath "rust-gdb"
:target nil
:cwd nil)))
Add #+auto_tange: t
to the org header to automatically tangle when a document is saved
Also set a keybinding for this
(defun insert-auto-tangle-tag ()
"Insert auto-tangle tag in a literate config."
(interactive)
(evil-org-open-below 1)
(insert "#+auto_tangle: t ")
(evil-force-normal-state))
(map! :map org-mode-map
:after org-mode
:localleader
:prefix ("j" . "org header")
:desc "auto tangle tag"
"a" 'insert-auto-tangle-tag)
Set filepath for org agenda
;; (setq org-agenda-files '("~/Documents/"))
Make creating org buffers a little easier
(evil-define-command +evil-buffer-org-new (_count file)
"Creates a new ORG buffer replacing the current window, optionally editing a certain FILE"
:repeat nil
(interactive "P<f>")
(if file
(evil-edit file)
(let ((buffer (generate-new-buffer "*new org*")))
(set-window-buffer nil buffer)
(with-current-buffer buffer
(org-mode)
(setq-local doom-real-buffer-p t)))))
(map! :leader
(:prefix "b"
:desc "New empty Org buffer" "o" #'+evil-buffer-org-new))
Quickly take down notes
(setq org-capture-templates
'(("t" "Tasks" entry
(file+headline "" "Inbox")
"* TODO %?\n %U")
("c" "Phone Call" entry
(file+headline "" "Inbox")
"* TODO Call %?\n %U")
("m" "Meeting" entry
(file+headline "" "Meetings")
"* %?\n %U")))
I like to export markdown files written in org as README.org. I’m creating a shortcut to use for this in future.
I also export a lot of org files to markdown so I will also add another shortcut for that command here.
(map! :map org-mode-map
:after org-mode
:localleader
:desc "org-export-to-org"
"E" 'org-org-export-to-org
:desc "org-export-as-md"
"M" 'org-pandoc-export-to-markdown)
Leaving org is sad. Thankfully, there’s a way around this!
- Package installed in packages.el
(use-package! org-pandoc-import
:after org-mode)
Provide different options for default headers for emacs org files
(defun org-literate-config ()
(interactive)
(setq title (read-string "Title: "))
(setq filename (read-string "Original file name: "))
(insert "#+TITLE: " title " \n"
"#+AUTHOR: Dylan Morgan\n"
"#+EMAIL: dbmorgan98@gmail.com\n"
"#+PROPERTY: header-args :tangle " filename "\n"
"#+STARTUP: content\n\n"
"* Table of Contents :toc:\n\n"))
(defun org-header-notes ()
(interactive)
(setq title (read-string "Title: "))
(insert "#+TITLE: " title " \n"
"#+AUTHOR: Dylan Morgan\n"
"#+EMAIL: dbmorgan98@gmail.com\n"
"#+STARTUP: content\n\n"
"* Table of Contents :toc:\n\n"))
(defun org-header-notes-custom-property ()
(interactive)
(setq title (read-string "Title: "))
(setq properties (read-string "Properties: "))
(insert "#+TITLE: " title " \n"
"#+AUTHOR: Dylan Morgan\n"
"#+EMAIL: dbmorgan98@gmail.com\n"
"#+PROPERTY: " properties "\n"
"#+STARTUP: content\n\n"
"* Table of Contents :toc:\n\n"))
(defun org-header-with-readme ()
(interactive)
(setq title (read-string "Title: "))
(insert "#+TITLE: " title " \n"
"#+AUTHOR: Dylan Morgan\n"
"#+EMAIL: dbmorgan98@gmail.com\n"
"#+STARTUP: content\n"
"#+EXPORT_FILE_NAME: ./README.org\n\n"
"* Table of Contents :toc:\n\n"))
(map! :map org-mode-map
:after org-mode
:localleader
:prefix ("j" . "org header")
:desc "literate config"
"l" 'org-literate-config
:desc "note taking"
"n" 'org-header-notes
:desc "notes custom property"
"p" 'org-header-notes-custom-property
:desc "header with readme"
"r" 'org-header-with-readme)
- Default file location
- If you use `org’ and don’t want your org files in the default location below, change `org-directory’. It must be set before org loads!
- It’s convenient to have properties inherited
- Alphabetical lists
- Export processes in external emacs process
- Try to not accidentally do weird stuff in invisible regions
(setq org-directory "~/Documents/org/"
org-use-property-inheritance t
org-list-allow-alphabetical t
org-export-in-background t
org-fold-catch-invisible-edits 'smart)
(use-package! org-special-block-extras
:hook (org-mode . org-special-block-extras-mode))
Show all headings on opening an org file and assign numbers to those headings
(after! org-mode
(setq org-startup-folded 'content
org-startup-numerated nil))
Set plain list indents such that the bullet point style signifies the indentation level
(after! org
(setq org-cycle-include-plain-lists 'integrate)
(setq org-list-demote-modify-bullet '(("+" . "-")
("-" . "+")
("1." . "a.")
("1)" . "a)")))
(setq org-list-use-circular-motion t)
(setq org-list-allow-alphabetical t))
Automatically display images when opening an org file
(after! org-mode
(setq org-startup-with-inline-images t))
Change some of the org keybinding
;; (defun org-insert-newline-heading ()
;; ('newline)
;; ('org-insert-heading))
;; (map! :map org-mode-map
;; :after org
;; :desc "Insert Heading"
;; "M-<return>" 'org-insert-newline-heading)
(map! :map org-mode-map
:after org
:desc "Insert Heading"
"M-<return>" 'org-insert-heading)
Enable cdlatex by default and edit an environment after inserting one.
(after! org-mode
(setq org-startup-with-latex-preview t)
(add-hook! 'org-mode-hook 'turn-on-org-cdlatex)
(defadvice! org-edit-latex-emv-after-insert ()
:after #'org-cdlatex-environment-indent
(org-edit-latex-environment)))
Use org-fragtog mode to automatically generate latex fragments For some reason this doesn’t work on my mac, so I will only enable this for my home computer
Change Latex fragment size
(add-hook! 'org-mode-hook #'org-fragtog-mode)
;; (defun update-org-latex-fragments ()
;; (org-latex-preview '(64))
;; (plist-put org-format-latex-options :background "Transparent" :scale 1.5 text-scale-mode-amount)
;; (org-latex-preview '(16)))
;; (add-hook 'text-scale-mode-hook 'update-org-latex-fragments)
(after! org-mode
'(org-format-latex-options
(quote
(:foreground default :background default :scale 1.5 :html-foreground "Black" :html-background "Transparent" :html-scale 1 :matchers
("begin" "$1" "$" "$$" "\\(" "\\[")))))
Use listing instead of verbatim src blocks
(after! org-mode
(add-to-list 'org-latex-packages-alist '("" "listings"))
(setq org-latex-listings 'listings))
We want fragments to look lovely
(after! org-mode
(setq org-highlight-latex-and-related '(native script entities))
(require 'org-src)
(add-to-list 'org-src-block-faces '("latex" (:inherit default :extend t))))
Make LaTeX fragments look better in text
;; (setq org-format-latex-header "\\documentclass{article}
;; \\usepackage[usenames]{xcolor}
;; \\usepackage[T1]{fontenc}
;; \\usepackage{booktabs}
;; \\pagestyle{empty} % do not remove
;; % The settings below are copied from fullpage.sty
;; \\setlength{\\textwidth}{\\paperwidth}
;; \\addtolength{\\textwidth}{-3cm}
;; \\setlength{\\oddsidemargin}{1.5cm}
;; \\addtolength{\\oddsidemargin}{-2.54cm}
;; \\setlength{\\evensidemargin}{\\oddsidemargin}
;; \\setlength{\\textheight}{\\paperheight}
;; \\addtolength{\\textheight}{-\\headheight}
;; \\addtolength{\\textheight}{-\\headsep}
;; \\addtolength{\\textheight}{-\\footskip}
;; \\addtolength{\\textheight}{-3cm}
;; \\setlength{\\topmargin}{1.5cm}
;; \\addtolength{\\topmargin}{-2.54cm}
;; % my custom stuff
;; \\usepackage{arev}
;; ")
Make background colour transparent
;; (setq org-format-latex-options
;; (plist-put org-format-latex-options :background "Transparent"))
Lets try this stuff from Scimax
(after! org-mode
(defun scimax-org-latex-fragment-justify (justification)
"Justify the latex fragment at point with JUSTIFICATION.
JUSTIFICATION is a symbol for 'left, 'center or 'right."
(interactive
(list (intern-soft
(completing-read "Justification (left): " '(left center right)
nil t nil nil 'left))))
(let* ((ov (ov-at))
(beg (ov-beg ov))
(end (ov-end ov))
(shift (- beg (line-beginning-position)))
(img (overlay-get ov 'display))
(img (and (and img (consp img) (eq (car img) 'image)
(image-type-available-p (plist-get (cdr img) :type)))
img))
space-left offset)
(when (and img
;; This means the equation is at the start of the line
(= beg (line-beginning-position))
(or
(string= "" (s-trim (buffer-substring end (line-end-position))))
(eq 'latex-environment (car (org-element-context)))))
(setq space-left (- (window-max-chars-per-line) (car (image-size img)))
offset (floor (cond
((eq justification 'center)
(- (/ space-left 2) shift))
((eq justification 'right)
(- space-left shift))
(t
0))))
(when (>= offset 0)
(overlay-put ov 'before-string (make-string offset ?\ ))))))
(defun scimax-org-latex-fragment-justify-advice ()
"After advice function to justify fragments."
(scimax-org-latex-fragment-justify (or (plist-get org-format-latex-options :justify) 'left)))
(defun scimax-toggle-latex-fragment-justification ()
"Toggle if LaTeX fragment justification options can be used."
(interactive)
(if (not (get 'scimax-org-latex-fragment-justify-advice 'enabled))
(progn
(advice-add 'org--format-latex-make-overlay :after 'scimax-org-latex-fragment-justify-advice)
(put 'scimax-org-latex-fragment-justify-advice 'enabled t)
(message "Latex fragment justification enabled"))
(advice-remove 'org--format-latex-make-overlay 'scimax-org-latex-fragment-justify-advice)
(put 'scimax-org-latex-fragment-justify-advice 'enabled nil)
(message "Latex fragment justification disabled")))
;; Numbered equations all have (1) as the number for fragments with vanilla
;; org-mode. This code injects the correct numbers into the previews so they
;; look good.
(defun scimax-org-renumber-environment (orig-func &rest args)
"A function to inject numbers in LaTeX fragment previews."
(let ((results '())
(counter -1)
(numberp))
(setq results (cl-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)))
(cl-incf counter)
(cons begin counter))
((string-match "\\\\begin{align}" env)
(prog2
(cl-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
(cl-incf counter (count-matches "\\\\$"))
;; unless there are nonumbers.
(goto-char (point-min))
(cl-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))
(defun scimax-toggle-latex-equation-numbering ()
"Toggle whether LaTeX fragments are numbered."
(interactive)
(if (not (get 'scimax-org-renumber-environment 'enabled))
(progn
(advice-add 'org-create-formula-image :around #'scimax-org-renumber-environment)
(put 'scimax-org-renumber-environment 'enabled t)
(message "Latex numbering enabled"))
(advice-remove 'org-create-formula-image #'scimax-org-renumber-environment)
(put 'scimax-org-renumber-environment 'enabled nil)
(message "Latex numbering disabled.")))
(advice-add 'org-create-formula-image :around #'scimax-org-renumber-environment)
(put 'scimax-org-renumber-environment 'enabled t))
Use the Warwick theme by default
(after! org-beamer-mode
(setq org-beamer-theme "[progressbar=foot]Warwick"))
For more advanced functionality, we can also make presentations using org-present
(defun my/org-present-prepare-slide (buffer-name heading)
(org-overview) ; Show only top-level headlines
(org-show-entry) ; Unfold the current entry
(org-show-children)) ; Show only direct subheadings of the slide but don't expand them
(defun mu/org-present-start ()
;; Tweak font sizes
(setq-local face-remapping-alist '((default (:height 1.5) variable-pitch)
(header-line (:height 4.0) variable-pitch)
(org-document-title (:height 1.75) org-document-title)
(org-code (:height 1.55) org-code)
(org-verbatim (:height 1.55) org-verbatim)
(org-block (:height 1.25) org-block)
(org-block-begin-line (:height 0.7) org-block)))
;; Set a blank header line string to create blank space at the top
(setq header-line-format " ")
;; Display inline images automatically
(org-display-inline-images)
;; Center the presentation and wrap lines
(visual-fill-column-mode 1)
(visual-line-mode 1))
(defun my/org-present-end ()
;; Reset font customizations
(setq-local face-remapping-alist '((default variable-pitch default)))
;; Clear the header line string so that it isn't displayed
(setq header-line-format nil)
;; Stop displaying inline images
(org-remove-inline-images)
;; Stop centering the document
(visual-fill-column-mode 0)
(visual-line-mode 0))
(use-package! org-present
:hook
(org-mode-hook . variable-pitch-mode)
(org-present-mode-hook . my/org-present-start)
(org-present-mode-quit-hook . my/org-present-end)
(org-present-after-navigate-functions . my/org-present-prepare-slide)
:config
;; Set reusable font name variables
(defvar my/fixed-width-font "FiraCode Nerd Font"
"The font to use for monospaced (fixed width) text.")
(defvar my/variable-width-font "Iosevka Aile"
"The font to use for variable-pitch (document) text.")
(set-face-attribute 'default nil :font my/fixed-width-font :weight 'light :height 180)
(set-face-attribute 'fixed-pitch nil :font my/fixed-width-font :weight 'light :height 190)
(set-face-attribute 'variable-pitch nil :font my/variable-width-font :weight 'light :height 1.3)
;; Load org-faces to make sure we can set appropriate faces
(require 'org-faces)
;; Resize Org headings
(dolist (face '((org-level-1 . 1.2)
(org-level-2 . 1.1)
(org-level-3 . 1.05)
(org-level-4 . 1.0)
(org-level-5 . 1.1)
(org-level-6 . 1.1)
(org-level-7 . 1.1)
(org-level-8 . 1.1)))
(set-face-attribute (car face) nil :font my/variable-width-font :weight 'medium :height (cdr face)))
;; Make the document title a bit bigger
(set-face-attribute 'org-document-title nil :font my/variable-width-font :weight 'bold :height 1.3)
;; Make sure certain org faces use the fixed-pitch face when variable-pitch-mode is on
(set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch)
(set-face-attribute 'org-table nil :inherit 'fixed-pitch)
(set-face-attribute 'org-formula nil :inherit 'fixed-pitch)
(set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)
;; Configure fill width
(setq visual-fill-column-width 110
visual-fill-column-center-text t))
(setq org-re-reveal-theme "solarized"
org-re-reveal-revealjs-version "5.1"
org-re-reveal-slide-number "c/t"
org-re-reveal-mousewheel "t")
It is possible to give presentations in org-mode using org-tree-slide
(use-package! org-tree-slide
:after org-mode
:config
(setq org-image-actual-width nil))
We don’t want to see underscores and asterisks when writing italic and bold text.
(after! org
(setq org-hide-emphasis-markers t))
Make all the things look pretty
(after! org-mode
(setq org-pretty-entities t)
(setq +org-pretty-mode t))
Live preview org files in github-flavoured markdown
(eval-after-load "org"
'(require 'ox-gfm nil t))
(after! org-roam-mode
(setq org-roam-directory "~/Documents/org/roam")
(org-roam-db-autosync-mode))
org-roam-ui
(use-package! websocket
:after org-roam)
(use-package! org-roam-ui
:after org-mode
;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;; a hookable mode anymore, you're advised to pick something yourself
;; if you don't care about startup time, use
;; :hook (after-init . org-roam-ui-mode)
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t))
Typing out src block headers all the time is a pain
(after! org-mode
(defun +yas/org-src-header-p ()
"Determine whether `point' is within a src-block header or header-args."
(pcase (org-element-type (org-element-context))
('src-block (< (point) ; before code part of the src-block
(save-excursion (goto-char (org-element-property :begin (org-element-context)))
(forward-line 1)
(point))))
('inline-src-block (< (point) ; before code part of the inline-src-block
(save-excursion (goto-char (org-element-property :begin (org-element-context)))
(search-forward "]{")
(point))))
('keyword (string-match-p "^header-args" (org-element-property :value (org-element-context))))))
(defun +yas/org-prompt-header-arg (arg question values)
"Prompt the user to set ARG header property to one of VALUES with QUESTION.
The default value is identified and indicated. If either default is selected,
or no selection is made: nil is returned."
(let* ((src-block-p (not (looking-back "^#\\+property:[ \t]+header-args:.*" (line-beginning-position))))
(default
(or
(cdr (assoc arg
(if src-block-p
(nth 2 (org-babel-get-src-block-info t))
(org-babel-merge-params
org-babel-default-header-args
(let ((lang-headers
(intern (concat "org-babel-default-header-args:"
(+yas/org-src-lang)))))
(when (boundp lang-headers) (eval lang-headers t)))))))
""))
default-value)
(setq values (mapcar
(lambda (value)
(if (string-match-p (regexp-quote value) default)
(setq default-value
(concat value " "
(propertize "(default)" 'face 'font-lock-doc-face)))
value))
values))
(let ((selection (consult--read question values :default default-value)))
(unless (or (string-match-p "(default)$" selection)
(string= "" selection))
selection))))
(defun +yas/org-src-lang ()
"Try to find the current language of the src/header at `point'. Return nil otherwise."
(let ((context (org-element-context)))
(pcase (org-element-type context)
('src-block (org-element-property :language context))
('inline-src-block (org-element-property :language context))
('keyword (when (string-match "^header-args:\\([^ ]+\\)" (org-element-property :value context))
(match-string 1 (org-element-property :value context)))))))
(defun +yas/org-last-src-lang ()
"Return the language of the last src-block, if it exists."
(save-excursion
(beginning-of-line)
(when (re-search-backward "^[ \t]*#\\+begin_src" nil t)
(org-element-property :language (org-element-context)))))
(defun +yas/org-most-common-no-property-lang ()
"Find the lang with the most source blocks that has no global header-args, else nil."
(let (src-langs header-langs)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^[ \t]*#\\+begin_src" nil t)
(push (+yas/org-src-lang) src-langs))
(goto-char (point-min))
(while (re-search-forward "^[ \t]*#\\+property: +header-args" nil t)
(push (+yas/org-src-lang) header-langs)))
(setq src-langs
(mapcar #'car
;; sort alist by frequency (desc.)
(sort
;; generate alist with form (value . frequency)
(cl-loop for (n . m) in (seq-group-by #'identity src-langs)
collect (cons n (length m)))
(lambda (a b) (> (cdr a) (cdr b))))))
(car (cl-set-difference src-langs header-langs :test #'string=))))
(defun org-syntax-convert-keyword-case-to-lower ()
"Convert all #+KEYWORDS to #+keywords."
(interactive)
(save-excursion
(goto-char (point-min))
(let ((count 0)
(case-fold-search nil))
(while (re-search-forward "^[ \t]*#\\+[A-Z_]+" nil t)
(unless (s-matches-p "RESULTS" (match-string 0))
(replace-match (downcase (match-string 0)) t)
(setq count (1+ count))))
(message "Replaced %d occurances" count))))
(defun org-auto-file-export ()
"Export to file if #+export_file_name is found in org file metadata"
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^[ \t]*#\\+export_file_name:*" nil t)
;; (while (re-search-forward "*export_file_name:*" nil t)
(setq org_export_fname (org-org-export-to-org))
(message "Exported org file %s" org_export_fname))))
(add-hook 'org-mode-hook
(lambda ()
(add-hook 'before-save-hook #'org-syntax-convert-keyword-case-to-lower nil 'make-it-local)
(add-hook 'after-save-hook #'org-auto-file-export nil 'make-it-local))))
- Use python code blocks in org mode (as well as some other languages thrown in)
- Don’t require :results output as a header in python SRC blocks
- Formatting for source code blocks
(after! org-mode
(require 'ob-emacs-lisp)
(require 'ob-fortran)
(require 'ob-julia)
(require 'ob-latex)
(require 'ob-lua)
(require 'ob-python)
(require 'ob-shell)
(setq org-babel-default-header-args
(cons '(:results . "output")
(assq-delete-all :results org-babel-default-header-args)))
(setq org-src-fontify-natively t
org-src-preserve-indentation t
org-src-tab-acts-natively t))
Specify shortcuts for src blocks with specific languages (not working)
;; (after! org
;; (setq org-structure-template-alist
;; '(("lsp" . "#begin_src emacs-lisp\n?\n#+end_src")
;; ("f90" . "#begin_src f90\n?\n#+end_src")
;; ("f" . "#begin_src fortran\n?\n#+end_src")
;; ("jl" . "#begin_src julia\n?\n#+end_src")
;; ("tex" . "#begin_src latex\n?\n#+end_src")
;; ("lua" . "#begin_src lua\n?\n#+end_src")
;; ("py" . "#begin_src python\n?\n#+end_src")
;; ("sh" . "#begin_src shell\n?\n#+end_src"))))
Support lsp in SRC blocks (not working)
;; (cl-defmacro lsp-org-babel-enable (lang)
;; "Support LANG in org source code block."
;; (setq centaur-lsp 'lsp-mode)
;; (cl-check-type lang stringp)
;; (let* ((edit-pre (intern (format "org-babel-edit-prep:%s" lang)))
;; (intern-pre (intern (format "lsp--%s" (symbol-name edit-pre)))))
;; `(progn
;; (defun ,intern-pre (info)
;; (let ((file-name (->> info caddr (alist-get :file))))
;; (unless file-name
;; (setq file-name (make-temp-file "babel-lsp-")))
;; (setq buffer-file-name file-name)
;; (lsp-deferred)))
;; (put ',intern-pre 'function-documentation
;; (format "Enable lsp-mode in the buffer of org source block (%s)."
;; (upcase ,lang)))
;; (if (fboundp ',edit-pre)
;; (advice-add ',edit-pre :after ',intern-pre)
;; (progn
;; (defun ,edit-pre (info)
;; (,intern-pre info))
;; (put ',edit-pre 'function-documentation
;; (format "Prepare local buffer environment for org source block (%s)."
;; (upcase ,lang))))))))
;; (defvar org-babel-lang-list
;; '("python" "ipython" "bash" "sh" "emacs-lisp" "fortran" "f90" "julia" "shell" "lua" "latex"))
;; (dolist (lang org-babel-lang-list)
;; (eval `(lsp-org-babel-enable ,lang)))
;; (defun org-babel-edit-prep:python (babel-info)
;; (setq-local buffer-file-name (->> babel-info caddr (alist-get :tangle)))
;; (lsp))
Generate a table of contents and set a shortcut
(use-package! toc-org
:commands toc-org-enable
:init (add-hook 'org-mode-hook 'toc-org-enable))
(after! org-mode
(defun add-toc ()
(interactive)
(insert "* Table of Contents :toc:\n\n"))
(map! :map org-mode-map
:after org
:localleader
:prefix ("C" . "insert toc")
:desc "insert-toc"
"C" #'add-toc))
Automatically log when a ‘TODO’ is marked as completed
(after! org-mode
(setq org-log-done 'time)
(setq org-closed-keep-when-no-todo 'non-nil))
This is basically just like opening a fish shell in a buffer in emacs
;; (defun custom-vterm-popup ()
;; (if (window-dedicated-p nil)
;; (message "yep")
;; (message "nope")))
;; (map! :leader
;; :desc "Custom vterm popup" "o t" #'custom-vterm-popup)
(use-package! vterm
:after vterm
:init
:config
(setq vterm-kill-buffer-on-exit t
vterm-always-compile-module t
vterm-ignore-blink-cursor nil))
Eshell is an emacs ‘shell’ written in elisp.
eshell-syntax-highlighting
– adds fish/zsh-like syntax highlighting.eshell-rc-script
– your profile for eshell; like a bashrc for eshell.eshell-aliases-file
– sets an aliases file for the eshell.
(use-package! eshell-syntax-highlighting
:after esh-mode
:config
(eshell-syntax-highlighting-global-mode +1)
(setq eshell-rc-script (concat user-emacs-directory "eshell/profile")
eshell-aliases-file (concat user-emacs-directory "eshell/aliases")
eshell-history-size 5000
eshell-buffer-maximum-lines 5000
eshell-hist-ignoredups t
eshell-scroll-to-bottom-on-input t
eshell-destroy-buffer-when-process-dies t
eshell-visual-commands'("fish" "htop" "ssh" "top" "zsh")))
Automatically close the command buffer on exit
(after! eshell
(setq eshell-destroy-buffer-when-process-dies t))
This package extends the pcomplete completion framework with completion from the fish shell. The fish shell has smart completion for a wide range of programs. fish does not require any special configuration to work with this package. Eshell, which uses pcomplete for completion, can be made to fall back on fish when it does not find any completion candidate with its native completion support. M-x shell can be made to use fish. This will disable the underlying shell completion.
;; (when (and (executable-find "fish")
;; (require 'fish-completion nil t))
;; (global-fish-completion-mode))
The condition will prevent the package from loading if fish is not found (change the executable name according to you local installation. Alternatively, you can simply load the package with (require ‘fish-completion) and call fish-completion-mode manually. Optionally, if the package bash-completion is installed, fish-completion-complete can be configured to fall back on bash to further try completing. See fish-completion-fallback-on-bash-p.
Fancier prompt:
Edit: I actually don’t like this, but will just keep it around for now.
;; (defun with-face (str &rest face-plist)
;; (propertize str 'face face-plist))
;; (defun shk-eshell-prompt ()
;; (let ((header-bg "#fff"))
;; (concat
;; (with-face (concat (eshell/pwd) " ") :background header-bg)
;; (with-face (format-time-string "(%Y-%m-%d %H:%M) " (current-time)) :background header-bg :foreground "#888")
;; (with-face
;; (or (ignore-errors (format "(%s)" (vc-responsible-backend default-directory))) "")
;; :background header-bg)
;; (with-face "\n" :background header-bg)
;; (with-face user-login-name :foreground "blue")
;; "@"
;; (with-face "localhost" :foreground "green")
;; (if (= (user-uid) 0)
;; (with-face " #" :foreground "red")
;; " $")
;; " ")))
;; (setq eshell-prompt-function 'shk-eshell-prompt)
;; (setq eshell-highlight-prompt nil)
Here are some additional functions/macros that could help you configure Doom:
- `load!’ for loading external *.el files relative to this one
- `use-package!’ for configuring packages
- `after!’ for running code after a package has loaded
- `add-load-path!’ for adding directories to the `load-path’, relative to this file. Emacs searches the `load-path’ when you load packages with `require’ or `use-package’.
- `map!’ for binding new keys
To get information about any of these functions/macros, move the cursor over the highlighted symbol at press ‘K’ (non-evil users must press ‘C-c c k’). This will open documentation for it, including demos of how they are used.
You can also try ‘gd’ (or ‘C-c c d’) to jump to their definition and see how they are implemented.