doom icon indicating copy to clipboard operation
doom copied to clipboard

Doom Emacs config

#+title: Config #+author: Ellis Kenyő #+property: header-args:emacs-lisp :tangle yes :comments link #+property: header-args:elisp :tangle packages.el :comments link #+property: header-args :tangle no :results silent :eval no-export #+caption: Banner #+latex_class: chameleon #+html_content_class: chameleon #+html_head: #+html_head:

[[file:images/banner.png]]

(icon courtesy of https://github.com/eccentric-j/doom-icon)

Below is my [[https://github.com/hlissner/doom-emacs][doom-emacs]] config. Most of it isn't particularly original; snippets from stackoverflow, modernemacs, [[https://github.com/daviwil][David Wilson]] and [[https://github.com/tecosaur][Tecosaur]]. Everything else will be commented to the best of /my/ ability.

Most of this cobbled mess is now mine, and if you do intend to use any of it it would be nice if you mentioned me when doing so. It's just polite :)

  • Table of Contents :TOC_5_gh:noexport:
  • [[#setup][Setup]]
    • [[#workflow][Workflow]]
    • [[#exwm-setup][EXWM Setup]]
      • [[#modulesuiexwm][modules/ui/exwm]]
      • [[#session][Session]]
      • [[#startsh--start-debugsh][start.sh / start-debug.sh]]
  • [[#faq][FAQ]]
  • [[#globals][Globals]]
    • [[#constants-and-variables][Constants and Variables]]
      • [[#lexical-binding][Lexical binding]]
      • [[#default-projectile-path][Default projectile path]]
      • [[#lookup-provider-urls][Lookup provider URLs]]
      • [[#subword-mode][Subword-mode]]
      • [[#auto-revert-mode][Auto-revert-mode]]
      • [[#prevent-flickering][Prevent flickering]]
      • [[#clear-snippets-before-loading][Clear snippets before loading]]
      • [[#load-env-after-reload][Load env after reload]]
      • [[#bury-compile-buffer][Bury compile buffer]]
      • [[#evil][Evil]]
        • [[#splits][Splits]]
        • [[#fine-undo][Fine undo]]
        • [[#global-substitute][Global substitute]]
        • [[#ignore-visual-text-in-the-kill-ring][Ignore visual text in the kill ring]]
        • [[#use-emacs-binds-in-insert-mode][Use emacs binds in insert mode]]
      • [[#lispyville][Lispyville]]
      • [[#default-scratch-mode][Default scratch mode]]
      • [[#auth-info][Auth info]]
      • [[#fetch-auth-source][fetch-auth-source]]
      • [[#magit][Magit]]
        • [[#forge][Forge]]
      • [[#eshell][EShell]]
        • [[#prompt][Prompt]]
        • [[#settings][Settings]]
      • [[#user-setup][User setup]]
      • [[#vterm][vterm]]
        • [[#always-compile][Always compile]]
        • [[#kill-buffer][Kill buffer]]
        • [[#fix-c-backspace][Fix =c-backspace=]]
        • [[#functions][Functions]]
        • [[#multi-vterm][Multi-vterm]]
  • [[#keybindings][Keybindings]]
    • [[#save][Save]]
    • [[#search][Search]]
    • [[#dired][Dired]]
    • [[#journal][Journal]]
  • [[#graphical-setup][Graphical setup]]
    • [[#which-key][which-key]]
    • [[#marginalia][Marginalia]]
      • [[#files][Files]]
    • [[#info-pages][Info pages]]
    • [[#dashboard][Dashboard]]
    • [[#tab-bar-mode][Tab-bar-mode]]
    • [[#modeline][Modeline]]
    • [[#fonts][Fonts]]
      • [[#ligatures][Ligatures]]
    • [[#theme][Theme]]
    • [[#line-numbers][Line Numbers]]
    • [[#guiframe][GUI/Frame]]
  • [[#org-mode][Org Mode]]
    • [[#fill-column][fill-column]]
    • [[#hook-setup][Hook setup]]
    • [[#org-directory][org-directory]]
    • [[#font-setup][Font setup]]
    • [[#heading-minimap][Heading minimap]]
    • [[#properties][Properties]]
      • [[#allow-property-inheritance][Allow property inheritance]]
    • [[#characters][Characters]]
      • [[#org-bars][org-bars]]
      • [[#headline-bullets][Headline bullets]]
      • [[#item-bullets][Item bullets]]
      • [[#dropdown-icon][Dropdown icon]]
    • [[#keywords][Keywords]]
    • [[#agendalog][Agenda/Log]]
      • [[#show-done-tasks-in-agenda][Show =DONE= tasks in agenda]]
      • [[#timestamp-done-items][Timestamp done items]]
      • [[#log-items-in-the-drawer][Log items in the drawer]]
    • [[#cycle][Cycle]]
    • [[#folding][Folding]]
    • [[#org-appear][Org-appear]]
    • [[#mixed-pitch][Mixed pitch]]
    • [[#archivecleanup][Archive/Cleanup]]
      • [[#archive-done-tasks][Archive =DONE= tasks]]
      • [[#remove-kill-tasks][Remove =KILL= tasks]]
    • [[#show-images][Show images]]
    • [[#autoexecute-tangled-shell-files][Autoexecute tangled shell files]]
    • [[#variable-setup][Variable setup]]
    • [[#better-snippets][Better snippets]]
    • [[#roam][Roam]]
      • [[#templates][Templates]]
    • [[#capture][Capture]]
      • [[#prettify][Prettify]]
      • [[#templates-1][Templates]]
    • [[#export][Export]]
      • [[#latex][LaTeX]]
        • [[#preambles][Preambles]]
        • [[#conditional-features][Conditional features]]
        • [[#tectonic][Tectonic]]
        • [[#classes][Classes]]
        • [[#packages][Packages]]
        • [[#pretty-code-blocks][Pretty code blocks]]
        • [[#ox-chameleon][ox-chameleon]]
        • [[#beamer][Beamer]]
      • [[#subsuperscript-characters][(sub|super)script characters]]
  • [[#languages][Languages]]
    • [[#dart][Dart]]
    • [[#rust][Rust]]
    • [[#c][C#]]
    • [[#elisp][ELISP]]
    • [[#lspdap][LSP/DAP]]
      • [[#increase-variable-line-length][Increase variable line length]]
      • [[#ignore-files-in-xref][Ignore files in xref]]
      • [[#improve-completions][Improve completions]]
      • [[#ignore-directories][Ignore directories]]
    • [[#php][PHP]]
      • [[#web-mode-setup][Web-mode setup]]
      • [[#intelephense][Intelephense]]
      • [[#eglot][Eglot]]
  • [[#snippets][Snippets]]
    • [[#cape][Cape]]
    • [[#snippet-definitions][Snippet definitions]]
      • [[#org-mode-1][Org-mode]]
        • [[#][]]
      • [[#php-mode][PHP-Mode]]
        • [[#function][function]]
        • [[#php-1][php]]
      • [[#kotlin-mode][kotlin-mode]]
        • [[#-1][]]
      • [[#slack-message-compose-buffer-mode][slack-message-compose-buffer-mode]]
        • [[#standup][standup]]
      • [[#php-laravel-mode][+php-laravel-mode]]
        • [[#yas-parentsel][.yas-parents.el]]
        • [[#-2][]]
        • [[#migration][migration]]
        • [[#scope][scope]]
  • [[#packages-1][Packages]]
    • [[#disabledunpin][Disabled/unpin]]
    • [[#embark-vc][embark-vc]]
    • [[#laravel-mode][laravel-mode]]
    • [[#prescient][prescient]]
    • [[#rainbow-identifiers][Rainbow Identifiers]]
      • [[#fix-in-web-mode][Fix in web-mode]]
    • [[#cucumber][Cucumber]]
    • [[#rpm-spec][RPM Spec]]
  • [[#spelling][Spelling]]
  • [[#slack][Slack]]
  • [[#reaper][Reaper]]
  • [[#graphviz][Graphviz]]
  • [[#jira][Jira]]
    • [[#org-jira][org-jira]]
    • [[#jira-workflow][jira-workflow]]
  • [[#translate][Translate]]
  • [[#exercism][Exercism]]
  • [[#local-settings][Local settings]]
    • [[#dotenv][dotenv]]
  • Setup ** Workflow [[file:images/overview.png]]

** EXWM Setup One of the many usecases for this config is =exwm= which is pulled from a number of files in the current environment. Loading =exwm= is a simple case of enabling the exwm module, although it's /wildly/ untested and is basically hardcoded to just work for me. Any input is welcome, although unlikely to make it into /this/ module (until I stop being lazy and attempt to make it better for upstream....)

*** modules/ui/exwm The primary lisp file where the bulk of the configuration is held, with everything from my process manager to a now-playing segment. Below are some usage screenshots. Standard doom module layout, nothing fishy going on. For those unfamiliar,

  • =init.el= is loaded before anything else really, which is important to properly check if the flag exists to load the exwm code as early as possible
  • =config.el= is the main bread and butter, all the config lives here (surprisingly)
  • =doctor.el= is currently just used for detecting missing exe's, by plugging into =doom doctor=
  • =packages.el= is a list of extra packages to be installed by doom's package manager

[[file:images/kill-process.png]]

[[file:images/tray.png]]

Transparency is handled both through [[*GUI/Frame][Doom]] and via [[file:exwm/picom.conf][picom]].

*** Session For the sake of simplicity, I use a slightly modified version of [[https://github.com/WJCFerguson/exwm-gnome-flashback][GNOME Flashback]] to run the startup scripts. It also gives me ootb access to things like =pinentry=, the various password stores, =gnome-screensaver= lock screen and the useful screenshot tool.

As such, everything is themed around [[https://nordtheme.com][Nord]].

Over time and due to various issues, I have been migrating to a plain =exwm= session but I haven't yet settled on the best approach.

*** start.sh / start-debug.sh The scripts responsible for starting up exwm in the right way, including env variables and picom.

  • FAQ None yet because luckily nobody else has seen this spaghetti junction

  • Globals ** Constants and Variables I could make a Bioshock Infinite joke here but I can't think of one. Wouldn't think of one? Would have thought of one.

*** Lexical binding

#+begin_src emacs-lisp ;;; -- lexical-binding: t; -- #+end_src

*** Default projectile path I stick to the same convention for projects on every OS, so it makes sense to tell projectile about it.

#+begin_src emacs-lisp (setq projectile-project-search-path '("~/build")) #+end_src

*** Lookup provider URLs Majority of the default lookup providers are useless (to me) so let's trim the fat, adjust the order and add in some of our own!

#+begin_src emacs-lisp (setq +lookup-provider-url-alist '(("Doom Emacs issues" "https://github.com/hlissner/doom-emacs/issues?q=is%%3Aissue+%s") ("DuckDuckGo" +lookup--online-backend-duckduckgo "https://duckduckgo.com/?q=%s") ("StackOverflow" "https://stackoverflow.com/search?q=%s") ("Github" "https://github.com/search?ref=simplesearch&q=%s") ("Youtube" "https://youtube.com/results?aq=f&oq=&search_query=%s") ("MDN" "https://developer.mozilla.org/en-US/search?q=%s") ("Nixpkgs" "https://search.nixos.org/packages?query=%s"))) #+end_src

*** Subword-mode Subword mode is a good start because PHP uses a lot of CamelCase and it makes refactoring slightly easier

#+begin_src emacs-lisp (global-subword-mode 1) #+end_src

*** Auto-revert-mode Testing having auto-revert-mode on for text-mode buffers (should just be log files mostly)

#+begin_src emacs-lisp (add-hook! 'text-mode (lambda () (auto-revert-mode 1))) #+end_src

*** Prevent flickering Noticed some odd flickering here and there, apparently this should resolve it

#+begin_src emacs-lisp (add-to-list 'default-frame-alist '(inhibit-double-buffering . t)) #+end_src

*** Clear snippets before loading Some attempt to make them reproducible.

#+begin_src emacs-lisp (add-hook! 'org-babel-pre-tangle-hook (when (file-directory-p "snippets") (require 'async) (async-start (lambda () (delete-directory "snippets" t (not (null delete-by-moving-to-trash)))) (lambda (result) (print! "Delete snippets dir got: " result))))) #+end_src

*** Load env after reload Most of the time, reloading breaks. So, let's not break.

#+begin_src emacs-lisp (add-hook! 'doom-after-reload-hook (doom-load-envvars-file (expand-file-name "env" doom-local-dir) t)) #+end_src

*** Bury compile buffer Assuming the buffer finishes successfully, close after 1 second.

#+begin_src emacs-lisp (defun bury-compile-buffer-if-successful (buffer string) "Bury a compilation buffer if succeeded without warnings " (when (and (eq major-mode 'comint-mode) (string-match "finished" string) (not (with-current-buffer buffer (search-forward "warning" nil t)))) (run-with-timer 1 nil (lambda (buf) (let ((window (get-buffer-window buf))) (when (and (window-live-p window) (eq buf (window-buffer window))) (delete-window window)))) buffer)))

(add-hook 'compilation-finish-functions #'bury-compile-buffer-if-successful) #+end_src

*** Evil **** Splits I make a lot of splits, and it finally got annoying having to swap to them all the time. So, let's change that

#+begin_src emacs-lisp (setq evil-split-window-below t evil-vsplit-window-right t) #+end_src

**** Fine undo I don't need this because I, like all programmers, make 0 mistaeks.

#+begin_src emacs-lisp (setq evil-want-fine-undo t) #+end_src

**** Global substitute More often than not, I'd argue always, I want ~s/~ on my ex commands, so let's sort that out.

#+begin_src emacs-lisp (setq evil-ex-substitute-global t) #+end_src

**** Ignore visual text in the kill ring When we overwrite text in visual mode, say =vip=, don't add to the kill ring.

#+begin_src emacs-lisp (setq evil-kill-on-visual-paste nil) #+end_src

**** Use emacs binds in insert mode Some of them are quite useful, and I normally use them in the DE.

#+begin_src emacs-lisp (setq evil-disable-insert-state-bindings t) #+end_src *** Lispyville This structured-editing thing is apparently really neat, so let's see how we go

#+begin_src emacs-lisp (after! lispy (setq lispyville-key-theme '((operators normal) c-w (prettify insert) (atom-movement normal visual) (additional-movement normal) slurp/barf-lispy additional))) #+end_src

*** Default scratch mode Make the scratch buffer start in lisp mode

#+begin_src emacs-lisp (setq doom-scratch-initial-major-mode 'lisp-interaction-mode) #+end_src

*** Auth info Add plaintext authinfo file to the list of sources. I /know/ I should use a GPG file but I'll get around to it damn it.

#+begin_src emacs-lisp (add-to-list 'auth-sources "~/.authinfo") #+end_src

*** fetch-auth-source Useful function to retrieve passwords from auth-sources

#+begin_src emacs-lisp (defun fetch-auth-source (&rest params) (require 'auth-source) (let ((match (car (apply #'auth-source-search params)))) (if match (let ((secret (plist-get match :secret))) (if (functionp secret) (funcall secret) secret)) (error "Password not found for %S" params)))) #+end_src

*** Magit **** Forge Allow forge to create repos under my name

#+begin_src emacs-lisp (setq forge-owned-accounts '(("elken"))) #+end_src

*** EShell **** Prompt Eshell is a beautiful thing but ootb experience is a tad dated. Custom prompt based on a combination of the famous p10k and eshell-git-prompt. I only /really/ need the minimum out of a prompt:

  • =cwd=; almost impossible to work without knowing the current working directory
  • =git= info; current branch, dirty/clean status, etc
  • prompt number: useful for jumping up and down for fast history in a given session

Can't get enough out of the default powerline theme, and removing a dependancy we're rolling our own prompt called =eshell-p10kline=

#+begin_src elisp (package! eshell-p10k :recipe (:host github :repo "elken/eshell-p10k")) #+end_src

#+begin_src emacs-lisp (use-package! eshell-p10k :after eshell :config (setq eshell-prompt-function #'eshell-p10k-prompt-function eshell-prompt-regexp eshell-p10k-prompt-string)) #+end_src

**** Settings We use eshell in a cross platform world, so we should prefer the lisp version of things to ensure a more consistent experience.

#+begin_src emacs-lisp (setq eshell-prefer-lisp-functions t) #+end_src

*** User setup Use my name and emails for things like GPG, snippets, mail, magit, etc. Differs based on which OS I'm on.

#+BEGIN_SRC emacs-lisp (setq user-full-name "Ellis Kenyő" user-mail-address "[email protected]") #+END_SRC

*** vterm Vterm clearly wins the terminal war. Also doesn't need much configuration out of the box, although the shell integration does. That currently exists in my [[https://github.com/elken/.files][dotfiles]]

**** Always compile Fixes a weird bug with native-comp, and I don't use guix anymore.

#+begin_src emacs-lisp (setq vterm-always-compile-module t) #+end_src

**** Kill buffer If the process exits, kill the =vterm= buffer

#+begin_src emacs-lisp (setq vterm-kill-buffer-on-exit t) #+end_src

**** Fix =c-backspace= I've picked this up in muscle memory now and I'm fed up with it not working. Not anymore!

#+begin_src emacs-lisp (after! vterm (define-key vterm-mode-map (kbd "<C-backspace>") (lambda () (interactive) (vterm-send-key (kbd "C-w"))))) #+end_src

**** Functions Useful functions for the shell-side integration provided by vterm.

#+begin_src emacs-lisp (after! vterm (setf (alist-get "woman" vterm-eval-cmds nil nil #'equal) '((lambda (topic) (woman topic)))) (setf (alist-get "magit-status" vterm-eval-cmds nil nil #'equal) '((lambda (path) (magit-status path)))) (setf (alist-get "dired" vterm-eval-cmds nil nil #'equal) '((lambda (dir) (dired dir))))) #+end_src

**** Multi-vterm #+begin_src elisp (package! multi-vterm) #+end_src

#+begin_src emacs-lisp (use-package! multi-vterm :after vterm) #+end_src

  • Keybindings It's not a custom config without some fancy keybinds

** Save Back to a simpler time...

#+begin_src emacs-lisp (map! :g "C-s" #'save-buffer) #+end_src

** Search +Swiper+ Consult is /much/ better than isearch

#+begin_src emacs-lisp (map! :after evil :gnvi "C-f" #'consult-line) #+end_src

** Dired Dired should behave better with evil mappings

#+begin_src emacs-lisp (map! :map dired-mode-map :n "h" #'dired-up-directory :n "l" #'dired-find-alternate-file) #+end_src

** Journal This is something I'm likely to use quite often, especially with an easy convenience binding

#+begin_src emacs-lisp (after! org-journal (setq org-journal-find-file #'find-file-other-window)

(map! :leader :desc "Open today's journal" "j" #'org-journal-open-current-journal-file)) #+end_src

  • Graphical setup ** which-key Remove some of the useless =evil-= prefixes from which-key commands.

#+begin_src emacs-lisp (setq which-key-allow-multiple-replacements t) (after! which-key (pushnew! which-key-replacement-alist '(("" . "\+?evil[-:]?\\(?:a-\\)?\\(.*\\)") . (nil . " \\1")) '(("\\g s" . "\`evilem--?motion-\(.*\)") . (nil . " \1")))) #+end_src

** Marginalia Marginalia is part of the Vertico stack, and is responsible for all the fancy faces and extra information. *** Files The doom module out of the box includes a number of customizations, but the below from Teco gives a much better experience for files.

#+begin_src emacs-lisp (after! marginalia (setq marginalia-censor-variables nil)

(defadvice! +marginalia--anotate-local-file-colorful (cand) "Just a more colourful version of `marginalia--anotate-local-file'." :override #'marginalia--annotate-local-file (when-let (attrs (file-attributes (substitute-in-file-name (marginalia--full-candidate cand)) 'integer)) (marginalia--fields ((marginalia--file-owner attrs) :width 12 :face 'marginalia-file-owner) ((marginalia--file-modes attrs)) ((+marginalia-file-size-colorful (file-attribute-size attrs)) :width 7) ((+marginalia--time-colorful (file-attribute-modification-time attrs)) :width 12))))

(defun +marginalia--time-colorful (time) (let* ((seconds (float-time (time-subtract (current-time) time))) (color (doom-blend (face-attribute 'marginalia-date :foreground nil t) (face-attribute 'marginalia-documentation :foreground nil t) (/ 1.0 (log (+ 3 (/ (+ 1 seconds) 345600.0))))))) ;; 1 - log(3 + 1/(days + 1)) % grey (propertize (marginalia--time time) 'face (list :foreground color))))

(defun +marginalia-file-size-colorful (size) (let* ((size-index (/ (log10 (+ 1 size)) 7.0)) (color (if (< size-index 10000000) ; 10m (doom-blend 'orange 'green size-index) (doom-blend 'red 'orange (- size-index 1))))) (propertize (file-size-human-readable size) 'face (list :foreground color))))) #+end_src

** Info pages Slightly improve the look and feel of Info pages, might actually encourage me to /read/ them.

#+begin_src elisp (package! info-colors) #+end_src

#+begin_src emacs-lisp (use-package! info-colors :after info :commands (info-colors-fontify-node) :hook (Info-selection . info-colors-fontify-node)) #+end_src

** Dashboard Inhibit the menu to improve things slightly

#+begin_src emacs-lisp (remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-shortmenu) (remove-hook '+doom-dashboard-functions #'doom-dashboard-widget-footer) #+end_src

** Tab-bar-mode Acting as a global modeline (of sorts), this could end up being quite useful but for now this is just testing.

#+begin_src emacs-lisp (defun reaper-get-running-timer-duration () "Return duration of current running timer." (reaper-with-buffer (when reaper-running-timer (reaper--hours-to-time (let ((entry (assoc reaper-running-timer reaper-timeentries))) (cdr (assoc :hours entry)))))))

(defun reaper-modeline-vanilla () "Return modeline string for vanilla emacs." (when (and (string= "" reaper-api-key) (string= "" reaper-account-id)) ;; TODO Make this a function (dotenv-update-project-env doom-user-dir) (customize-set-variable 'reaper-api-key (getenv "REAPER_API_KEY")) (customize-set-variable 'reaper-account-id (getenv "REAPER_ACCOUNT_ID"))) (when-let ((note (reaper-get-running-timer-note)) (timer (reaper-get-running-timer-duration))) (format " [ %s | %s] " (string-trim note) timer)))

(defvar reaper-modeline--timer nil) (defun reaper-modeline-timer () "Start/stop the time for updating reaper doom-modeline segment." (if (timerp reaper-modeline--timer) (cancel-timer reaper-modeline--timer)) (setq reaper-modeline--timer (run-with-timer 1 60 (lambda () (reaper-with-buffer (setq reaper-timeentries nil) (reaper-refresh-entries))))))

(after! (reaper dotenv) (unless (and (or (null reaper-api-key) (string= "" reaper-api-key)) (or (null reaper-account-id) (string= "" reaper-account-id))) (reaper-modeline-timer) (add-to-list 'global-mode-string '(:eval (reaper-modeline-vanilla)))))

(customize-set-variable 'tab-bar-format '(tab-bar-format-global)) (customize-set-variable 'tab-bar-mode t) (when tab-bar-mode (setq display-time-format " [ %H:%M %d/%m/%y]" display-time-default-load-average nil) (display-time-mode 1)) #+end_src

** Modeline Default modeline is a tad cluttered, and because I don't use exwm anymore the modeline from that module isn't in use. So, it's duplicated here and tweaked.

#+begin_src emacs-lisp (after! doom-modeline (setq all-the-icons-scale-factor 1.1 auto-revert-check-vc-info t doom-modeline-major-mode-icon (display-graphic-p) doom-modeline-major-mode-color-icon (display-graphic-p) doom-modeline-buffer-file-name-style 'relative-to-project doom-modeline-github t doom-modeline-github-interval 60 doom-modeline-vcs-max-length 60) (remove-hook 'doom-modeline-mode-hook #'size-indication-mode) (doom-modeline-def-modeline 'main '(bar modals workspace-name window-number persp-name buffer-position selection-info buffer-info matches remote-host debug vcs matches) '(github mu4e grip gnus checker misc-info repl lsp " "))) #+end_src

** Fonts Configure the fonts across all used platforms (slightly different names).

#+BEGIN_SRC emacs-lisp (setq doom-font (font-spec :family "Iosevka Nerd Font" :size 13) doom-variable-pitch-font (font-spec :family "Overpass" :size 13) doom-unicode-font (font-spec :family "Iosevka Nerd Font" :size 14) doom-serif-font (font-spec :family "Overpass Mono" :weight 'light)) #+END_SRC

*** Ligatures Ligatures are a mess in programming languages, however they make org documents quite nice so let's just use them here until a good fix is found.

#+begin_src emacs-lisp (setq-hook! org-mode prettify-symbols-alist '(("#+end_quote" . "”") ("#+END_QUOTE" . "”") ("#+begin_quote" . "“") ("#+BEGIN_QUOTE" . "“") ("#+end_src" . "«") ("#+END_SRC" . "«") ("#+begin_src" . "»") ("#+BEGIN_SRC" . "»") ("#+name:" . "»") ("#+NAME:" . "»"))) #+end_src

** Theme Load my current flavour-of-the-month colour scheme.

#+BEGIN_SRC emacs-lisp (setq doom-theme 'doom-nord) #+END_SRC

Along with a few face overrides (thought about merging upstream but it would have sparked a discussion, maybe later)

#+begin_src emacs-lisp (custom-theme-set-faces! 'doom-nord (tree-sitter-hl-face:constructor :foreground ,(doom-color 'blue)) (tree-sitter-hl-face:number :foreground ,(doom-color 'magenta)) (tree-sitter-hl-face:attribute :foreground ,(doom-color 'magenta) :weight bold) (tree-sitter-hl-face:variable :foreground ,(doom-color 'base7) :weight bold) (tree-sitter-hl-face:variable.builtin :foreground ,(doom-color 'base7) :weight bold) (tree-sitter-hl-face:constant.builtin :foreground ,(doom-color 'magenta) :weight bold) (tree-sitter-hl-face:constant :foreground ,(doom-color 'teal) :weight bold) (tree-sitter-hl-face:function.macro :foreground ,(doom-color 'teal)) (tree-sitter-hl-face:label :foreground ,(doom-color 'magenta)) (tree-sitter-hl-face:operator :foreground ,(doom-color 'blue)) (tree-sitter-hl-face:variable.parameter :foreground ,(doom-color 'dark-blue)) (tree-sitter-hl-face:punctuation.delimiter :foreground ,(doom-color 'cyan)) (tree-sitter-hl-face:punctuation.bracket :foreground ,(doom-color 'cyan)) (tree-sitter-hl-face:punctuation.special :foreground ,(doom-color 'cyan)) (tree-sitter-hl-face:type :foreground ,(doom-color 'blue)) (tree-sitter-hl-face:type.builtin :foreground ,(doom-color 'blue)) (tree-sitter-hl-face:tag :foreground ,(doom-color 'base7)) (tree-sitter-hl-face:string :foreground ,(doom-color 'green)) (tree-sitter-hl-face:comment :foreground ,(doom-color 'base6)) (tree-sitter-hl-face:function :foreground ,(doom-color 'cyan)) (tree-sitter-hl-face:method :foreground ,(doom-color 'teal)) (tree-sitter-hl-face:function.builtin :foreground ,(doom-color 'cyan)) (tree-sitter-hl-face:property :foreground ,(doom-color 'dark-blue)) (tree-sitter-hl-face:keyword :foreground ,(doom-color 'blue)) (php-class :foreground ,(doom-color 'blue)) (php-php-tag :foreground ,(doom-color 'blue)) (php-constant :foreground ,(doom-color 'violet)) (php-magical-constant :foreground ,(doom-color 'orange)) (php-operator :foreground ,(doom-color 'blue)) (php-doc-$this :foreground ,(doom-color 'cyan)) (php-object-op :foreground ,(doom-color 'cyan)) (php-string-op :foreground ,(doom-color 'blue)) (php-static-method-call :foreground ,(doom-color 'magenta)) (php-method-call :foreground ,(doom-color 'magenta)) (php-function-name :foreground ,(doom-lighten 'magenta 0.3)) (corfu-default :font "Iosevka Nerd Font Mono" :background "#272C36" :foreground "#ECEFF4")) #+end_src

Change the default banner (need to add the ASCII banner at some point)

#+BEGIN_SRC emacs-lisp (setq +doom-dashboard-banner-file (expand-file-name "images/banner.png" doom-private-dir)) #+END_SRC

** Line Numbers Set the default line number format to be relative and disable line numbers for specific modes

#+BEGIN_SRC emacs-lisp (setq display-line-numbers-type 'relative)

(dolist (mode '(org-mode-hook term-mode-hook shell-mode-hook eshell-mode-hook)) (add-hook mode (lambda () (display-line-numbers-mode 0)))) #+END_SRC

** GUI/Frame Maximise emacs on startup

#+BEGIN_SRC emacs-lisp (add-to-list 'default-frame-alist '(fullscreen . maximized)) #+END_SRC

Add some transparency

#+begin_src emacs-lisp (after! exwm (set-frame-parameter (selected-frame) 'alpha 90) (add-to-list 'default-frame-alist '(alpha . 90))) #+end_src

  • Org Mode ** fill-column Keep the content centered on the page when writing org documents

#+begin_src elisp (package! visual-fill-column) #+end_src

#+begin_src emacs-lisp (use-package! visual-fill-column :custom (visual-fill-column-width 300) (visual-fill-column-center-text t) :hook (org-mode . visual-fill-column-mode)) #+end_src

** Hook setup =org-mode= is a wonderful thing, and far too complex to bury in another section. The more I use it, the more I will add to this area but for now it's mostly used for documentation and organisation.

#+begin_src emacs-lisp (defun elken/org-setup-hook () "Modes to enable on org-mode start" (org-indent-mode) (visual-line-mode 1) (+org-pretty-mode) (elken/org-font-setup))

(add-hook! org-mode #'elken/org-setup-hook) #+end_src

** org-directory Let's set a sane default directory based on where I am

#+begin_src emacs-lisp (setq org-directory "~/Nextcloud/org") #+end_src

** Font setup Font setup to prettify the fonts. Uses Overpass in most places except where it makes sense to use the defined fixed width font.

#+BEGIN_SRC emacs-lisp (defun elken/org-font-setup () ;; Set faces for heading levels (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 "Overpass" :weight 'regular :height (cdr face)))

;; Ensure that anything that should be fixed-pitch in Org files appears that way (set-face-attribute 'org-tag nil :foreground nil :inherit '(shadow fixed-pitch) :weight 'bold) (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch)) (set-face-attribute 'org-table 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)) #+END_SRC

** Heading minimap Outline structure of the org documents, apparently it has cool things for promoting from it.

#+begin_src elisp (package! org-ol-tree :recipe (:host github :repo "Townk/org-ol-tree")) #+end_src

#+begin_src emacs-lisp (use-package! org-ol-tree :after org :commands org-ol-tree :config (setq org-ol-tree-ui-window-position 'left))

(map! :map org-mode-map :after org :localleader :desc "Outline" "O" #'org-ol-tree) #+end_src

** Properties *** Allow property inheritance This may be the solution to /so/ many weird issues with src blocks.

#+begin_src emacs-lisp (setq org-use-property-inheritance t) #+end_src

** Characters Tried out org-modern recently, it is /very/ nice but also detracts away from some of the org markup and makes editing it too hard, so back to =(:lang org +pretty)= we go.

*** org-bars Indent guides meets org-mode, these are /surprisingly/ nice.

Though, they don't work with variable pitch fonts :(

#+begin_src elisp (package! org-bars :recipe (:host github :repo "tonyaldon/org-bars")) #+end_src

#+begin_src emacs-lisp (use-package! org-bars :when (and (display-graphic-p) nil) :hook (org-mode . org-bars-mode) :init (setq org-bars-extra-pixels-height 10)) #+end_src

*** Headline bullets I don't feel the need for fancy characters to discern depth, I found this on someone else's config and I actually quite like the minimal look.

#+begin_src emacs-lisp (setq org-superstar-headline-bullets-list '("› ")) #+end_src

*** Item bullets Barely any adjustment here, just make them look a /bit/ nicer.

#+begin_src emacs-lisp (setq org-superstar-item-bullet-alist '((?* . ?⋆) (?+ . ?‣) (?- . ?•))) #+end_src

*** Dropdown icon When a drawer is collapsed, show a nice dropdown arrow.

#+begin_src emacs-lisp (setq org-ellipsis " ▾") #+end_src

** Keywords Default keywords are /far/ too minimal. This will need further tweaking as I start using org mode for organisation more.

#+begin_src emacs-lisp (after! org (setq org-todo-keywords '((sequence "TODO(t)" "INPROG(i)" "PROJ(p)" "STORY(s)" "WAIT(w)" "HOLD(h)" "|" "DONE(d)" "KILL(k)") (sequence " " "-" "?" "|" "X")))) #+end_src

** Agenda/Log *** Show =DONE= tasks in agenda

#+begin_src emacs-lisp (setq org-agenda-start-with-log-mode t) #+end_src

*** Timestamp done items

#+begin_src emacs-lisp (setq org-log-done 'time) #+end_src

*** Log items in the drawer

#+begin_src emacs-lisp (setq org-log-into-drawer t) #+end_src

** Cycle Cycle by default (no idea why this isn't default)

#+begin_src emacs-lisp (setq org-cycle-emulate-tab nil) #+end_src

** Folding Default folding is very noisy, I /rarely/ need to see everything expanded

#+begin_src emacs-lisp (setq org-startup-folded 'content) #+end_src

** Org-appear Defines a minor mode to allow special forms such as /italics/, bold, underline and =literal= to be editable when the cursor is over them, otherwise display the proper value.

#+begin_src elisp (package! org-appear :recipe (:host github :repo "awth13/org-appear")) #+end_src

#+begin_src emacs-lisp (use-package! org-appear :after org :hook (org-mode . org-appear-mode) :config (setq org-appear-autoemphasis t org-appear-autolinks t org-appear-autosubmarkers t)) #+end_src

** Mixed pitch Enable =mixed-pitch-mode= to enable the more readable fonts where it makes sense.

#+begin_src emacs-lisp (setq +zen-mixed-pitch-modes '(org-mode LaTeX-mode markdown-mode gfm-mode Info-mode rst-mode adoc-mode))

(dolist (hook +zen-mixed-pitch-modes) (add-hook (intern (concat (symbol-name hook) "-hook")) #'mixed-pitch-mode)) #+end_src

** Archive/Cleanup Adjust the format of archived org files (so they don't show up in orgzly)

#+begin_src emacs-lisp (setq org-archive-location "archive/Archive_%s::") #+end_src

*** Archive =DONE= tasks

Enables archiving of tasks. Replaces the in-built version which only works for single tasks.

#+BEGIN_SRC emacs-lisp (defun elken/org-archive-done-tasks () "Attempt to archive all done tasks in file" (interactive) (org-map-entries (lambda () (org-archive-subtree) (setq org-map-continue-from (org-element-property :begin (org-element-at-point)))) "/DONE" 'file))

(map! :map org-mode-map :desc "Archive tasks marked DONE" "C-c DEL a" #'elken/org-archive-done-tasks) #+END_SRC

*** Remove =KILL= tasks

Enables removal of killed tasks. I'm not /yet/ interested in tracking this long-term.

#+BEGIN_SRC emacs-lisp (defun elken/org-remove-kill-tasks () (interactive) (org-map-entries (lambda () (org-cut-subtree) (pop kill-ring) (setq org-map-continue-from (org-element-property :begin (org-element-at-point)))) "/KILL" 'file))

(map! :map org-mode-map :desc "Remove tasks marked as KILL" "C-c DEL k" #'elken/org-remove-kill-tasks) #+END_SRC

** Show images Show images inline by default

#+BEGIN_SRC emacs-lisp (setq org-startup-with-inline-images t) #+END_SRC

But also, adjust them to an appropriate size. This should be adjusted to handle better resolutions.

#+begin_src emacs-lisp (setq org-image-actual-width 600) #+end_src

** Autoexecute tangled shell files Make tangled shell files executable (I trust myself, ish...)

#+begin_src emacs-lisp (defun elken/make-tangled-shell-executable () "Ensure that tangled shell files are executable" (set-file-modes (buffer-file-name) #o755))

(add-hook 'org-babel-post-tangle-hook 'elken/make-tangled-shell-executable) #+end_src

** Variable setup Useful settings and functions for maintaining modified dates in org files

#+begin_src emacs-lisp (setq enable-dir-local-variables t) (defun elken/find-time-property (property) "Find the PROPETY in the current buffer." (save-excursion (goto-char (point-min)) (let ((first-heading (save-excursion (re-search-forward org-outline-regexp-bol nil t)))) (when (re-search-forward (format "^#\+%s:" property) nil t) (point)))))

(defun elken/has-time-property-p (property) "Gets the position of PROPETY if it exists, nil if not and empty string if it's undefined." (when-let ((pos (elken/find-time-property property))) (save-excursion (goto-char pos) (if (and (looking-at-p " ") (progn (forward-char) (org-at-timestamp-p 'lax))) pos ""))))

(defun elken/set-time-property (property &optional pos) "Set the PROPERTY in the current buffer. Can pass the position as POS if already computed." (when-let ((pos (or pos (elken/find-time-property property)))) (save-excursion (goto-char pos) (if (looking-at-p " ") (forward-char) (insert " ")) (delete-region (point) (line-end-position)) (let* ((now (format-time-string "<%Y-%m-%d %H:%M>"))) (insert now)))))

(add-hook! 'before-save-hook (when (derived-mode-p 'org-mode) (elken/set-time-property "LAST_MODIFIED") (elken/set-time-property "DATE_UPDATED"))) #+end_src

** Better snippets Programmers are, by design, lazy

#+begin_src emacs-lisp (use-package! org-tempo :after org :init (add-to-list 'org-structure-template-alist '("sh" . "src shell")) (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))) #+end_src

** Roam Let's jump on the bandwagon and start taking useful notes.

#+begin_src emacs-lisp (setq org-roam-directory (expand-file-name "roam" org-directory)) #+end_src

*** Templates #+begin_src emacs-lisp (after! org-roam (setq org-roam-capture-templates `(("d" "default" plain (file ,(expand-file-name "templates/roam-default.org" doom-private-dir)) :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "") :unnarrowed t)))) #+end_src

** Capture It's about time I start using =org-capture=, but because I'm a developer I'm inhernetly lazy so time to steal from other people.

Useful wrapper package for creating more declarative templates #+begin_src elisp (package! doct) #+end_src

#+begin_src emacs-lisp (use-package! doct :defer t :commands (doct)) #+end_src

*** Prettify Improve the look of the capture dialog (idea borrowed from [[https://github.com/tecosaur][tecosaur]]) #+begin_src emacs-lisp (defun org-capture-select-template-prettier (&optional keys) "Select a capture template, in a prettier way than default Lisp programs can force the template by setting KEYS to a string." (let ((org-capture-templates (or (org-contextualize-keys (org-capture-upgrade-templates org-capture-templates) org-capture-templates-contexts) '(("t" "Task" entry (file+headline "" "Tasks") "* TODO %?\n %u\n %a"))))) (if keys (or (assoc keys org-capture-templates) (error "No capture template referred to by "%s" keys" keys)) (org-mks org-capture-templates "Select a capture template\n━━━━━━━━━━━━━━━━━━━━━━━━━" "Template key: " `(("q" ,(concat (all-the-icons-octicon "stop" :face 'all-the-icons-red :v-adjust 0.01) "\tAbort"))))))) (advice-add 'org-capture-select-template :override #'org-capture-select-template-prettier)

(defun org-mks-pretty (table title &optional prompt specials) "Select a member of an alist with multiple keys. Prettified.

TABLE is the alist which should contain entries where the car is a string. There should be two types of entries.

  1. prefix descriptions like ("a" "Description") This indicates that `a' is a prefix key for multi-letter selection, and that there are entries following with keys like "ab", "ax"…

  2. Select-able members must have more than two elements, with the first being the string of keys that lead to selecting it, and the second a short description string of the item.

The command will then make a temporary buffer listing all entries that can be selected with a single key, and all the single key prefixes. When you press the key for a single-letter entry, it is selected. When you press a prefix key, the commands (and maybe further prefixes) under this key will be shown and offered for selection.

TITLE will be placed over the selection in the temporary buffer, PROMPT will be used when prompting for a key. SPECIALS is an alist with ("key" "description") entries. When one of these is selected, only the bare key is returned." (save-window-excursion (let ((inhibit-quit t) (buffer (org-switch-to-buffer-other-window "Org Select")) (prompt (or prompt "Select: ")) case-fold-search current) (unwind-protect (catch 'exit (while t (setq-local evil-normal-state-cursor (list nil)) (erase-buffer) (insert title "\n\n") (let ((des-keys nil) (allowed-keys '("\C-g")) (tab-alternatives '("\s" "\t" "\r")) (cursor-type nil)) ;; Populate allowed keys and descriptions keys ;; available with CURRENT selector. (let ((re (format "\%s\\(.\\)\\'" (if current (regexp-quote current) ""))) (prefix (if current (concat current " ") ""))) (dolist (entry table) (pcase entry ;; Description. ((,(and key (pred (string-match re))) ,desc) (let ((k (match-string 1 key))) (push k des-keys) ;; Keys ending in tab, space or RET are equivalent. (if (member k tab-alternatives) (push "\t" allowed-keys) (push k allowed-keys)) (insert (propertize prefix 'face 'font-lock-comment-face) (propertize k 'face 'bold) (propertize "›" 'face 'font-lock-comment-face) " " desc "…" "\n"))) ;; Usable entry. ((,(and key (pred (string-match re))) ,desc . ,_) (let ((k (match-string 1 key))) (insert (propertize prefix 'face 'font-lock-comment-face) (propertize k 'face 'bold) " " desc "\n") (push k allowed-keys))) (_ nil)))) ;; Insert special entries, if any. (when specials (insert "─────────────────────────\n") (pcase-dolist ((,key ,description) specials) (insert (format "%s %s\n" (propertize key 'face '(bold all-the-icons-red)) description)) (push key allowed-keys))) ;; Display UI and let user select an entry or ;; a sub-level prefix. (goto-char (point-min)) (unless (pos-visible-in-window-p (point-max)) (org-fit-window-to-buffer)) (let ((pressed (org--mks-read-key allowed-keys prompt nil))) (setq current (concat current pressed)) (cond ((equal pressed "\C-g") (user-error "Abort")) ((equal pressed "ESC") (user-error "Abort")) ;; Selection is a prefix: open a new menu. ((member pressed des-keys)) ;; Selection matches an association: return it. ((let ((entry (assoc current table))) (and entry (throw 'exit entry)))) ;; Selection matches a special entry: return the ;; selection prefix. ((assoc current specials) (throw 'exit current)) (t (error "No entry available"))))))) (when buffer (kill-buffer buffer)))))) (advice-add 'org-mks :override #'org-mks-pretty) #+end_src

The [[file:~/.emacs.doom/bin/org-capture][org-capture bin]] is rather nice, but I'd be nicer with a smaller frame, and no modeline.

#+begin_src emacs-lisp (setf (alist-get 'height +org-capture-frame-parameters) 15) ;; (alist-get 'name +org-capture-frame-parameters) "❖ Capture") ;; ATM hardcoded in other places, so changing breaks stuff (setq +org-capture-fn (lambda () (interactive) (set-window-parameter nil 'mode-line-format 'none) (org-capture))) #+end_src

Sprinkle in some =doct= utility functions #+begin_src emacs-lisp (defun +doct-icon-declaration-to-icon (declaration) "Convert :icon declaration to icon" (let ((name (pop declaration)) (set (intern (concat "all-the-icons-" (plist-get declaration :set)))) (face (intern (concat "all-the-icons-" (plist-get declaration :color)))) (v-adjust (or (plist-get declaration :v-adjust) 0.01))) (apply set `(,name :face ,face :v-adjust ,v-adjust))))

(defun +doct-iconify-capture-templates (groups) "Add declaration's :icon to each template group in GROUPS." (let ((templates (doct-flatten-lists-in groups))) (setq doct-templates (mapcar (lambda (template) (when-let* ((props (nthcdr (if (= (length template) 4) 2 5) template)) (spec (plist-get (plist-get props :doct) :icon))) (setf (nth 1 template) (concat (+doct-icon-declaration-to-icon spec) "\t" (nth 1 template)))) template) templates))))

(setq doct-after-conversion-functions '(+doct-iconify-capture-templates)) #+end_src

*** Templates

And we can now add some templates! This isn't even remotely set in stone, I wouldn't even describe them as set in /jelly/ really. #+begin_src emacs-lisp (after! org-capture (setq org-capture-templates (doct `(("Home" :keys "h" :icon ("home" :set "octicon" :color "cyan") :file "Home.org" :prepend t :headline "Inbox" :template ("* TODO %?" "%i %a")) ("Work" :keys "w" :icon ("business" :set "material" :color "yellow") :file "Work.org" :prepend t :headline "Inbox" :template ("* TODO %?" "SCHEDULED: %^{Schedule:}t" "DEADLINE: %^{Deadline:}t" "%i %a")) ("Note" :keys "n" :icon ("sticky-note" :set "faicon" :color "yellow") :file "Notes.org" :template ("* ?" "%i %a")) ("Journal" :keys "j" :icon ("calendar" :set "faicon" :color "pink") :type plain :function (lambda () (org-journal-new-entry t) (unless (eq org-journal-file-type 'daily) (org-narrow-to-subtree)) (goto-char (point-max))) :template "* %(format-time-string org-journal-time-format)%^{Title}\n%i%?" :jump-to-captured t :immediate-finish t) ("Project" :keys "p" :icon ("repo" :set "octicon" :color "silver") :prepend t :type entry :headline "Inbox" :template ("* %{keyword} %?" "%i" "%a") :file "" :custom (:keyword "") :children (("Task" :keys "t" :icon ("checklist" :set "octicon" :color "green") :keyword "TODO" :file +org-capture-project-todo-file) ("Note" :keys "n" :icon ("sticky-note" :set "faicon" :color "yellow") :keyword "%U" :file +org-capture-project-notes-file))) ))))

#+end_src

** Export *** LaTeX A necessary evil. I hate it, it hates me, but it makes my PDF documents look nice.

**** Preambles Various preamble setups to improve the overall look of several items

#+begin_src emacs-lisp (defvar org-latex-caption-preamble " \usepackage{subcaption} \usepackage[hypcap=true]{caption} \setkomafont{caption}{\sffamily\small} \setkomafont{captionlabel}{\upshape\bfseries} \captionsetup{justification=raggedright,singlelinecheck=true} \usepackage{capt-of} % required by Org " "Preamble that improves captions.")

(defvar org-latex-checkbox-preamble " \newcommand{\checkboxUnchecked}{$\square$} \newcommand{\checkboxTransitive}{\rlap{\raisebox{-0.1ex}{\hspace{0.35ex}\Large\textbf -}}$\square$} \newcommand{\checkboxChecked}{\rlap{\raisebox{0.2ex}{\hspace{0.35ex}\scriptsize \ding{52}}}$\square$} " "Preamble that improves checkboxes.")

(defvar org-latex-box-preamble " % args = #1 Name, #2 Colour, #3 Ding, #4 Label \newcommand{\defsimplebox}[4]{% \definecolor{#1}{HTML}{#2} \newenvironment{#1}[1][] {% \par\vspace{-0.7\baselineskip}% \textcolor{#1}{#3} \textcolor{#1}{\textbf{\def\temp{##1}\ifx\temp\empty#4\else##1\fi}}% \vspace{-0.8\baselineskip} \begin{addmargin}[1em]{1em} }{% \end{addmargin} \vspace{-0.5\baselineskip} }% } " "Preamble that provides a macro for custom boxes.") #+end_src

**** Conditional features Don't always need everything in LaTeX, so only add it what we need when we need it.

#+begin_src emacs-lisp (defvar org-latex-italic-quotes t "Make "quote" environments italic.") (defvar org-latex-par-sep t "Vertically seperate paragraphs, and remove indentation.")

(defvar org-latex-conditional-features '(("\[\[\(?:file\|https?\):\(?:[^]]\|\\\]\)+?\.\(?:eps\|pdf\|png\|jpeg\|jpg\|jbig2\)\]\]" . image) ("\[\[\(?:file\|https?\):\(?:[^]]+?\|\\\]\)\.svg\]\]\|\\includesvg" . svg) ("^[ \t]|" . table) ("cref:\|\cref{\|\[\[[^\]]+\]\]" . cleveref) ("[;\\]?\b[A-Z][A-Z]+s?[^A-Za-z]" . acronym) ("\+[^ ].[^ ]\+\|[^ ].*[^ ]\|\\uu?line\|\\uwave\|\\sout\|\\xout\|\\dashuline\|\dotuline\|\markoverwith" . underline) (":float wrap" . float-wrap) (":float sideways" . rotate) ("^[ \t]#\+caption:\|\\caption" . caption) ("\[\[xkcd:" . (image caption)) ((and org-latex-italic-quotes "^[ \t]#\+begin_quote\|\\begin{quote}") . italic-quotes) (org-latex-par-sep . par-sep) ("^[ \t]\(?:[-+]\|[0-9]+[.)]\|[A-Za-z]+[.)]\) \[[ -X]\]" . checkbox) ("^[ \t]#\+begin_warning\|\\begin{warning}" . box-warning) ("^[ \t]#\+begin_info\|\\begin{info}" . box-info) ("^[ \t]#\+begin_success\|\\begin{success}" . box-success) ("^[ \t]#\+begin_error\|\\begin{error}" . box-error)) "Org feature tests and associated LaTeX feature flags.

Alist where the car is a test for the presense of the feature, and the cdr is either a single feature symbol or list of feature symbols.

When a string, it is used as a regex search in the buffer. The feature is registered as present when there is a match.

The car can also be a

  • symbol, the value of which is fetched
  • function, which is called with info as an argument
  • list, which is `eval'uated

If the symbol, function, or list produces a string: that is used as a regex search in the buffer. Otherwise any non-nil return value will indicate the existance of the feature.")

(defvar org-latex-feature-implementations '((image :snippet "\usepackage{graphicx}" :order 2) (svg :snippet "\usepackage{svg}" :order 2) (table :snippet "\usepackage{longtable}\n\usepackage{booktabs}" :order 2) (cleveref :snippet "\usepackage[capitalize]{cleveref}" :order 1) (underline :snippet "\usepackage[normalem]{ulem}" :order 0.5) (float-wrap :snippet "\usepackage{wrapfig}" :order 2) (rotate :snippet "\usepackage{rotating}" :order 2) (caption :snippet org-latex-caption-preamble :order 2.1) (acronym :snippet "\newcommand{\acr}[1]{\protect\textls*[110]{\scshape #1}}\n\newcommand{\acrs}{\protect\scalebox{.91}[.84]{\hspace{0.15ex}s}}" :order 0.4) (italic-quotes :snippet "\renewcommand{\quote}{\list{}{\rightmargin\leftmargin}\item\relax\em}\n" :order 0.5) (par-sep :snippet "\setlength{\parskip}{\baselineskip}\n\setlength{\parindent}{0pt}\n" :order 0.5) (.pifont :snippet "\usepackage{pifont}") (checkbox :requires .pifont :order 3 :snippet (concat (unless (memq 'maths features) "\usepackage{amssymb} % provides \square") org-latex-checkbox-preamble)) (.fancy-box :requires .pifont :snippet org-latex-box-preamble :order 3.9) (box-warning :requires .fancy-box :snippet "\defsimplebox{warning}{e66100}{\ding{68}}{Warning}" :order 4) (box-info :requires .fancy-box :snippet "\defsimplebox{info}{3584e4}{\ding{68}}{Information}" :order 4) (box-success :requires .fancy-box :snippet "\defsimplebox{success}{26a269}{\ding{68}}{\vspace{-\baselineskip}}" :order 4) (box-error :requires .fancy-box :snippet "\defsimplebox{error}{c01c28}{\ding{68}}{Important}" :order 4)) "LaTeX features and details required to implement them.

List where the car is the feature symbol, and the rest forms a plist with the following keys:

  • :snippet, which may be either

    • a string which should be included in the preamble
    • a symbol, the value of which is included in the preamble
    • a function, which is evaluated with the list of feature flags as its single argument. The result of which is included in the preamble
    • a list, which is passed to `eval', with a list of feature flags available as "features"
  • :requires, a feature or list of features that must be available

  • :when, a feature or list of features that when all available should cause this to be automatically enabled.

  • :prevents, a feature or list of features that should be masked

  • :order, for when ordering is important. Lower values appear first. The default is 0.

Features that start with ! will be eagerly loaded, i.e. without being detected.") #+end_src

First, we need to detect which features we actually need

#+begin_src emacs-lisp (defun org-latex-detect-features (&optional buffer info) "List features from `org-latex-conditional-features' detected in BUFFER." (let ((case-fold-search nil)) (with-current-buffer (or buffer (current-buffer)) (delete-dups (mapcan (lambda (construct-feature) (when (let ((out (pcase (car construct-feature) ((pred stringp) (car construct-feature)) ((pred functionp) (funcall (car construct-feature) info)) ((pred listp) (eval (car construct-feature))) ((pred symbolp) (symbol-value (car construct-feature))) (_ (user-error "org-latex-conditional-features key %s unable to be used" (car construct-feature)))))) (if (stringp out) (save-excursion (goto-char (point-min)) (re-search-forward out nil t)) out)) (if (listp (cdr construct-feature)) (cdr construct-feature) (list (cdr construct-feature))))) org-latex-conditional-features))))) #+end_src

Then we need to expand them and sort them according to the above definitions

#+begin_src emacs-lisp (defun org-latex-expand-features (features) "For each feature in FEATURES process :requires, :when, and :prevents keywords and sort according to :order." (dolist (feature features) (unless (assoc feature org-latex-feature-implementations) (error "Feature %s not provided in org-latex-feature-implementations" feature))) (setq current features) (while current (when-let ((requirements (plist-get (cdr (assq (car current) org-latex-feature-implementations)) :requires))) (setcdr current (if (listp requirements) (append requirements (cdr current)) (cons requirements (cdr current))))) (setq current (cdr current))) (dolist (potential-feature (append features (delq nil (mapcar (lambda (feat) (when (plist-get (cdr feat) :eager) (car feat))) org-latex-feature-implementations)))) (when-let ((prerequisites (plist-get (cdr (assoc potential-feature org-latex-feature-implementations)) :when))) (setf features (if (if (listp prerequisites) (cl-every (lambda (preq) (memq preq features)) prerequisites) (memq prerequisites features)) (append (list potential-feature) features) (delq potential-feature features))))) (dolist (feature features) (when-let ((prevents (plist-get (cdr (assoc feature org-latex-feature-implementations)) :prevents))) (setf features (cl-set-difference features (if (listp prevents) prevents (list prevents)))))) (sort (delete-dups features) (lambda (feat1 feat2) (if (< (or (plist-get (cdr (assoc feat1 org-latex-feature-implementations)) :order) 1) (or (plist-get (cdr (assoc feat2 org-latex-feature-implementations)) :order) 1)) t nil)))) #+end_src

Finally, we can create the preamble to be inserted

#+begin_src emacs-lisp (defun org-latex-generate-features-preamble (features) "Generate the LaTeX preamble content required to provide FEATURES. This is done according to org-latex-feature-implementations'" (let ((expanded-features (org-latex-expand-features features))) (concat (format "\n%% features: %s\n" expanded-features) (mapconcat (lambda (feature) (when-let ((snippet (plist-get (cdr (assoc feature org-latex-feature-implementations)) :snippet))) (concat (pcase snippet ((pred stringp) snippet) ((pred functionp) (funcall snippet features)) ((pred listp) (eval (let ((features ',features)) (,@snippet)))) ((pred symbolp) (symbol-value snippet)) (_ (user-error "org-latex-feature-implementations :snippet value %s unable to be used" snippet))) "\n"))) expanded-features "") "% end features\n"))) #+end_src

Last step, some advice to hook in all of the above to work

#+begin_src emacs-lisp (defvar info--tmp nil)

(defadvice! org-latex-save-info (info &optional t_ s_) :before #'org-latex-make-preamble (setq info--tmp info))

(defadvice! org-splice-latex-header-and-generated-preamble-a (orig-fn tpl def-pkg pkg snippets-p &optional extra) "Dynamically insert preamble content based on `org-latex-conditional-preambles'." :around #'org-splice-latex-header (let ((header (funcall orig-fn tpl def-pkg pkg snippets-p extra))) (if snippets-p header (concat header (org-latex-generate-features-preamble (org-latex-detect-features nil info--tmp)) "\n")))) #+end_src

**** Tectonic Tectonic is the hot new thing, which also means I can get rid of my tex installation.

#+begin_src emacs-lisp (setq-default org-latex-pdf-process '("tectonic -Z shell-escape --outdir=%o %f")) #+end_src

**** TODO Classes Simple base header shared by all defines classes

#+name: base-template #+begin_src latex \documentclass[10pt]{scrartcl} [PACKAGES] [DEFAULT-PACKAGES] [EXTRA] \setmainfont[Ligatures=TeX]{Overpass} \setmonofont[Ligatures=TeX]{Iosevka Nerd Font Mono} #+end_src

#+name: chameleon-template #+begin_src latex :noweb yes % Using chameleon <> #+end_src

#+name: work-template #+begin_src latex :noweb yes % Using work <> \usepackage{fontawesome5} \usepackage{tcolorbox} \usepackage{fancyhdr} \usepackage{lastpage} \pagestyle{fancy} \fancyhead{} \fancyhead[RO, LE]{} #+end_src

Now for some class setup (likely to change over time)

#+begin_src emacs-lisp :noweb no-export (after! ox-latex (add-to-list 'org-latex-classes '("chameleon" " <> " ("\section{%s}" . "\section*{%s}") ("\subsection{%s}" . "\subsection*{%s}") ("\subsubsection{%s}" . "\subsubsection*{%s}") ("\paragraph{%s}" . "\paragraph*{%s}") ("\subparagraph{%s}" . "\subparagraph*{%s}")))) #+end_src

And some saner default for them

#+begin_src emacs-lisp (after! ox-latex (setq org-latex-default-class "chameleon" org-latex-tables-booktabs t org-latex-hyperref-template "\colorlet{greenyblue}{blue!70!green} \colorlet{blueygreen}{blue!40!green} \providecolor{link}{named}{greenyblue} \providecolor{cite}{named}{blueygreen} \hypersetup{ pdfauthor={%a}, pdftitle={%t}, pdfkeywords={%k}, pdfsubject={%d}, pdfcreator={%c}, pdflang={%L}, breaklinks=true, colorlinks=true, linkcolor=, urlcolor=link, citecolor=cite\n} \urlstyle{same} " org-latex-reference-command "\cref{%s}")) #+end_src

**** Packages Add some packages (also very likely to change)

#+begin_src emacs-lisp (setq org-latex-default-packages-alist `(("AUTO" "inputenc" t ("pdflatex")) ("T1" "fontenc" t ("pdflatex")) ("" "fontspec" t) ("" "xcolor" nil) ("" "hyperref" nil))) #+end_src

**** Pretty code blocks Teco is the goto for this, so basically just ripping off him.

#+begin_src elisp (package! engrave-faces :recipe (:host github :repo "tecosaur/engrave-faces")) #+end_src

#+begin_src emacs-lisp (use-package! engrave-faces-latex :after ox-latex :config (setq org-latex-listings 'engraved)) #+end_src

#+begin_src emacs-lisp (use-package! engrave-faces-html :after ox-html :config (setq org-latex-listings 'engraved)) #+end_src

#+begin_src emacs-lisp (defvar-local org-export-has-code-p nil)

(defadvice! org-export-expect-no-code (&rest _) :before #'org-export-as (setq org-export-has-code-p nil))

(defadvice! org-export-register-code (&rest _) :after #'org-latex-src-block :after #'org-latex-inline-src-block-engraved (setq org-export-has-code-p t))

(defadvice! org-latex-example-block-engraved (orig-fn example-block contents info) "Like `org-latex-example-block', but supporting an engraved backend" :around #'org-latex-example-block (let ((output-block (funcall orig-fn example-block contents info))) (if (eq 'engraved (plist-get info :latex-listings)) (format "\begin{Code}[alt]\n%s\n\end{Code}" output-block) output-block))) #+end_src

**** ox-chameleon Chameleons are cool, not having to touches faces is cooler (not the COVID kind)

#+begin_src elisp (package! ox-chameleon :recipe (:host github :repo "tecosaur/ox-chameleon")) #+end_src

#+begin_src emacs-lisp (use-package! ox-chameleon :after ox) #+end_src

**** Beamer Starting to look into beamer for creating presentations, seems like we need to +steal+ borrow more config from Tecosaur.

Metropolis is a nice theme, with a tiny adjustment it might be the best.

#+begin_src emacs-lisp (setq org-beamer-theme "[progressbar=foot]metropolis") #+end_src

#+begin_src emacs-lisp :noweb yes (defun org-beamer-p (info) (eq 'beamer (and (plist-get info :back-end) (org-export-backend-name (plist-get info :back-end)))))

(add-to-list 'org-latex-conditional-features '(org-beamer-p . beamer) t) (add-to-list 'org-latex-feature-implementations '(beamer :requires .missing-koma :prevents (italic-quotes condensed-lists)) t) (add-to-list 'org-latex-feature-implementations '(.missing-koma :snippet "\usepackage{scrextend}" :order 2) t) #+end_src

And lastly, a small tweak to improve how sections are divided

#+begin_src emacs-lisp (setq org-beamer-frame-level 2) #+end_src

*** (sub|super)script characters Annoying having to gate these, so let's fix that

#+begin_src emacs-lisp (setq org-export-with-sub-superscripts '{}) #+end_src

  • Languages Configuration for various programming languages.

** Dart Simple hook/project mode to update translations on save.

#+begin_src emacs-lisp (after! lsp-dart (customize-set-variable 'lsp-dart-dap-extension-version "3.46.1")

(defun lsp-dart--intl-update () "Update translations on save." (when (and +dart-intl-mode buffer-file-name (string-search "lib/l10n" buffer-file-name)) (lsp-dart--run-command (lsp-dart-flutter-command) "pub run intl_utils:generate")))

(def-project-mode! +dart-intl-mode :modes '(json-mode) :files (and "pubspec.yaml" "lib/l10n") :on-enter (add-hook 'after-save-hook #'lsp-dart--intl-update) :on-exit (remove-hook 'after-save-hook #'lsp-dart--intl-update))

(setq lsp-dart-dap-flutter-hot-reload-on-save t)) #+end_src

** Rust Rust is the hot new thing, guess I better jump on the bandwagon if I want to stay cool.

#+begin_src emacs-lisp (after! lsp-mode (setq lsp-rust-analyzer-display-parameter-hints t lsp-rust-analyzer-server-display-inlay-hints t)) #+end_src

** C# Projectile doesn't recognise these projects properly, so we have to fix that

#+begin_src emacs-lisp (after! projectile (defun projectile-dotnet-project-p () "Check if a project contains a .sln file at the project root, or either a .csproj or .fsproj file at either the project root or within src//." (or (projectile-verify-file-wildcard "?.sln") (projectile-verify-file-wildcard "?.csproj") (projectile-verify-file-wildcard "src//?.csproj") (projectile-verify-file-wildcard "?.fsproj") (projectile-verify-file-wildcard "src//?*.fsproj")))) #+end_src

** ELISP /Finally/ get the completion in elisp working (not sure why it's been so bad for so long...)

#+begin_src emacs-lisp (set-company-backend! 'emacs-lisp-mode 'company-capf 'company-yasnippet) #+end_src

** LSP/DAP *** Increase variable line length By default this is /way/ too short.

#+begin_src emacs-lisp (setq dap-ui-variable-length 200) #+end_src

*** Ignore files in xref PHP is a dumb language (don't get me started...), as such we need extra files to get decent completion & documentation. 0% of the time will we want to use them as references, so we won't.

#+begin_src emacs-lisp (defvar xref-ignored-files '("_ide_helper_models.php" "_ide_helper.php") "List of files to be ignored by `xref'.")

(defun xref-ignored-file-p (item) "Return t if `item' should be ignored." (seq-some (lambda (cand) (string-suffix-p cand (oref (xref-item-location item) file))) xref-ignored-files))

(defadvice! +lsp--ignored-locations-to-xref-items-a (items) "Remove ignored files from list of xref-items." :filter-return #'lsp--locations-to-xref-items (cl-remove-if #'xref-ignored-file-p items))

(defadvice! +lsp-ui-peek--ignored-locations-a (items) "Remove ignored files from list of xref-items." :filter-return #'lsp-ui-peek--get-references (cl-remove-if #'xref-ignored-file-p items)) #+end_src

*** Improve completions The default completions are quite bad

#+begin_src emacs-lisp (after! lsp-mode (setq +lsp-company-backends '(:separate company-capf company-yasnippet company-dabbrev))) #+end_src

*** Ignore directories Add some extra ignored directories for =+lsp=.

#+begin_src emacs-lisp (after! lsp-mode (add-to-list 'lsp-file-watch-ignored-directories "[/\\]\vendor")) #+end_src

And some more for projectile

#+begin_src emacs-lisp (after! projectile (add-to-list 'projectile-globally-ignored-directories "vendor")) #+end_src

** PHP *** Web-mode setup #+begin_src emacs-lisp (after! web-mode (pushnew! web-mode-engines-alist '(("blade" . "\.blade\.")))) #+end_src

*** Intelephense Because I'm a massive sellout who likes features

#+begin_src emacs-lisp (after! eglot (setq lsp-intelephense-licence-key (or (ignore-errors (fetch-auth-source :user "intelephense") nil)))) #+end_src

*** Eglot Trying out this eglot thing for a bit, let's see how it goes.

Make sure it's loaded in php-mode

#+begin_src emacs-lisp (after! eglot (add-hook 'php-mode-hook 'eglot-ensure) (add-hook 'dart-mode-hook 'eglot-ensure)) #+end_src

Set some config needed for the server

#+begin_src emacs-lisp (when (modulep! :tools lsp +eglot) (defvar php-intelephense-storage-path (expand-file-name "lsp-intelephense" doom-etc-dir)) (defvar php-intelephense-command (expand-file-name "lsp/npm/intelephense/bin/intelephense" doom-etc-dir))) #+end_src

And set the server to be loaded

#+begin_src emacs-lisp (after! eglot (defclass eglot-php (eglot-lsp-server) () :documentation "PHP's Intelephense") (cl-defmethod eglot-initialization-options ((server eglot-php)) "Passes through required intelephense options" (:storagePath ,php-intelephense-storage-path :licenceKey ,lsp-intelephense-licence-key :clearCache t)) (add-to-list 'eglot-server-programs ((php-mode phps-mode) . (eglot-php . (,php-intelephense-command "--stdio"))))) #+end_src

  • Snippets I constantly find myself complaining I don't have snippets setup, and yet I always forget to set snippets up. [[https://www.youtube.com/watch?v=sc5iTNVEOAg][My own worst enemy]]? Probably. But who's keeping score...

** Cape Capes are kinda cool #+begin_src elisp (package! cape-yasnippet :recipe (:local-repo "~/build/elisp/cape-yasnippet" :build (:not compile))) #+end_src

#+begin_src emacs-lisp (use-package! cape-yasnippet :after cape :init (add-to-list 'completion-at-point-functions #'cape-yasnippet) (after! lsp-mode (add-hook 'lsp-managed-mode-hook #'cape-yasnippet--lsp)) (after! eglot (add-hook 'eglot-managed-mode-hook #'cape-yasnippet--eglot))) #+end_src

** Snippet definitions :PROPERTIES: :header-args:snippet: :mkdirp yes :tangle (expand-file-name (downcase (elt (org-get-outline-path t) (- (length (org-get-outline-path t)) 1))) (expand-file-name (downcase (elt (org-get-outline-path t) 1)) "snippets")) :END:

A collection of snippets tangled using clever magic.

*** Org-mode **** __ #+begin_src snippet

-- mode: snippet --

name: Org template

--

#+title: ${1:(s-titleized-words (replace-regexp-in-string "^[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9]-" "" (file-name-base (or buffer-file-name "new buffer"))))} #+author: ${2:(user-full-name)} #+date: ${3:(format-time-string "%Y-%m-%d")} #+latex_class: chameleon

$0 #+end_src

*** PHP-Mode **** function #+begin_src snippet

-- mode: snippet --

name: function

key: fu

uuid: fu

expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))

--

${1:$$(yas-auto-next (yas-completing-read "Visibility (public): " '("public" "private" "protected") nil nil nil nil "public"))} function ${2:name}($3) { $0 } #+end_src

**** php #+begin_src snippet

-- mode: snippet --

name: <?php

key: php

uuid: php

--

setContext('$2', [ $3 ]); `%`$0 }); #+end_src * Packages Place to put packages that don't have a guaranteed home yet. ** Disabled/unpin Packages to be unpinned or just completely disabled #+begin_src elisp (disable-packages! evil-escape) (unpin! vterm) (unpin! lsp-mode) (unpin! lsp-dart) (when (modulep! :tools lsp +eglot) (unpin! eglot)) (unpin! forge) (when (modulep! :completion corfu) (unpin! evil-collection)) #+end_src ** embark-vc Embark additions to improve various vc operations #+begin_src elisp (package! embark-vc) #+end_src #+begin_src emacs-lisp (use-package! embark-vc :after embark) #+end_src ** laravel-mode Not yet fit for human consumption, but fit for mine because I'm +sub+ super-human #+begin_src elisp (package! laravel-mode :recipe (:local-repo "~/build/elisp/laravel-mode" :build (:not compile))) #+end_src #+begin_src emacs-lisp (use-package! laravel-tinker :after php-mode :init (set-popup-rule! "^\\tinker:" :vslot -5 :size 0.35 :select t :modeline nil :ttl nil) (map! :localleader :map php-mode-map :desc "Toggle a project-local Tinker REPL" "o t" #'laravel-tinker-toggle)) #+end_src ** prescient Need to add this into company module when I've tested #+begin_src elisp (when (modulep! :completion company) (package! company-prescient)) #+end_src #+begin_src emacs-lisp (when (modulep! :completion company) (use-package! company-prescient :after company :hook (company-mode . company-prescient-mode) :hook (company-prescient-mode . prescient-persist-mode) :config (setq prescient-save-file (concat doom-cache-dir "prescient-save.el") history-length 1000))) #+end_src ** Rainbow Identifiers *** TODO Fix in web-mode Web-mode has normal text which should be ignored. #+begin_src elisp (package! rainbow-identifiers) #+end_src #+begin_src emacs-lisp (use-package! rainbow-identifiers ;; :hook (php-mode . rainbow-identifiers-mode) ;; :hook (org-mode . (lambda () (rainbow-identifiers-mode -1))) ;; :hook (web-mode . (lambda () (rainbow-identifiers-mode -1))) :config (setq rainbow-identifiers-faces-to-override '(php-variable-name php-property-name php-variable-sigil web-mode-variable-name-face))) #+end_src ** Cucumber Needed for feature test files #+begin_src elisp (package! feature-mode) #+end_src #+begin_src emacs-lisp (use-package! feature-mode :mode "\\.feature$") #+end_src ** RPM Spec Needed for rpm files to not be treated poorly (there, there) #+begin_src elisp (package! rpm-spec-mode :recipe (:host github :repo "bhavin192/rpm-spec-mode")) #+end_src #+begin_src emacs-lisp (use-package! rpm-spec-mode :mode "\\.spec\\(\\.in\\)?$") #+end_src * Spelling #+begin_src emacs-lisp (setq ispell-program-name "aspell" ispell-extra-args '("--sug-mode=ultra" "--lang=en_GB") ispell-dictionary "en" ispell-personal-dictionary "~/Nextcloud/dict") (after! cape (setq cape-dict-file (if (file-exists-p ispell-personal-dictionary) ispell-personal-dictionary cape-dict-file))) #+end_src * Slack Includes a couple of niceties locally, these /may/ end up in a module one day. #+begin_src elisp (package! slack) #+end_src #+begin_src emacs-lisp (use-package! slack :commands (slack-start) :custom (slack-buffer-emojify t) (slack-prefer-current-team t) (slack-enable-global-mode-string t) (slack-modeline-count-only-subscribed-channel nil) :init (require 'auth-source) (require 'slack-util) (defun +slack/register-team (&rest plist) "Given an email, get the `:token' and `:cookie' for the given EMAIL." (let ((token (auth-source-pick-first-password :host (plist-get plist :host) :user (plist-get plist :email))) (cookie (auth-source-pick-first-password :host (plist-get plist :host) :user (format "%s^cookie" (plist-get plist :email))))) (apply 'slack-register-team (slack-merge-plist `(:token ,token :cookie ,cookie) plist)))) (defun +slack/post-standup-message () "Create a standup message to send to a room." (interactive) (let* ((team (slack-team-select)) (channel (slack-room-select (cl-loop for team in (list team) for channels = (append (slack-team-channels team) (slack-team-ims team)) nconc channels) team)) (buf (slack-create-room-message-compose-buffer channel team))) (slack-buffer-display buf) (yas-expand-snippet (yas-lookup-snippet "Standup template" 'slack-message-compose-buffer-mode))))) #+end_src * Reaper Emacs client for Harvest, the time tracker we use at =$DAYJOB=. Over time, this will likely become more config than just a dump of macros. #+begin_src elisp (package! reaper) #+end_src #+begin_src emacs-lisp (use-package! reaper :init (map! :leader :n :desc "Track time" "t t" #'reaper)) #+end_src * Graphviz Some config to help with graphviz #+begin_src elisp (package! graphviz-dot-mode) #+end_src #+begin_src emacs-lisp (use-package! graphviz-dot-mode :init (after! company (require 'company-graphviz-dot))) #+end_src * Jira Config related to setting up Jira. ** org-jira Used for accessing Jira tasks through org-mode. Jira's interface is /quite/ a mess so I'd rather not use it as much. You know what we love? =org-mode=. #+begin_src elisp (package! org-jira) #+end_src #+begin_src emacs-lisp (use-package! org-jira :commands (org-jira-get-issues org-jira-get-issues-from-custom-jql) :init (let ((dir (expand-file-name ".org-jira" (or (getenv "XDG_CONFIG_HOME") (getenv "HOME"))))) (unless (file-directory-p dir) (make-directory dir)) (setq org-jira-working-dir dir))) #+end_src ** jira-workflow Custom package I've written to handle some of my jira workflow usage to get over some of the gripes I have. Still some features left to work on (eg automatic ticket movement) but for the most part it does a decent job #+begin_src elisp (package! jira-workflow :recipe (:host github :repo "elken/jira-workflow")) #+end_src #+begin_src emacs-lisp (use-package! jira-workflow :after dotenv :commands (jira-workflow-start-ticket) :init (map! :desc "Start working on a ticket" :leader "g c B" #'jira-workflow-start-ticket)) #+end_src * Translate I do a decent amount of copy/paste translating stuff for work, so let's make this /easier/. #+begin_src elisp (package! google-translate) #+end_src #+begin_src emacs-lisp (use-package! google-translate :config (defun google-translate--search-tkk () "Search TKK." (list 430675 2721866130)) (setf (alist-get "Kinyarwanda" google-translate-supported-languages-alist) "rw") (setq google-translate-output-destination 'kill-ring) (setq google-translate-translation-directions-alist '(("en" . "fr") ("en" . "rw")))) #+end_src * Exercism Exercism is a useful site for learning a programming language by performing various exercises. You can opt to use either an in-browser editor or your own via a local CLI. Which do you think I want? #+begin_src elisp (package! exercism-modern :recipe (:files (:defaults "icons") :host github :repo "elken/exercism-modern")) #+end_src #+begin_src emacs-lisp (use-package! exercism-modern :commands (exercism-modern-jump exercism-modern-view-tracks)) #+end_src * Local settings Needed some way to manage settings for a local machine, so let's be lazy with it #+begin_src emacs-lisp (when (file-exists-p! "config-local.el" doom-private-dir) (load! "config-local.el" doom-private-dir)) #+end_src ** dotenv Better handle setting of environment variables needed for various tools #+begin_src elisp (package! dotenv :recipe (:host github :repo "pkulev/dotenv.el")) #+end_src #+begin_src emacs-lisp (use-package! dotenv :init (when (file-exists-p (expand-file-name ".env" doom-user-dir)) (add-hook! 'doom-init-ui-hook (defun +dotenv-startup-hook () "Load .env after starting emacs" (dotenv-update-project-env doom-user-dir) (customize-set-variable 'jiralib-url (getenv "JIRALIB_URL")) (customize-set-variable 'jira-workflow-harvest-service-icon-url (getenv "JIRA_WORKFLOW_SERVICE_ICON_URL")) (customize-set-variable 'jira-workflow-harvest-service-name (replace-regexp-in-string "http[s]://" "" jiralib-url))))) :config (add-hook! 'projectile-after-switch-project-hook (defun +dotenv-projectile-hook () "Load .env after changing projects." (dotenv-update-project-env (projectile-project-root))))) #+end_src