dot-emacs

My emacs dotfiles/config
git clone git://git.ethandl.dev/dot-emacs
Log | Files | Refs

config.org (38185B)


      1 #+Startup: overview
      2 #+Title:My emacs config
      3 #+Author:Ethan Long
      4 * What is this document/config/file?
      5 This is a literate config for emacs, using the built-in ~org~ package for Emacs.
      6 * Elpaca
      7 The Elpaca package manager is an alternative to ~use-package~ that works asynchronously in the background. Beyond the initial set-up, it is faster and more reliable than using ~package.el~.
      8 
      9 Its asynchronous nature is also what makes package initialisation a little more involved; packages that need to be set up after others must explicitly specify this. This is mostly a non-issue for my setup, and I try to avoid issues with variable declarations by using ~use-package~ whenever possible.
     10 
     11 Alternatively, if you already have a setup with extensive use of ~use-package~ with some form of synchronous package manager, then you might want to use the ~elpaca-wait~ function, which forces it to complete all package evaluation up until that point. Using this function too often will defeat the purpose of elpaca, as the asynchronicity is exactly what makes it faster than alternatives.
     12 
     13 Another thing of note, if you ever need to use ~after-init-hook~, then make sure to use ~elpaca-after-init-hook~ instead, as the regular ~after-init-hook~ can be triggered before Elpaca has finished its evaluation unless you use the afforementioned ~elpaca-wait~ function.
     14 
     15 Elpaca is installed in =init.el=, as we need to install the latest version of org before we enter here.
     16 ** ~init.el~
     17 #+include: "./init.el" src elisp :tangle no
     18 * Configuration functions
     19 Here I define all of my helper functions in the one place just for convenience. Unfortunately this can have the effect of breaking up some of my configuration, especially for things like ~org~.
     20 ** General programming configuration
     21 Firstly, here are some convenience functions that I find useful for general programming, making the editor act the way that I want it to.
     22 *** Auto buffer formatting
     23 I quite like how the LSP (or some other kind of parser/prettifier) can automatically format the buffer. In other editors, I typically like to have this happen whenever I save.
     24 
     25 This functionality is especially useful for languages like Rust or Haskell where the formatting is handled quite well by the LSP.
     26 #+begin_src emacs-lisp
     27   (defun ethandl/format-buffer ()
     28     (when (and eglot--managed-mode
     29                (eglot-server-capable :documentFormattingProvider))
     30       (eglot-format-buffer)))
     31 #+end_src
     32 In the future, I may extend this function to utilise prettifiers, but I don't really have the need to at the moment.
     33 *** Line numbers
     34 Line numbering in emacs is still a bit of a third class citizen; the primary reason I say this is because the width of the line number column is prone to change for seemingly no good reason. Example: scrolling any file below 80 lines will increase the size to accomodate line numbers greater than 1oo for no reason; the same thing happens around 850 lines for numbers greater than 1000.
     35 
     36 I find that currently I am mostly satisfied by using the variable ~display-line-numbers-grow-only~. This effectively stops most of the annoyance around the width changing when scrolling up and down.
     37 
     38 Following is a function that sets up the requisite variables for the behaviour that I like:
     39 #+begin_src emacs-lisp
     40   (defun ethandl/config-line-numbers ()
     41     ;; Set line numbers to relative
     42     (setq display-line-numbers 'relative
     43           display-line-numbers-width 2
     44           display-line-numbers-current-absolute t
     45           display-line-numbers-grow-only t))
     46 #+end_src
     47 I would recommend adding this to a hook, which I have done in [[*General Emacs/Editor settings]], specifically the ~hook~ section of the ~use-package~ declaration.
     48 *** Code fonts
     49 For convenience, I have defined a helper function that can be called to configure the code fonts.
     50 In typical one-frame setups not using the daemon functionality of emacs, you only need to use this once with the ~default~ parameter set as ~t~.
     51 
     52 With daemon usage, it is not so simple. When using emacs as a daemon, emacs will initially read over this config file in terminal mode, which will stop it from setting the fonts correctly; the only solution to this is to set this function to run every time you create a new frame. I have this exact functionality set up in [[*Font & Ligature Definitions]].
     53 #+begin_src emacs-lisp
     54   (defun ethandl/set-fonts (fontname &optional default)
     55     (set-face-attribute 'default default :font fontname)
     56     (set-face-attribute 'font-lock-comment-face default :slant 'italic)
     57     (set-face-attribute 'font-lock-keyword-face default :slant 'italic)
     58     (set-face-attribute 'mode-line default :font fontname))
     59 #+end_src
     60 *** Tree sitter grammar compilation
     61 I wanted a function that, given an ~alist~ of tree sitter grammars and their associated sources, would clone, build, and set up those grammars.
     62 This function avoids recompiling grammars that are already installed.
     63 #+begin_src emacs-lisp
     64   (defun ethandl/install-treesit-grammars (lang-alist)
     65     (require 'cl-lib)
     66     (when (treesit-available-p)
     67       (mapc #'treesit-install-language-grammar
     68             (cl-remove-if #'treesit-language-available-p (mapcar #'car lang-alist)))))
     69 #+end_src
     70 I use this function in [[*Tree Sitter Setup]].
     71 ** Org mode configuration
     72 Org mode is quite ugly out of the box, at least in my opinion. I use this config, which was inspired by a config that I found online.
     73 
     74 I have used the following guides in informing this setup; it is largely based on the work of Diego Zamboni:
     75  * [[https://zzamboni.org/post/beautifying-org-mode-in-emacs/][Beautifying Org Mode in Emacs by Diego Zamboni]]
     76  * [[https://lepisma.xyz/2017/10/28/ricing-org-mode/][Ricing up Org Mode]]
     77 
     78 I think that the latter guide is by far the prettier, more /"aesthetically pleasing"/ setup deserving of Unixporn or something, but personally I like it slightly less minimalistic.
     79 
     80 *** Basic behavioral configuration
     81 For starters, I define the function ~ethandl/org-base~ as the function to set up org in the way that I generally like. Of primary concern: it sets org to indent on section depth, sets the font to be variable pitch, and turns on visual lines for wrapped lines.
     82 #+begin_src emacs-lisp
     83   (defun ethandl/org-base ()
     84     (require 'org)
     85     (org-indent-mode t)
     86     (variable-pitch-mode t)
     87     (visual-line-mode t)
     88     (setq org-src-fontify-natively t
     89           org-src-tab-acts-natively t
     90           org-hide-leading-stars nil
     91           org-indent-mode-turns-on-hiding-stars nil
     92           org-hide-emphasis-markers t)
     93     (font-lock-add-keywords 'org-mode ;; HACK to fix the catppuccin theme
     94                             '(("^ *\\([-]\\) "
     95                                (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))))
     96 #+end_src
     97 This function should be called when you are setting up org mode, personally I do it when I'm [[*
     98 *** Fonts
     99 The following function is the config for org to set all the fonts. It is still a bit rough around the edges, I'd like to programatically set the heading sizes.
    100 #+begin_src emacs-lisp
    101   (defun ethandl/org-font ()
    102     (require 'org)
    103     (let*
    104         ;; Variable bindings
    105         ((variable-font   (if (x-list-fonts "P052")
    106                               '(:font "P052")
    107                             '(:family "Serif")))
    108          (mono-font       (if (x-list-fonts ethandl/code-font)
    109                               `(:font ,ethandl/code-font)
    110                             '(:family "Mono")))
    111          (base-font-color (face-foreground 'default nil 'default))
    112          (headline        `(:inherit default :weight bold :foreground ,base-font-color))
    113          (body            `(:inherit default :weight thin :foreground ,base-font-color :height 200)) ;; 20 pt
    114          (code            `(:weight regular :height 0.8 :inherit t)))
    115 
    116       ;; Actual function body
    117       (custom-theme-set-faces
    118        'user
    119        `(variable-pitch            ((t (,@variable-font ,@body))) t "Variable width font")
    120        `(fixed-pitch               ((t (,@mono-font ,@code))) t "Fixed width font")
    121        `(org-level-8               ((t (,@variable-font ,@headline :height 1.1))) t "Heading level 8")
    122        `(org-level-7               ((t (,@variable-font ,@headline :height 1.2))) t "Heading level 7")
    123        `(org-level-6               ((t (,@variable-font ,@headline :height 1.3))) t "Heading level 6")
    124        `(org-level-5               ((t (,@variable-font ,@headline :height 1.4))) t "Heading level 5")
    125        `(org-level-4               ((t (,@variable-font ,@headline :height 1.5))) t "Heading level 4")
    126        `(org-level-3               ((t (,@variable-font ,@headline :height 1.6))) t "Heading level 3")
    127        `(org-level-2               ((t (,@variable-font ,@headline :height 1.7))) t "Heading level 2")
    128        `(org-level-1               ((t (,@variable-font ,@headline :height 1.8))) t "Heading level 1")
    129        `(org-document-title        ((t (,@variable-font ,@headline :height 2.0 :underline nil))) t "Document title")
    130        '(org-block                 ((t (:inherit fixed-pitch))) t "Verbatim block")
    131        '(org-code                  ((t (:inherit (shadow fixed-pitch)))) t "Code block")
    132        '(org-document-info         ((t (:foreground "dark orange"))) t "Document info")
    133        '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))) t "Document info keys")
    134        '(org-indent                ((t (:inherit (org-hide fixed-pitch)))) t "Trailing Org Indentation")
    135        '(org-link                  ((t (:foreground "royal blue" :underline t))) t "Links")
    136        '(org-meta-line             ((t (:inherit (font-lock-comment-face fixed-pitch)))) t "Metadata")
    137        '(org-property-value        ((t (:inherit fixed-pitch))) t "Org value")
    138        '(org-special-keyword       ((t (:inherit (font-lock-comment-face fixed-pitch)))) t "Org keyword")
    139        '(org-table                 ((t (:inherit fixed-pitch :foreground "#83a598"))) t "Org table")
    140        '(org-tag                   ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))) t "Org tag")
    141        '(org-verbatim              ((t (:inherit (shadow fixed-pitch)))) t "Verbatim"))))
    142 #+end_src
    143 *** LaTeX
    144 Some configuration for the LaTeX that appears in Org mode. We want to make sure that it's SVG, and also if we have the ability to, we should auto-render it.
    145 #+begin_src emacs-lisp
    146   (defun ethandl/org-latex ()
    147     (setq org-preview-latex-default-process 'dvisvgm))
    148 #+end_src
    149 ** Hooks
    150 *** Before saving
    151 I like to format my buffers every time that I save in other editors, for now this function just calls my other ~format-buffer~ function. Any other action that you might like to do whenever you save can be put here too.
    152 #+begin_src emacs-lisp
    153   (defun ethandl/before-save-hook ()
    154     (ethandl/format-buffer))
    155 #+end_src
    156 A good example of something that you might want to put here would be a ~git~ commit and push every time that you save within an Overleaf git project. This can be useful for when you're trying to avoid actually using the god-forsaken web editor of Overleaf.
    157 * Theme
    158 There are many themes for Emacs that you can use. It can be nice to spice things up every once in a while.
    159 One day I might make my own theme, only if I get too lost in the ricing.
    160 *** Catppuccin
    161 I use the Catppuccin theme normally.
    162 Unfortunately, this theme has some issues and I'm not sure whether they will ever be fixed; I have the fixes all here.
    163 #+begin_src emacs-lisp
    164   ;; Catppuccin Mocha theme
    165   (use-package catppuccin-theme
    166     :config
    167     (load-theme 'catppuccin t)
    168     (setq catppuccin-flavor 'mocha)
    169     ;;(setq catppuccin-flavor 'latte)
    170     (catppuccin-reload)
    171     (add-to-list 'org-src-block-faces
    172                  (list "" (list :foreground (catppuccin-get-color 'green))))
    173     ;; HACK FIX: https://github.com/catppuccin/emacs/issues/61
    174     (defun ctp/text-org-blocks ()
    175       (face-remap-add-relative 'org-block (list :foreground (catppuccin-get-color 'text))))
    176     ;; FIXME: Move this hook to the Org mode configuration so that it is obvious that we are adding this hook.
    177     (add-hook 'org-mode-hook #'ctp/text-org-blocks)
    178     (setq pdf-view-midnight-colors (cons (catppuccin-get-color 'text)
    179                                          (catppuccin-get-color 'base))))
    180 #+end_src
    181 *** Gruber darker
    182 Sometimes I like to use Gruber Darker, following is an example snippet that is not tangled into my actual config.
    183 #+begin_src emacs-lisp :tangle no
    184   (use-package gruber-darker-theme
    185     :config
    186     (load-theme 'gruber-darker t))
    187 #+end_src
    188 *** Emacs titlebar colour
    189 FIXME: Move all configuration of the titlebar colour here for all versions of emacs like the mac port and gtk+.
    190 It's nice to have the titlebar blend for the NS port of emacs:
    191 #+begin_src emacs-lisp
    192   (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
    193   (add-to-list 'default-frame-alist '(ns-appearance . dark))
    194 #+end_src
    195 #+begin_src emacs-lisp
    196   (add-to-list 'default-frame-alist '(mac-transparent-titlebar . t))
    197 #+end_src
    198 
    199 * Font & Ligature Definitions
    200 FIXME: Bring in the ~org~ and variable font definitions here!
    201 *** Code fonts
    202 Here I pick the code font and declare it as the variable ~ethandl/code-font~.
    203 #+begin_src emacs-lisp
    204   (setq ethandl/code-font "Codelia Ligatures-16")
    205 #+end_src
    206 Currently I use Codelia, but I also recommend Comic Code, Julia Mono, or Victor Mono. In the end this is entirely personal preference.
    207 Some more examples:
    208 #+begin_src emacs-lisp :tangle no
    209   (setq ethandl/code-font "Comic Code Ligatures-16")
    210   (setq ethandl/code-font "JuliaMono-16")
    211   (setq ethandl/code-font "Victor Mono-16")
    212 #+end_src
    213 
    214 Now we use [[*Code fonts][the font helper function that I defined earlier]] to actually set the fonts.
    215 #+begin_src emacs-lisp
    216   ;; Set default font
    217   (ethandl/set-fonts ethandl/code-font t)
    218   (add-hook 'elpaca-after-init-hook (apply-partially #'ethandl/set-fonts ethandl/code-font))
    219 #+end_src
    220 
    221 We also want this to run after we make a new frame, in case we are running emacs as a daemon.
    222 #+begin_src emacs-lisp
    223   (add-hook 'after-make-frame-functions (apply-partially #'ethandl/set-fonts ethandl/code-font))
    224   (add-hook 'after-make-frame-functions
    225           #'(lambda (frame) (run-with-timer 1 nil #'meow--prepare-face)))
    226 #+end_src
    227 **** Font supported ligatures for ~ligature.el~
    228 Each font has its own set of supported ligatures.
    229 Unfortunately on Linux systems you must use ~ligature.el~ to set ligatures, and this requires manually defining which ligatures are supported. This is either really cool or a pain in the ass.
    230 
    231 Here are some examples:
    232 #+begin_src emacs-lisp
    233   (setq
    234    comic-code-ligs
    235    '(("-" (rx (+ "-"))) ("-" (rx (* "-") ">"))
    236      ("+" (rx (+ "+"))) ("<" (rx (+ "=")))
    237      ("<" (rx (+ "=") ">")) ("<" (rx (+ "~")))
    238      ("<" (rx (+ "~") ">")) ("<" (rx "!" (+ "-")))
    239      ("<" (rx (+ "-"))) ("<" (rx (+ "-") ">")) ("<" (rx "|"))
    240      (">" (rx (+ "="))) (">" (rx ">" (+ "=")))
    241      (">" (rx ">" (+ "=") ">")) (">" (rx (+ "-")))
    242      (">" (rx (+ "-") "<")) ("~" (rx (+ "~")))
    243      ("~" (rx (+ "~") ">")) ("=" (rx (* "=") ">"))
    244      ("=" (rx (+ (or ">" "<" "|" "/" "~" ":" "!" "="))))
    245      "!=" "!==" "[|" "|]" "{|" "|}" "|>" "||" "&&")
    246    
    247    victor-mono-ligs
    248    '("</" "</>" "/>" "~-" "-~" "~@" "<~" "<~>" "<~~" "~>" "~~" "~~>" ">=" "<="
    249      "<!--" "##" "###" "####" "|-" "-|" "|->" "<-|" ">-|" "|-<" "|=" "|=>"
    250      ">-" "<-" "<--" "-->" "->" "-<" ">->" ">>-" "<<-" "<->" "->>" "-<<" "<-<"
    251      "==>" "=>" "=/=" "!==" "!=" "<==" ">>=" "=>>" ">=>" "<=>" "<=<"
    252      "=<=" "=>=" "<<=" "=<<" ".-" ".=" "=:=" "=!=" "==" "===" "::" ":=" ":>"
    253      ":<" ">:" ";;" "<|" "<|>" "|>" "<>" "<$" "<$>" "$>" "<+"
    254      "<+>" "+>" "?=" "/=" "/==" "/\\" "\\/" "__" "&&" "++" "+++")
    255    
    256    julia-mono-ligs
    257    '("->" "=>" "|>" "<|" "::" "<--" "-->" "<-->"
    258      ("=" (rx (+ "=")))))
    259 #+end_src
    260 In my configuration, I declare the variable ~ethandl/ligs~ to use in the ~ligature.el~ setup.
    261 #+begin_src emacs-lisp
    262   (setq ethandl/ligs julia-mono-ligs)
    263 #+end_src
    264 
    265 All of this configuration is unnecessary if you don't want ligatures, or if you are using ~emacs-mac~ by Mitsuharu Yamamoto.
    266 * Custom Keybinds
    267 Here I have made a little section for setting up some additional keybinds.
    268 ** Vim-style window navigation
    269 When navigating windows, I find the binding ~C-x o~ to be rather slow when you have many buffers open in different arrangements, so I have adopted a set of bindings for the ~windmove-*~ functions that are left unused in the default configuration. These binds fit in quite nicely with the ~vim~-like keybinds that I use with ~meow~.
    270 
    271 This is mostly just the work of [[https://dev.to/rajasegar/vim-style-repeatable-key-bindings-for-navigating-windows-in-emacs-5c4l][Rajasegar Chandran]].
    272 #+begin_src emacs-lisp
    273   (global-set-key (kbd "C-x w h") 'windmove-left)
    274   (global-set-key (kbd "C-x w j") 'windmove-down)
    275   (global-set-key (kbd "C-x w k") 'windmove-up)
    276   (global-set-key (kbd "C-x w l") 'windmove-right)
    277   (repeat-mode t)
    278   (defvar-keymap windmove-repeat-map
    279     :repeat t
    280     "h" #'windmove-left
    281     "j" #'windmove-down
    282     "k" #'windmove-up
    283     "l" #'windmove-right)
    284 #+end_src
    285 ** Super as meta
    286 I like having the super key also function as a meta key, as I frequently switch between MacOS and Linux with different window managers and keyboards.
    287 #+begin_src emacs-lisp
    288   (setq x-super-keysym 'meta)
    289 #+end_src
    290 * General Emacs/Editor settings
    291 FIXME: Continue from here (remembering that I have other FIXMEs above)
    292 
    293 FIXME: Bring as many of these changes into standalone functions as possible to reduce fragmentation.
    294 We can use ~use-package~ to configure emacs at startup.
    295 #+begin_src emacs-lisp
    296   ;; Emacs init config
    297   (use-package emacs
    298     :ensure nil
    299     :hook
    300     (prog-mode . display-line-numbers-mode)
    301     (display-line-numbers-mode . ethandl/config-line-numbers)
    302     (before-save . ethandl/before-save-hook)
    303     :init
    304     ;; Get rid of default crud
    305     (setq inhibit-startup-screen t)
    306     (if (eq system-type 'darwin)
    307         (menu-bar-mode 1)
    308       (menu-bar-mode 0))
    309     (tool-bar-mode -1)
    310     (setq-default tool-bar-mode -1)
    311     (add-to-list 'default-frame-alist '(tool-bar-lines . 0))
    312     ;; (add-to-list 'after-make-frame-functions 'ethandl/tool-bar-padding-remove)
    313     (when (fboundp 'scroll-bar-mode)
    314       (scroll-bar-mode 0))
    315     ;; Note, the following two are already set in early_init.el,
    316     ;; so the initial frame has good resizing
    317     (setq frame-resize-pixelwise t) 
    318     (setq window-resize-pixelwise t)
    319     ;; Modeline
    320     (line-number-mode 1) ;; Line no.
    321     (column-number-mode 1) ;; Col. no.
    322     ;; Indentation
    323     (setq-default indent-tabs-mode nil)
    324     (setq-default tab-width 4)
    325     ;; windmove wraps around
    326     (setq windmove-wrap-around t)
    327     ;; Get rid of the bell
    328     (setq ring-bell-function 'ignore)
    329     ;; Fuck Control+Scroll to zoom, that's terrible and seems to be bound by something in emacs-plus?
    330     (global-unset-key (kbd "<C-wheel-up>"))
    331     (global-unset-key (kbd "<C-wheel-down>"))
    332     ;; Autosave files need to piss off
    333     (setq backup-directory-alist
    334           `((".*" . ,temporary-file-directory)))
    335     (setq auto-save-file-name-transforms
    336           `((".*" ,temporary-file-directory t)))
    337     ;; Ensure that the command key is the emacs meta key
    338     (when (eq system-type 'darwin)
    339       (setq mac-option-modifier nil
    340             mac-command-modifier 'meta))
    341     ;; Emacs frame title format default is bad:
    342     (setq frame-title-format "%b"))
    343 #+end_src
    344 * Packages
    345 ** Ivy and Counsel (Better Text Navigation)
    346 [[https://github.com/abo-abo/swiper][Ivy and Counsel]] are replacements for the default ~find-file~ and =M-x= menus.
    347 #+begin_src emacs-lisp
    348   ;; ivy mode instead of ido mode:
    349   (use-package ivy
    350     :hook (elpaca-after-init . ivy-mode))
    351   ;; counsel extends ivy:
    352   (use-package counsel
    353     :hook (elpaca-after-init . counsel-mode))
    354 #+end_src
    355 ** Ligature.el / Ligature set up
    356 Ligatures are native on =emacs-mac=, so we just call the built in helper function if we are using that. If not, we will use [[https://github.com/mickeynp/ligature.el][ligature.el]] to give ligatures with Harfbuzz.
    357 #+begin_src emacs-lisp
    358   ;; Font ligatures:
    359   (if (eq window-system 'mac)
    360       ;; We must be in emacs-mac
    361       (mac-auto-operator-composition-mode)
    362     ;; Else, we're in a vanilla emacs
    363     (use-package ligature
    364       :config
    365       (ligature-set-ligatures 'prog-mode ethandl/ligs)
    366       (global-ligature-mode t)))
    367 #+end_src
    368 ** Rainbow Delimiters
    369 Rainbow delimiters are essential for working with LISP, and are just nice to have elsewhere. This package has apparently got a very small footprint, and I can't be arsed actually benchmarking anything in this emacs config so I trust them.
    370 #+begin_src emacs-lisp
    371   ;; Rainbow delimiters:
    372   (use-package rainbow-delimiters
    373     :hook (prog-mode . rainbow-delimiters-mode))
    374 #+end_src
    375 ** Doom Modeline
    376 This modeline is supposedly sexier than the original, honestly I don't mind the original either, it's also very nice. If there's ever a reason to ditch this because of some bug or performance issue, etc. then don't be afraid to.
    377 #+begin_src emacs-lisp
    378   ;; Doom modeline:
    379   (use-package doom-modeline
    380     :hook (elpaca-after-init . doom-modeline-mode)
    381     :config
    382     ;; simpc-mode icon
    383     (push '(simpc-mode nerd-icons-sucicon "nf-custom-c"
    384                        :face nerd-icons-blue)
    385           nerd-icons-mode-icon-alist))
    386 #+end_src
    387 ** Which Key?
    388 Fix my ineptitude in this keyboard chord hell.
    389 #+begin_src emacs-lisp
    390   ;; Which key (this is the thing responsible for coming up with the minibuffer of keys that you can pick):
    391   (use-package which-key
    392     :init (which-key-mode)
    393     :diminish which-key-mode
    394     :config
    395     (setq which-key-idle-delay 0.3))
    396 #+end_src
    397 ** Meow
    398 Meow takes some getting used to, but I want to try.
    399 The beauty is that I have full control explicitly over every binding in a really simple interface, and the rest of the binds are just emacs' default with some enhancement:
    400 #+begin_src emacs-lisp
    401   ;; meow :D
    402   (use-package meow
    403     :config
    404     ;; General meow config:
    405     (setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
    406     (meow-motion-overwrite-define-key
    407      '("j" . meow-next)
    408      '("k" . meow-prev)
    409      '("<escape>" . ignore))
    410     (meow-leader-define-key
    411      ;; SPC j/k will run the original command in MOTION state.
    412      '("j" . "H-j")
    413      '("k" . "H-k")
    414      ;; Use SPC (0-9) for digit arguments.
    415      '("1" . meow-digit-argument)
    416      '("2" . meow-digit-argument)
    417      '("3" . meow-digit-argument)
    418      '("4" . meow-digit-argument)
    419      '("5" . meow-digit-argument)
    420      '("6" . meow-digit-argument)
    421      '("7" . meow-digit-argument)
    422      '("8" . meow-digit-argument)
    423      '("9" . meow-digit-argument)
    424      '("0" . meow-digit-argument)
    425      '("/" . meow-keypad-describe-key)
    426      '("?" . meow-cheatsheet))
    427     (meow-normal-define-key
    428      '("0" . meow-expand-0)
    429      '("9" . meow-expand-9)
    430      '("8" . meow-expand-8)
    431      '("7" . meow-expand-7)
    432      '("6" . meow-expand-6)
    433      '("5" . meow-expand-5)
    434      '("4" . meow-expand-4)
    435      '("3" . meow-expand-3)
    436      '("2" . meow-expand-2)
    437      '("1" . meow-expand-1)
    438      '("-" . negative-argument)
    439      '(";" . meow-reverse)
    440      '("," . meow-inner-of-thing)
    441      '("." . meow-bounds-of-thing)
    442      '("[" . meow-beginning-of-thing)
    443      '("]" . meow-end-of-thing)
    444      '("a" . meow-append)
    445      '("A" . meow-open-below)
    446      '("b" . meow-back-word)
    447      '("B" . meow-back-symbol)
    448      '("c" . meow-change)
    449      '("d" . meow-delete)
    450      '("D" . meow-backward-delete)
    451      '("e" . meow-next-word)
    452      '("E" . meow-next-symbol)
    453      '("f" . meow-find)
    454      '("g" . meow-cancel-selection)
    455      '("G" . meow-grab)
    456      '("h" . meow-left)
    457      '("H" . meow-left-expand)
    458      '("i" . meow-insert)
    459      '("I" . meow-open-above)
    460      '("j" . meow-next)
    461      '("J" . meow-next-expand)
    462      '("k" . meow-prev)
    463      '("K" . meow-prev-expand)
    464      '("l" . meow-right)
    465      '("L" . meow-right-expand)
    466      '("m" . meow-join)
    467      '("n" . meow-search)
    468      '("o" . meow-block)
    469      '("O" . meow-to-block)
    470      '("p" . meow-yank)
    471      '("q" . meow-quit)
    472      '("Q" . meow-goto-line)
    473      '("r" . meow-replace)
    474      '("R" . meow-swap-grab)
    475      '("s" . meow-kill)
    476      '("t" . meow-till)
    477      '("u" . meow-undo)
    478      '("U" . meow-undo-in-selection)
    479      '("v" . meow-visit)
    480      '("w" . meow-mark-word)
    481      '("W" . meow-mark-symbol)
    482      '("x" . meow-line)
    483      '("X" . meow-goto-line)
    484      '("y" . meow-save)
    485      '("Y" . meow-sync-grab)
    486      '("z" . meow-pop-selection)
    487      '("'" . repeat)
    488      '(">" . indent-rigidly-right-to-tab-stop)
    489      '("<" . indent-rigidly-left-to-tab-stop)
    490      '("<escape>" . ignore))
    491     (setq meow-keypad-leader-dispatch "C-x")
    492     (meow-global-mode t))
    493 #+end_src
    494 ** Corfu & Cape (Completion)
    495 Based completion. This is an overhaul of the autocomplete. Company mode sucks because when you resize a buffer with C-x-+ everything breaks.
    496 #+begin_src emacs-lisp
    497   ;; Cape
    498   (use-package cape)
    499 
    500   ;; Completion & Modernisation
    501   (use-package corfu
    502     :after cape
    503     :hook
    504     (meow-insert-exit . corfu-quit)
    505     :custom
    506     (corfu-cycle t)
    507     (corfu-auto t)
    508     (corfu-preselect 'prompt)
    509     (corfu-scroll-margin 5)
    510     (completion-cycle-threshold 3)
    511     :init
    512     (global-corfu-mode))
    513 #+end_src
    514 ** Scrolling Patches
    515 JDT makes a nice scroll package, this should be good regardless of the emacs build that you are using.
    516 #+begin_src emacs-lisp
    517   (use-package ultra-scroll
    518     :ensure `(ultra-scroll
    519               :host github
    520               :repo "jdtsmith/ultra-scroll")
    521     :init
    522     (setq scroll-conservatively 10
    523           scroll-margin 0)
    524     :config
    525     (ultra-scroll-mode 1))
    526 #+end_src
    527 ** Exec path from shell (Fix Path issues)
    528 When running emacs as a daemon, we have a notable lack of things in our ~PATH~ because daemons are started in a minimal environment, at least on MacOS.
    529 This solution will increase startup time, but will fix any path issues for most shells. May need tinkering. This will also only be activated when using a daemon.
    530 #+begin_src emacs-lisp
    531   (use-package exec-path-from-shell
    532     :config
    533     (exec-path-from-shell-initialize))
    534 #+end_src
    535 ** PDF-tools
    536 #+begin_src emacs-lisp
    537   (use-package pdf-tools
    538     :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
    539     :hook
    540     (pdf-view-mode . auto-revert-mode)
    541     (pdf-view-mode . pdf-view-midnight-minor-mode)
    542     :config
    543     (setq-default pdf-view-display-size 'fit-page)
    544     (setq pdf-annot-activate-created-annotations t))
    545 #+end_src
    546 ** Emacs vterm
    547 In emacs' included packages, there are only 2 shells that are good to use: ~eshell~ and ~shell~.
    548 I have had poor experiences with ~term~, as it doesn't properly emulate all escape sequences and is a bit slow.
    549 
    550 The solution to this is the ~vterm~ package, which is a package written mostly in C with a much better terminal emulation. It is fast and more reliable.
    551 #+begin_src emacs-lisp
    552   (use-package vterm)
    553 #+end_src
    554 * Org Mode
    555 My org mode is based on [[https://orgmode.org/guide/Hyperlinks.html][a post by Diego Zamboni]]. The general idea is that we want headings to be bigger and more noticable than text, all regular text should be non-monospace, and all code should be in the regular monospace font.
    556 The org configuration uses a lot of custom functions, see the 
    557 #+begin_src emacs-lisp
    558   (use-package org
    559     :ensure nil
    560     :hook
    561     (org-mode . ethandl/org-base)
    562     (org-mode . ethandl/org-font)
    563     (org-mode . ethandl/org-latex))
    564 #+end_src
    565 Other auxillary packages for Org mode:
    566 #+begin_src emacs-lisp
    567   (use-package org-superstar ;; Nicer bullet fonts
    568     :hook (org-mode . org-superstar-mode)
    569     :config (setq org-superstar-leading-bullet ?\s))
    570   (use-package org-autolist ;; Automatic bulleting
    571     :hook (org-mode . org-autolist-mode))
    572   (use-package org-download ;; Seamless image pasting
    573     :after org
    574     :defer nil
    575     :custom
    576     (org-download-method 'directory)
    577     (org-download-image-dir "pasted-images")
    578     (org-download-heading-lvl nil)
    579     (org-download-timestamp "%Y%m%d-%H%M%S_")
    580     (org-image-actual-width t)
    581     (org-download-screenshot-method "/opt/homebrew/bin/pngpaste %s")
    582     :bind
    583     ("M-p" . org-download-screenshot)
    584     :config
    585     (require 'org-download))
    586 #+end_src
    587 * Languages
    588 ** LSP Setup
    589 LSP, the Language Server Protocol. Very nice for debugging code live as you write it. It depends on the language as to how useful this really is.
    590 
    591 *Warning:* Some LSPs are godawful slow and can chew through CPU and memory. Be careful, tinker, and don't be afraid to disable the training wheels.
    592 We use the built-in package eglot (requires emacs 29) if possible as there seems to occasionally be issues with installing eglot.
    593 #+begin_src emacs-lisp
    594   (when (>= emacs-major-version 29)
    595       (use-package eglot
    596         :ensure nil))
    597   (use-package markdown-mode) ; needed for formatting the stuff in the eldoc buffers
    598 #+end_src
    599 ** Tree Sitter Setup
    600 Since Emacs 29, we don't need to install tree sitter separately anymore, it comes with Emacs!
    601 #+begin_src emacs-lisp
    602   (setq treesit-language-source-alist
    603         '((bash "https://github.com/tree-sitter/tree-sitter-bash")
    604           (css "https://github.com/tree-sitter/tree-sitter-css")
    605           (go "https://github.com/tree-sitter/tree-sitter-go")
    606           (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
    607           (html "https://github.com/tree-sitter/tree-sitter-html")
    608           (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
    609           (json "https://github.com/tree-sitter/tree-sitter-json")
    610           (yaml "https://github.com/ikatyang/tree-sitter-yaml")
    611           (python "https://github.com/tree-sitter/tree-sitter-python")
    612           (toml "https://github.com/tree-sitter/tree-sitter-toml")
    613           (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
    614           (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
    615           (rust "https://github.com/tree-sitter/tree-sitter-rust")
    616           (java "https://github.com/tree-sitter/tree-sitter-java")
    617           ;;(haskell "https://github.com/tree-sitter/tree-sitter-haskell") ; There is no haskell-ts-mode yet
    618           ;;(ocaml "https://github.com/tree-sitter/tree-sitter-ocaml" "master" "ocaml/src") ; There is no ocaml-ts-mode
    619           (c "https://github.com/tree-sitter/tree-sitter-c")
    620           (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
    621           (zig "https://github.com/maxxnino/tree-sitter-zig")))
    622 
    623   ;; Fuck windows, this won't work on there and I don't care
    624   (ethandl/install-treesit-grammars treesit-language-source-alist)
    625 #+end_src
    626 ** Eldoc Setup
    627 I like to have eldoc appear as a box below where I'm typing, rather than having it appear in the minibuffer. This is done with =eldoc-box=:
    628 #+begin_src emacs-lisp
    629   (use-package eldoc-box
    630     :hook
    631     (eglot-managed-mode . eldoc-box-hover-mode))
    632 #+end_src
    633 ** C
    634 I was using the C treesitter mode, but that mode is still based on C-mode and is incredibly slow for large files.
    635 #+begin_src emacs-lisp :tangle no
    636   (push '(c-mode . c-ts-mode) major-mode-remap-alist))
    637 #+end_src
    638 
    639 Instead, I now use Alexey Kutepov's ~simp-c~ mode, which performs far better.
    640 #+begin_src emacs-lisp
    641   (use-package simpc-mode
    642     :ensure '(simpc-mode
    643               :host github
    644               :repo "rexim/simpc-mode")
    645     :config
    646     (push '(c-mode . simpc-mode) major-mode-remap-alist))
    647 #+end_src
    648 ** Haskell
    649 This setup is just a combination of =haskell-mode= and the Haskell Language Server.
    650 Haskell mode is really good for the indenting and overall behaviour of the editor, and the Haskell language server is definitely there... (It can be slow, so can be turned off on larger projects)
    651 #+begin_src emacs-lisp
    652   (executable-find "haskell-language-server-wrapper")
    653   ;; Haskell setup
    654   (use-package haskell-mode)
    655   (use-package eglot
    656     :ensure nil
    657     :after eglot
    658     :hook (haskell-mode . eglot-ensure)
    659     :config
    660     (setq-default eglot-workplace-configuration
    661                   '((haskell
    662                      (plugin
    663                       (stan
    664                        (globalOn . :json-false))))))
    665     :custom
    666     (eglot-autoshutdown t)
    667     (eglot-confirm-server-initiated-edits nil))
    668 #+end_src
    669 NOTE: This is not using tree sitter, as there is no haskell-ts-mode yet or probably for a while into the future.
    670 ** Rust
    671 Rust mode alongside LSP again. The rust LSP is very good as far as LSPs go, very helpful. Though sometimes it's better to compile and see the rustc errors as they tend to be more verbose.
    672 #+begin_src emacs-lisp
    673   (use-package eglot
    674     :ensure nil
    675     :after eglot
    676     :hook (rust-ts-mode . eglot-ensure)
    677     :config
    678     (add-to-list 'eglot-server-programs
    679                  `(rust-mode . ("rust-analyzer" :initializationOptions
    680                                 (:procmacro (:enable t)))))
    681     (push '("\\.rs\\'" . rust-ts-mode) auto-mode-alist))
    682 #+end_src
    683 ** Go
    684 This basic LSP setup is based on the golang guide: https://cs.opensource.google/go/x/tools/+/refs/tags/gopls/v0.14.2:gopls/doc/emacs.md
    685 #+begin_src emacs-lisp
    686   (push '("\\.go\\'" . go-ts-mode) auto-mode-alist)
    687   (push '("\\.mod\\'" . go-mod-ts-mode) auto-mode-alist)
    688   (defun golang/project-find-go-module (dir)
    689     (when-let ((root (locate-dominating-file dir "go.mod")))
    690       (cons 'go-module root)))
    691   (cl-defmethod project-root ((project (head go-module)))
    692     (cdr project))
    693 
    694   (use-package project
    695     :ensure nil
    696     :config
    697     (add-hook 'project-find-functions #'golang/project-find-go-module))
    698 
    699   (use-package eglot
    700     :ensure nil
    701     :after eglot
    702     :hook (go-ts-mode . eglot-ensure)
    703     :config
    704     (setq-default eglot-workspace-configuration
    705                   '((:gopls .
    706                             ((staticcheck . t)
    707                              (matcher . "CaseSensitive")))))
    708     (push '("\\.go\\'" . go-ts-mode) auto-mode-alist)
    709     (push '("\\.mod\\'" . go-mod-ts-mode) auto-mode-alist))
    710 #+end_src
    711 ** Java
    712 OO hell 😌
    713 If you want a language server, maybe just use IntelliJ? I don't use Java and the ~eglot-java~ package has broken for my config, complaining about the eglot version.
    714 #+begin_src emacs-lisp
    715   ;; Java setup
    716   (use-package emacs
    717     :ensure nil
    718     :config
    719     (push '(java-mode . java-ts-mode) major-mode-remap-alist))
    720 #+end_src
    721 ** JavaScript/TypeScript
    722 JavaScript and TypeScript are easy thanks to tree sitter!
    723 #+begin_src emacs-lisp
    724   (use-package eglot
    725     :ensure nil
    726     :after eglot
    727     :hook
    728     (js-ts-mode . eglot-ensure)
    729     (typescript-ts-mode . eglot-ensure)
    730     (tsx-ts-mode . eglot-ensure)
    731     :config
    732     (setq typescript-ts-mode-indent-offset tab-width)
    733     (push '(js-mode . js-ts-mode) major-mode-remap-alist)
    734     (push '(javascript-mode . js-ts-mode) major-mode-remap-alist)
    735     (push '("\\.ts\\'" . typescript-ts-mode) auto-mode-alist)
    736     (push '("\\.tsx\\'" . tsx-ts-mode) auto-mode-alist))
    737 #+end_src
    738 ** Python
    739 A.K.A. the most overused and overhyped language. This language is incredibly slow, which is why its language server is not written in Python LMAO. Anywho the =pyright= LSP is made by Microshit so maybe this is proprietary software or telemetry, idk.
    740 #+begin_src emacs-lisp
    741   (use-package eglot
    742     :ensure nil
    743     :after eglot
    744     :hook ((python-mode python-ts-mode) . eglot-ensure)
    745     :config
    746     (add-to-list 'eglot-server-programs
    747                  `(python-mode . ("pyright-langserver" "--stdio")))
    748     (add-to-list 'eglot-server-programs
    749                  `(python-ts-mode . ("pyright-langserver" "--stdio")))
    750     ;(push ".dir-locals.el" project-vc-extra-root-markers)
    751     (push '(python-mode . python-ts-mode) major-mode-remap-alist))
    752 #+end_src
    753 The following lets me set up a venv for use with pyright (https://robbmann.io/posts/emacs-eglot-pyrightconfig/):
    754 #+begin_src emacs-lisp
    755   (defun pyrightconfig-write (virtualenv)
    756     (interactive "DEnv: ")
    757     
    758     (let* (;; file-truename and tramp-file-local-name ensure that neither `~' nor
    759            ;; the Tramp prefix (e.g. "/ssh:my-host:") wind up in the final
    760            ;; absolute directory path.
    761            (venv-dir (tramp-file-local-name (file-truename virtualenv)))
    762            
    763            ;; Given something like /path/to/.venv/, this strips off the trailing `/'.
    764            (venv-file-name (directory-file-name venv-dir))
    765            
    766            ;; Naming convention for venvPath matches the field for
    767            ;; pyrightconfig.json.  `file-name-directory' gets us the parent path
    768            ;; (one above .venv).
    769            (venvPath (file-name-directory venv-file-name))
    770            
    771            ;; Grabs just the `.venv' off the end of the venv-file-name.
    772            (venv (file-name-base venv-file-name))
    773            
    774            ;; Eglot demands that `pyrightconfig.json' is in the project root
    775            ;; folder.
    776            (base-dir (vc-git-root default-directory))
    777            (out-file (expand-file-name "pyrightconfig.json" base-dir))
    778 
    779            ;; Finally, get a string with the JSON payload.
    780            (out-contents (json-encode (list :venvPath venvPath :venv venv))))
    781 
    782       ;; Emacs uses buffers for everything.  This creates a temp buffer, inserts
    783       ;; the JSON payload, then flushes that content to final `pyrightconfig.json'
    784       ;; location
    785       (with-temp-file out-file (insert out-contents))))
    786 #+end_src
    787 ** LaTeX
    788 LaTeX isn't really a language, but we should set up stuff for it.
    789 Firstly, we should get CDLaTeX:
    790 #+begin_src emacs-lisp
    791   (use-package cdlatex
    792     :hook ((latex-mode LaTeX-mode) . turn-on-cdlatex)
    793     :hook (latex-mode . (lambda () (setq tab-width 2))))
    794 #+end_src
    795 * Native Comp
    796 Supress all warnings:
    797 #+begin_src emacs-lisp
    798   (setq native-comp-async-report-warnings-errors 'silent)
    799 #+end_src
    800 
    801 I used to always try to compile all the emacs packages in our config, I find that this just results in a build job running forever, even when we're done. Not sure what it's doing or what package it's for, but I'm just going to make this compilation process manual.
    802 #+begin_src emacs-lisp :tangle no
    803   (when (fboundp 'native-comp-available-p)
    804     (add-hook 'elpaca-after-init-hook (lambda () (native-compile-async user-emacs-directory t))))
    805 #+end_src
    806 
    807 Manual compile function:
    808 #+begin_src emacs-lisp
    809   (defun ethandl/nativecomp-all ()
    810     (interactive)
    811     (native-compile-async user-emacs-directory 'recursively))
    812 #+end_src