emacs.d

My Emacs configuration
git clone https://git.jamzattack.xyz/emacs.d
Log | Files | Refs | LICENSE

config.org (128255B)


      1 #+title: Emacs Configuration
      2 #+author: Jamie Beardslee
      3 #+email: jdb@jamzattack.xyz
      4 #+property: header-args:emacs-lisp :tangle lisp/config.el :noweb yes :results none
      5 
      6 This is my Emacs config file.  It is written in org-mode so that I can
      7 brag about how dope org-mode is.  [[file:README.org][This file]] contains my main
      8 configuration, which is tangled to [[file:lisp/config.el][config.el]].  [[file:init.el][init.el]] sets the
      9 variable [[help:custom-file][custom-file]] to [[file:lisp/custom.el][custom.el]], loads [[file:lisp/config.el][config.el]], and then loads
     10 [[file:lisp/custom.el][custom.el]].
     11 
     12 Note: [[file:lisp/custom.el][custom.el]] should not be edited manually, as it is used by Emacs
     13 for settings changed using the customisation interface.
     14 
     15 My own packages and other things that I need to [[help:require][require]] are housed in
     16 [[file:lisp][lisp/]].  I don't include the [[file:straight][straight/]] directory so the first startup
     17 will take some time.
     18 
     19 * Startup
     20 
     21 Get some things out of the way early.  Without [[*Straight][straight]] or
     22 [[*Use-package and dependencies][use-package]], none of this config would work.
     23 
     24 ** Straight
     25 
     26 Install [[https://github.com/raxod502/straight.el][straight.el]].  It gets a lot of hype, so I'm trying to use it
     27 instead of the built-in =package.el=.  It has a use-package keyword, so
     28 you can simply (re)define a package like so:
     29 
     30 #+begin_src emacs-lisp :tangle no
     31   (use-package package-name
     32     :straight
     33     (package-name :host gitlab
     34                   :repo "user/forked-package"
     35                   :branch "cool-new-feature"))
     36 #+end_src
     37 
     38 This snippet clones and loads straight, stolen from the README.
     39 
     40 #+begin_src emacs-lisp
     41   (let ((bootstrap-file
     42          (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
     43         (bootstrap-version 5))
     44     (unless (file-exists-p bootstrap-file)
     45       (with-current-buffer
     46           (url-retrieve-synchronously
     47            "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
     48            'silent 'inhibit-cookies)
     49         (goto-char (point-max))
     50         (eval-print-last-sexp)))
     51     (load bootstrap-file nil 'nomessage))
     52 
     53   (with-eval-after-load 'straight
     54     (setq straight-vc-git-default-protocol 'ssh)
     55     (add-to-list 'straight-built-in-pseudo-packages 'org)
     56     (fset 'try #'straight-use-package))
     57 #+end_src
     58 
     59 ** Use-package and dependencies
     60 
     61 Install use-package using [[help:straight-use-package][straight-use-package]], and load both
     62 [[help:use-package][use-package]] and [[help:bind-key][bind-key]].  Note [[help:bind-key][bind-key]] is a dependency of
     63 [[help:use-package][use-package]], so I don't need to install it manually.
     64 
     65 I also use use-package's =:delight= keyword, so install that as well.  I
     66 don't need to use =(require delight)= as use-package handles that.
     67 
     68 #+begin_src emacs-lisp
     69   (straight-use-package 'use-package)
     70   (straight-use-package 'delight)
     71   (setq use-package-compute-statistics t)
     72   (require 'use-package)
     73   (require 'bind-key)
     74 #+end_src
     75 
     76 ** Fonts
     77 
     78 The function [[help:set-up-fonts-please][set-up-fonts-please]] loads my [[file:lisp/fonts.el][font settings]].  Call it when
     79 creating a new frame or starting emacs.  The weird hack bit makes sure
     80 that [[*EXWM - Emacs X Window Manager][exwm]]'s additions are run /before/ settings the fonts.
     81 
     82 #+begin_src emacs-lisp
     83   (defun set-up-fonts-please (&optional frame)
     84     "Load font settings in `user-emacs-directory'/lisp/fonts.el."
     85     (interactive)
     86     (with-selected-frame (or frame (selected-frame))
     87       (load (expand-file-name "lisp/fonts.el" user-emacs-directory))))
     88 
     89   (add-hook 'server-after-make-frame-hook 'set-up-fonts-please)
     90   (add-hook 'window-setup-hook 'set-up-fonts-please)
     91   (add-to-list 'after-make-frame-functions 'set-up-fonts-please)
     92   (with-eval-after-load 'custom-exwm-config
     93     (setq after-make-frame-functions
     94           (cons 'set-up-fonts-please
     95                 (remove 'set-up-fonts-please after-make-frame-functions))))
     96 #+end_src
     97 
     98 ** Keybindings
     99 
    100 *** Prefix keys
    101 
    102 A couple of prefix keys.  It's useful to set these up early, so that
    103 you don't get any errors i.e "C-z is not a valid prefix key".
    104 
    105 **** Remove =C-z=
    106 
    107 Unbind =C-z= before anything else, so that I can use it as a prefix key.
    108 
    109 #+begin_src emacs-lisp
    110   (global-unset-key (kbd "C-z"))
    111 #+end_src
    112 
    113 **** Alias =<menu>= to =C-x=
    114 
    115 Make the menu key do the same as =C-x=.
    116 
    117 #+begin_src emacs-lisp
    118   (bind-key "<menu>" ctl-x-map)
    119 #+end_src
    120 
    121 *** Reloading config file
    122 
    123 Reload [[file:lisp/config.el][config file]] with =C-z C-r=.  This is done with [[help:bind-key][bind-key]] so that
    124 it is recorded in the variable [[help:personal-keybindings][personal-keybindings]].
    125 
    126 #+begin_src emacs-lisp
    127   (bind-key "C-z C-r" 'config-load)
    128 #+end_src
    129 
    130 * Built-in packages
    131 
    132 This is the section for built-in packages.
    133 
    134 ** package.el
    135 
    136 It's useful to keep =package.el= updated for the functions
    137 [[help:describe-package][describe-package]] and [[help:list-packages][list-packages]].  All my packages are now installed
    138 using [[*Straight][straight]], so disable the function [[help:package-install][package-install]].
    139 
    140 #+begin_src emacs-lisp
    141   (use-package package
    142     :no-require t
    143     :config
    144     (setq package-archives
    145           '(("gnu" . "http://elpa.gnu.org/packages/")
    146             ("melpa" . "http://melpa.org/packages/")))
    147     (fmakunbound 'package-install))
    148 #+end_src
    149 
    150 ** Major editing modes
    151 
    152 Major modes for text editing.  For non-editing major modes, see
    153 [[Applications]]
    154 
    155 *** Org Mode
    156 
    157 Open source blocks and stuff in the current window.  Use =TAB= from the
    158 language's major mode inside source blocks.  Open everything in Emacs,
    159 and use [[help:eww][eww]] for html instead of [[help:mhtml-mode][mhtml-mode]].
    160 
    161 #+begin_src emacs-lisp
    162   (use-package org
    163     :defer t
    164     :custom
    165     (org-src-window-setup 'current-window)
    166     (org-src-tab-acts-natively t)
    167     (org-adapt-indentation nil)
    168     (org-hide-emphasis-markers t)
    169     (org-file-apps
    170      '((auto-mode . emacs)
    171        ("\\.x?html?\\'" . (lambda (file &optional ignore)
    172                             (eww-open-file file)))))
    173     :delight
    174     (org-src-mode " #+src")
    175     :config
    176     ;; Quite ugly: (setf (last ...)) doesn't exist, and can't use
    177     ;; assoc/alist-get because the package name is the cadr
    178     (setf (nth (1- (length org-latex-default-packages-alist))
    179                org-latex-default-packages-alist)
    180           '("hidelinks" "hyperref" nil)
    181           (car org-latex-default-packages-alist)
    182           '("utf8x" "inputenc" "pdflatex"))
    183     <<org-insert-emacs-help>>
    184     :bind
    185     ("C-c M-." . org-time-stamp)
    186     (:map org-mode-map
    187           ("C-c C-v h" . org-hide-block-all)
    188           ("M-h" . mark-paragraph)
    189           ("C-M-h" . org-mark-element)
    190           ("C-c h" . org-insert-emacs-help)))
    191 #+end_src
    192 
    193 **** Insert help link
    194 
    195 A function to insert an org-mode help link.  This uses the symbol at
    196 point if it's a defined variable or function.  Otherwise, it prompts
    197 from all [[help:boundp][bound]] or [[help:fboundp][fbound]] symbols.
    198 
    199 #+name: org-insert-emacs-help
    200 #+begin_src emacs-lisp :tangle no
    201   (defun org-insert-emacs-help (&optional prompt)
    202     "Insert a help link to a symbol.
    203   If the symbol at point is bound, it is replaced by the link.
    204   Otherwise, or with prefix arg, PROMPT from all bound symbols in
    205   `obarray'."
    206     (interactive "*P")
    207     (when (eq (get-text-property (point) 'face)
    208               'org-link)
    209       (user-error "Text at point is already a link--don't want to mangle the buffer"))
    210     (cl-labels ((predicate (sym)
    211                            (and (or (boundp sym)
    212                                     (fboundp sym))
    213                                 (not (keywordp sym))))
    214                 (prompt ()
    215                         (completing-read
    216                          "Help link: "
    217                          obarray
    218                          #'predicate
    219                          t)))
    220       (let ((symbol
    221              (or (when prompt
    222                    (prompt))
    223                  (let ((symbol (symbol-at-point))
    224                        (bounds (bounds-of-thing-at-point 'symbol)))
    225                    (when (and symbol
    226                               (predicate symbol))
    227                      (delete-region (car bounds) (cdr bounds))
    228                      symbol))
    229                  (prompt))))
    230         (insert (format "[[help:%s][%s]]" symbol symbol)))))
    231 #+end_src
    232 
    233 **** Org Indent
    234 
    235 I used to use [[help:org-indent-mode][org-indent-mode]] a while back, but ditched it for reasons
    236 I can't remember.  I set the [[help:org-indent-indentation-per-level][indentation level]] to 1 character instead
    237 of its default value of 2.  This helps to keep the text within a
    238 manageable width and is probably the reason I disabled it.
    239 
    240 I find org-mode looks a bit cleaner and more "open" with this mode
    241 enabled.  Without it, the window can get cluttered pretty easily.
    242 
    243 #+begin_src emacs-lisp
    244   (use-package org-indent
    245     :defer
    246     :delight
    247     :config
    248     (setq org-indent-indentation-per-level 1))
    249 #+end_src
    250 
    251 **** Org capture
    252 
    253 Take notes in [[help:org-mode][org-mode]] with specific templates and write them to a
    254 file.  Similar to [[help:remember][remember]].
    255 
    256 #+begin_src emacs-lisp
    257   (use-package org-capture
    258     :custom
    259     (org-default-notes-file "~/org/notes.org")
    260     (org-capture-templates
    261      '(("t" "Todo")
    262        ("tt" "Misc." entry
    263         (file+headline "todo.org" "Miscellaneous")
    264         "* TODO %?\n\n%a\n")
    265        ("tu" "University" entry
    266         (file+headline "todo.org" "University")
    267         "* TODO %?\n\n%a\n")
    268        ("n" "Notes" entry
    269         (file+headline "notes.org" "Notes")
    270         "* %?\nEntered on %u\n\n%i\n\n%a\n")
    271        ("m" "Music" entry
    272         (file+headline "notes.org" "Music")
    273         "* %?\nEntered on %u\n\n%i\n")
    274        ("e" "Elisp" entry
    275         (file+headline "notes.org" "Emacs Lisp")
    276         "* %^{Title}\n\n#+begin_src emacs-lisp\n %i\n#+end_src\n")
    277        ("d" "Diary" entry
    278         (file "diary.org")
    279         "* %?\nEntered on %u\n\n")))
    280     (org-capture-bookmark nil)
    281     :bind
    282     ("C-x M-r" . org-capture))
    283 #+end_src
    284 
    285 **** Org babel
    286 
    287 Work with code blocks.  The libraries all provide support for a
    288 language so that you can run their source blocks with =C-c C-c=.
    289 
    290 ***** LilyPond
    291 
    292 Execute LilyPond source blocks.  For notes about exporting to pdf, see
    293 [[https://gitlab.com/jamzattack/lilypond/-/raw/master/org/lilypond.org][this org file]].  Only load it when lilypond is installed.
    294 
    295 #+begin_src emacs-lisp
    296   (use-package ob-lilypond
    297     :when (executable-find "lilypond")
    298     :defer t
    299     :config
    300     <<ob-lilypond-pdf-or-png>>
    301     :commands org-babel-execute:lilypond)
    302 #+end_src
    303 
    304 ****** Replace =:file= argument in lilypond source blocks
    305 
    306 This little bit of hackery to adjust the =:file= argument for lilypond
    307 source blocks.
    308 
    309 - pdf works great with latex export, but doesn't work with html.
    310 - png works great with html export, but looks fuzzy with latex.
    311 
    312 This [[info:elisp#Advising Functions][advice]] checks the backend of the export to determine which to
    313 use.
    314 
    315 #+name: ob-lilypond-pdf-or-png
    316 #+begin_src emacs-lisp :tangle no
    317   (defun ob-lilypond-pdf-or-png (backend &rest _args)
    318     "Replace the lilypond source blocks' :file argument.
    319   This will turn them all into .png files if BACKEND is html, and
    320   .pdf files in BACKEND is latex."
    321     (when (member backend '(latex html))
    322       (let ((case-fold-search t))
    323         (save-excursion
    324           (goto-char (point-min))
    325           (while (re-search-forward
    326                   "^\\(#\\+begin_src lilypond .*:file \\)\\(.*\\)\\.[a-z]+"
    327                   nil :noerror)
    328             (replace-match (pcase backend
    329                              ('latex "\\1\\2.pdf")
    330                              ('html "\\1\\2.png")))))
    331         (save-buffer))))
    332 
    333   (advice-add 'org-export-to-file :before #'ob-lilypond-pdf-or-png)
    334 #+end_src
    335 
    336 ***** C
    337 
    338 Execute C source blocks.  [[http://bellard.org/tcc/][TCC]] is a really fast compiler, so use it
    339 instead of gcc if it's installed.
    340 
    341 #+begin_src emacs-lisp
    342   (use-package ob-C
    343     :defer t
    344     :commands org-babel-execute:C
    345     :custom
    346     (org-babel-C-compiler
    347      (or (executable-find "tcc")
    348          "gcc")))
    349 #+end_src
    350 
    351 ***** Scheme
    352 
    353 Execute scheme source blocks.  This uses [[*Geiser][Geiser]] which is kind of
    354 awkward and slow, but evaluating scheme is useful.
    355 
    356 #+begin_src emacs-lisp
    357   (use-package ob-scheme
    358     :defer t
    359     :commands org-babel-execute:scheme)
    360 #+end_src
    361 
    362 ***** Common Lisp
    363 
    364 Execute Common Lisp source blocks.  This depends on [[*SLIME][Slime]], which
    365 doesn't start automatically (see the variable [[help:slime-auto-start][slime-auto-start]]).
    366 
    367 #+begin_src emacs-lisp
    368   (use-package ob-lisp
    369     :defer t
    370     :commands org-babel-execute:lisp)
    371 #+end_src
    372 
    373 ***** Shell
    374 
    375 Execute shell source blocks.  Autoload =sh=, =shell=, and =bash= functions.
    376 
    377 #+begin_src emacs-lisp
    378   (use-package ob-shell
    379     :defer t
    380     :commands
    381     org-babel-execute:sh
    382     org-babel-execute:shell
    383     org-babel-execute:bash)
    384 #+end_src
    385 
    386 **** Org links
    387 
    388 The library [[help:org-mode][org-mode]] uses to create and store links.  I bind =C-x M-l=
    389 to generate a link from the current position.
    390 
    391 #+begin_src emacs-lisp
    392   (use-package ol
    393     :config
    394     <<ol-help--export>>
    395     :bind
    396     ("C-x M-l" . org-store-link))
    397 #+end_src
    398 
    399 ***** Export help links in html
    400 
    401 I often use org's help links (e.g. [[help:org-mode][org-mode]]) but by default these are
    402 useless in an html export.  Thankfully, there's a neat [[https://doc.endlessparentheses.com][site]] that
    403 contains docstrings for all the built-in definitions.  Here, I:
    404 1. define a function that formats an href to the symbol's respective
    405    page, and
    406 2. [[help:org-link-set-parameters][let org know]] that I want to use that function whenever a help link
    407    is exported
    408 
    409 #+name: ol-help--export
    410 #+begin_src emacs-lisp :tangle no
    411   (defun ol-help--export (link description format)
    412     (let* ((desc (or description link))
    413            (sym (intern link))
    414            (type (if (fboundp sym)
    415                      "Fun"
    416                    "Var")))
    417       (when (eq format 'html)
    418         (format "<a target=\"_blank\" href=\"https://doc.endlessparentheses.com/%s/%s.html\">%s</a>"
    419                 type link desc))))
    420 
    421   (org-link-set-parameters "help" :export #'ol-help--export)
    422 #+end_src
    423 
    424 **** Org agenda
    425 
    426 Use all files in [[help:org-directory][org-directory]] to get my agenda.  And don't disrupt my
    427 window configuration.
    428 
    429 #+begin_src emacs-lisp
    430   (use-package org-agenda
    431     :defer t
    432     :after org
    433     :custom
    434     (org-agenda-files '("~/org" "~/org/uni"))
    435     (org-agenda-window-setup 'current-window)
    436     :bind
    437     ("C-z C-a" . org-agenda))
    438 #+end_src
    439 
    440 **** Org publish
    441 
    442 I use [[info:org#Publishing][org-publish]] for my websites.  This block has a lot going on:
    443 
    444 1. I set some [[my-org-publish-default-options][default options]] for publishing projects.
    445 2. I use a [[*Generate postamble][custom function]] to generate postamble.
    446 3. Include my three sites in [[help:org-publish-project-alist][org-publish-project-alist]].
    447 
    448 #+begin_src emacs-lisp
    449   (use-package ox-publish
    450     :defer t
    451     :config
    452     (use-package ox-jamzattack
    453       :demand
    454       :straight
    455       (ox-jamzattack :type git
    456                      :repo "git@jamzattack.xyz:ox-jamzattack.git"))
    457     <<my-org-html-postamble-format>>
    458     (defvar my-org-publish-default-options
    459       '(
    460         <<my-org-publish-default-options>>
    461         )
    462       "Default options for `org-publish-project-alist'.
    463 
    464   This variable must be spliced into `org-publish-project-alist'
    465   when set, i.e.
    466       (setq org-publish-project-alist
    467               `((\"project\"
    468                  ,@my-org-publish-default-options)))")
    469     (setq
    470      org-html-postamble t ; needed to use custom format
    471      org-export-headline-levels 6
    472      org-html-postamble-format
    473      (my-org-html-postamble-format
    474       "Author: %A")
    475      org-publish-timestamp-directory "~/.cache/org/timestamps/"
    476      org-html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\"/>"
    477      org-publish-project-alist
    478      `(("blog"
    479         ,@my-org-publish-default-options
    480         :base-directory "~/jamzattack.xyz/blog"
    481         :with-toc t
    482         :publishing-directory "~/jamzattack.xyz/out/blog"
    483         :html-postamble-format ,(my-org-html-postamble-format
    484                                  "Author: %A"
    485                                  "Date: %d (modified %M)"
    486                                  "Top: <a href=\"/index.html\">The Yeet Log</a>")
    487         :sitemap-filename "index.org"
    488         :sitemap-title "The Yeet Log"
    489         :sitemap-format-entry
    490         (lambda (entry style project)
    491           (cond ((not (directory-name-p entry))
    492                  (format "%s [[file:%s][%s]]"
    493                          (format-time-string
    494                           "%Y-%m-%d"
    495                           (org-publish-find-date entry project))
    496                          entry
    497                          (org-publish-find-title entry project)))
    498                 ((eq style 'tree)
    499                  ;; Return only last subdir.
    500                  (file-name-nondirectory (directory-file-name entry)))
    501                 (t entry)))
    502         :sitemap-sort-files anti-chronologically)
    503        ("gopher"
    504         :base-directory "~/jamzattack.xyz/blog"
    505         :with-toc t
    506         :with-email t
    507         :section-numbers nil
    508         :publishing-function org-ascii-publish-to-ascii
    509         :publishing-directory "~/jamzattack.xyz/out/gopher")
    510        ("music"
    511         ,@my-org-publish-default-options
    512         :base-directory "~/jamzattack.xyz/music"
    513         :recursive t
    514         :html-postamble-format ,(my-org-html-postamble-format
    515                                  "Author: %A"
    516                                  "Top: <a href=\"/sitemap.html\">All projects</a>")
    517         :publishing-directory "~/jamzattack.xyz/out/music"
    518         :sitemap-title "My Music Projects")
    519        ("html"
    520         ,@my-org-publish-default-options
    521         :base-directory "~/jamzattack.xyz/html"
    522         :publishing-directory "~/jamzattack.xyz/out/html"))))
    523 #+end_src
    524 
    525 ***** Generate postamble
    526 
    527 A little function to generate postamble.
    528 
    529 #+name: my-org-html-postamble-format
    530 #+begin_src emacs-lisp :tangle no
    531   (defun my-org-html-postamble-format (&rest args)
    532     "Generate an html postamble using ARGS.
    533 
    534   This generates a paragraph for each item in ARGS.  For format
    535   strings, see the docstring of `org-html-postamble-format'."
    536     (unless args
    537       (setq args '("Author: %a <%e>")))
    538     (list (list "en"
    539                 (mapconcat (lambda (str)
    540                              (format (cond
    541                                       ((string-match-p "%d" str)
    542                                        "<p class=\"date\">%s</p>")
    543                                       ((string-match-p "%A" str)
    544                                        "<p class=\"author\">%s</p>")
    545                                       (t
    546                                        "<p>%s</p>"))
    547                                      str))
    548                            args
    549                            "\n"))))
    550 #+end_src
    551 
    552 ***** Default export options
    553 
    554 A list of default export options.
    555 
    556 #+name: my-org-publish-default-options
    557 #+begin_src emacs-lisp :tangle no
    558   :auto-sitemap t
    559   :publishing-function org-html-publish-to-html
    560   :html-metadata-timestamp-format "%Y-%m-%d"
    561   :with-toc nil
    562   :with-email t
    563   :with-drawers nil
    564   :section-numbers nil
    565   :with-todo-keywords nil
    566 #+end_src
    567 
    568 *** Cc-mode
    569 
    570 Set the C style to bsd, which uses tabs.  Use Java/Awk indentation for
    571 Java/Awk files.
    572 
    573 #+begin_src emacs-lisp
    574   (use-package cc-mode
    575     :defer t
    576     :custom
    577     (c-default-style '((java-mode . "java")
    578                        (awk-mode . "awk")
    579                        (other . "bsd"))))
    580 #+end_src
    581 
    582 *** Emacs Lisp mode
    583 
    584 Make the scratch buffer use [[help:emacs-lisp-mode][emacs-lisp-mode]].  Note: Most of my Elisp
    585 keybindings are now in my package [[https://git.jamzattack.xyz/selime][selime]].
    586 
    587 #+begin_src emacs-lisp
    588   (use-package elisp-mode
    589     :custom
    590     (initial-major-mode 'emacs-lisp-mode)
    591     :delight
    592     (emacs-lisp-mode "el" :major)
    593     (inferior-emacs-lisp-mode "EL>" :major)
    594     :bind
    595     ("<C-M-backspace>" . backward-kill-sexp))
    596 #+end_src
    597 
    598 **** Find-func
    599 
    600 A package that defines a few functions for editing Elisp source code.
    601 It provides the function [[help:find-function-setup-keys][find-function-setup-keys]] which binds some
    602 keys in [[help:ctl-x-map][ctl-x-map]], but I prefer to have them under =C-h=.
    603 
    604 #+begin_src emacs-lisp
    605   (use-package find-func
    606     :defer t
    607     :bind
    608     (:map help-map
    609           ("C-l" . find-library)
    610           ("C-f" . find-function)
    611           ("C-v" . find-variable)
    612           ("C-k" . find-function-on-key)))
    613 #+end_src
    614 
    615 *** Typesetting
    616 
    617 **** Nroff-mode
    618 
    619 Set a compile-command hook for =nroff= files.  I usually use the ms
    620 macros when writing something, but I usually just use org-mode anyway.
    621 
    622 #+begin_src emacs-lisp
    623   (use-package nroff-mode
    624     :defer t
    625     :config
    626     <<nroff-mode-compile>>
    627     :hook (nroff-mode . nroff-mode-compile))
    628 #+end_src
    629 
    630 ***** Compile Command
    631 
    632 #+name: nroff-mode-compile
    633 #+begin_src emacs-lisp :tangle no
    634   (defun nroff-mode-compile ()
    635     "Set the compile command for nroff files.
    636 
    637   It will choose the macro set based on the file extension."
    638     (let* ((in (buffer-file-name))
    639            (out (concat (file-name-sans-extension in)
    640                         ".pdf")))
    641       (setq-local
    642        compile-command
    643        (format "groff -%s -Tpdf '%s' > '%s'"
    644                (file-name-extension in) in out))))
    645 #+end_src
    646 
    647 **** LaTeX
    648 
    649 Set a compile-command hook for latex files.  I prefer to write in
    650 org-mode, but compiling latex on its own is sometimes useful.
    651 
    652 #+begin_src emacs-lisp
    653   (use-package tex-mode
    654     :defer t
    655     :config
    656     <<latex-compile-command>>
    657     :hook (latex-mode . latex-compile-command))
    658 #+end_src
    659 
    660 ***** Compile Command
    661 
    662 #+name: latex-compile-command
    663 #+begin_src emacs-lisp :tangle no
    664   (defun latex-compile-command ()
    665     "Set the compile command for latex files."
    666     (setq-local compile-command
    667                 (format "pdflatex %s" buffer-file-name)))
    668 #+end_src
    669 
    670 ** Minor modes
    671 
    672 Minor modes that help with anything Emacs, be it programming, writing
    673 emails, or anything else that Emacs can do.
    674 
    675 *** Compile
    676 
    677 Bind =C-z RET= to [[help:compile][compile]] and =f9= to [[help:recompile][recompile]] (like [[help:compile][compile]], but no need
    678 to press =RET=).
    679 
    680 Also provided by this library is [[help:compilation-shell-minor-mode][compilation-shell-minor-mode]], a minor
    681 mode designed for [[*Shell][Shell]] that provides highlighting and navigation for
    682 errors and warnings.  I enable it in both [[*Shell][Shell]] and [[*Eshell][Eshell]].
    683 
    684 #+begin_src emacs-lisp
    685   (use-package compile
    686     :bind
    687     ("C-z C-m" . compile)
    688     ("<f9>" . recompile)
    689     :delight
    690     (compilation-shell-minor-mode " ¢")	; "C" for compile...
    691     :hook
    692     (eshell-mode . compilation-shell-minor-mode)
    693     (shell-mode . compilation-shell-minor-mode))
    694 #+end_src
    695 
    696 *** Hi-lock
    697 
    698 [[help:global-hi-lock-mode][global-hi-lock-mode]] binds a bunch of useful keys, but here I bind them
    699 manually to allow autoloading.  I also bind =C-c .= to my most used
    700 command, [[help:highlight-symbol-at-point][highlight-symbol-at-point]].
    701 
    702 #+begin_src emacs-lisp
    703   (use-package hi-lock
    704     :delight
    705     :bind
    706     ("C-c ." . highlight-symbol-at-point)
    707     ("C-x w i" . hi-lock-find-patterns)
    708     ("C-x w l" . highlight-lines-matching-regexp)
    709     ("C-x w p" . highlight-phrase)
    710     ("C-x w h" . highlight-regexp)
    711     ("C-x w ." . highlight-symbol-at-point)
    712     ("C-x w r" . unhighlight-regexp)
    713     ("C-x w b" . hi-lock-write-interactive-patterns))
    714 #+end_src
    715 
    716 *** Parens
    717 
    718 Highlight matching parens everywhere.
    719 
    720 #+begin_src emacs-lisp
    721   (use-package paren
    722     :config
    723     (show-paren-mode t))
    724 #+end_src
    725 
    726 *** Auto fill
    727 
    728 Instead of "Fill", show =^M= (carriage return) in the mode-line.
    729 
    730 #+begin_src emacs-lisp
    731   (use-package simple
    732     :delight
    733     (auto-fill-function " ^M"))
    734 #+end_src
    735 
    736 *** Isearch
    737 
    738 Instead of "ISearch", show =^S= (=C-s=) in the mode-line.
    739 
    740 #+begin_src emacs-lisp
    741   (use-package isearch
    742     :delight " ^S")
    743 #+end_src
    744 
    745 *** Eldoc
    746 
    747 Eldoc is what provides the function signature in the mode-line when
    748 editing Elisp.  By default, it waits for 0.5 seconds so I bump the
    749 delay down to 0.1.
    750 
    751 #+begin_src emacs-lisp
    752   (use-package eldoc
    753     :delight
    754     :defer t
    755     :custom
    756     (eldoc-idle-delay 0.1)
    757     :config
    758     (eldoc-add-command
    759      ;; Moving
    760      'paredit-backward
    761      'paredit-forward
    762      'paredit-forward-down
    763      'paredit-backward-up
    764      'paredit-backward-down
    765      'paredit-forward-up
    766      ;; Editing
    767      'paredit-raise-sexp
    768      'paredit-splice-sexp-killing-backward
    769      'paredit-convolute-sexp
    770      'paredit-close-round
    771      'paredit-close-round-and-newline
    772      'paredit-forward-delete
    773      'paredit-backward-delete))
    774 #+end_src
    775 
    776 ** Applications
    777 
    778 This section is for Elisp programs that have an interface of their
    779 own, rather than being just a major/minor mode.
    780 
    781 *** EWW
    782 
    783 Elisp web browser - I just set some variables to make eww the default
    784 browser, and change the width to 80 columns.
    785 
    786 #+begin_src emacs-lisp
    787   (use-package eww
    788     :defer t
    789     :custom
    790     (eww-bookmarks-directory
    791      (expand-file-name "eww" user-emacs-directory))
    792     (eww-browse-url-new-window-is-tab nil)
    793     :init
    794     (with-eval-after-load 'browse-url
    795       (setq browse-url-browser-function 'eww-browse-url
    796             browse-url-secondary-browser-function 'browse-url-externally-please))
    797     <<browse-url-externally-please>>
    798     :config
    799     <<eww-edit-current-url>>
    800     <<eww-set-width>>
    801     <<eww-follow-link-with-browse-url>>
    802     :bind
    803     ("C-z g" . eww)
    804     (:map eww-mode-map
    805           ("M-n" . forward-paragraph)
    806           ("M-p" . backward-paragraph)
    807           ("e" . eww-edit-current-url)
    808           ("V" . variable-pitch-mode)
    809           ("C-x f" . eww-set-width)
    810           ;; plumb
    811           ("f" . plumb-stream)
    812           ("D" . plumb-download-video)
    813           ("A" . plumb-audio)
    814           ;; transmission
    815           ("m" . transmission-add-url-at-point)
    816           ;; helm-eww
    817           ("B" . helm-eww-bookmarks)
    818           ("H" . helm-eww-history)
    819           ("s" . helm-eww-buffers)))
    820 #+end_src
    821 
    822 **** External browser
    823 
    824 #+name: browse-url-externally-please
    825 #+begin_src emacs-lisp :tangle no
    826   (defun browse-url-externally-please (url &optional ignored)
    827     "Open URL using either vimb or surf if they are found,
    828   otherwise use xdg-open."
    829     (interactive (browse-url-interactive-arg "URL: "))
    830     (call-process (or (executable-find "vimb")
    831                       (executable-find "surf")
    832                       (executable-find "xdg-open"))
    833                   nil 0 nil url))
    834 #+end_src
    835 
    836 **** Edit current URL
    837 
    838 Useful command to edit the current URL.  With prefix arg, open the
    839 edited URL in a new buffer.  Bound to =e= in eww-mode.
    840 
    841 #+name: eww-edit-current-url
    842 #+begin_src emacs-lisp :tangle no
    843   (defun eww-edit-current-url (&optional arg)
    844     "Edit the current URL.
    845   With prefix ARG, open in a new buffer."
    846     (interactive "p")
    847     (let ((url
    848            (read-string (if (= arg 1)
    849                             "URL: "
    850                           "URL (new buffer): ")
    851                         (eww-current-url))))
    852       (eww url arg)))
    853 #+end_src
    854 
    855 **** Set eww width
    856 
    857 This command sets [[help:shr-width][shr-width]] to a value read from the minibuffer.  Very
    858 useful in eww, and a fitting replacement for [[help:set-fill-column][set-fill-column]].
    859 
    860 #+name: eww-set-width
    861 #+begin_src emacs-lisp :tangle no
    862   (defun eww-set-width (width)
    863     "Set the html rendering width to WIDTH.
    864   If prefix arg is a number, use it.  Otherwise, read number from
    865   the minibuffer."
    866     (interactive (list
    867                   (if (numberp current-prefix-arg)
    868                       current-prefix-arg
    869                     (read-number "Set width: "
    870                                  (- (window-width) 5)))))
    871     (setq-local shr-width width)
    872     (eww-reload t))
    873 #+end_src
    874 
    875 **** Follow links using [[help:browse-url][browse-url]]
    876 
    877 The key =RET= in eww is bound to [[help:eww-follow-link][eww-follow-link]], which bypasses
    878 [[help:browse-url-handlers][browse-url-handlers]] meaning you can't open non-http links (except for
    879 the one exception, =mailto=).  Here I [[info:elisp#Advising Functions][override]] this function to use
    880 [[help:browse-url][browse-url]], and ensure that eww is used [[help:browse-url-browser-function][where possible]].  In effect,
    881 this means I can open gopher links from eww in [[*Elpher][elpher]].
    882 
    883 #+name: eww-follow-link-with-browse-url
    884 #+begin_src emacs-lisp :tangle no
    885   (defun eww-follow-link-with-browse-url (&optional external mouse-event)
    886     "Browse the URL under point.
    887   If EXTERNAL is single prefix, browse the URL using
    888   `browse-url-secondary-browser-function'."
    889     (interactive (list current-prefix-arg last-nonmenu-event))
    890     (mouse-set-point mouse-event)
    891     (let ((url (get-text-property (point) 'shr-url)))
    892       (cond
    893        ((not url)
    894         (message "No link under point"))
    895        ((and (consp external) (<= (car external) 4))
    896         (funcall browse-url-secondary-browser-function url)
    897         (shr--blink-link))
    898        ;; This is a #target url in the same page as the current one.
    899        ((and (url-target (url-generic-parse-url url))
    900              (eww-same-page-p url (plist-get eww-data :url)))
    901         (let ((dom (plist-get eww-data :dom)))
    902           (eww-save-history)
    903           (plist-put eww-data :url url)
    904           (eww-display-html 'utf-8 url dom nil (current-buffer))))
    905        (t
    906         (let ((browse-url-browser-function #'eww-browse-url))
    907           (browse-url url external))))))
    908 
    909   (advice-add 'eww-follow-link :override #'eww-follow-link-with-browse-url)
    910 #+end_src
    911 
    912 *** SHR
    913 
    914 #+begin_src emacs-lisp
    915   (use-package shr
    916     :defer t
    917     :custom
    918     (shr-width 80)
    919     :config
    920     <<un-duckduckgo-url>>
    921     :bind
    922     (:map shr-map
    923           ("f" . plumb-stream)
    924           ("A" . plumb-audio)
    925           ("D" . plumb-download-video)))
    926 #+end_src
    927 
    928 **** Remove duckduckgo tracking from url
    929 
    930 Duckduckgo does a very sinful thing -- instead of linking to
    931 =https://url.com=, it links to:
    932 : https://duckduckgo.com/l/?kh=-1&uddg=https%3A%2F%2Furl.com
    933 
    934 Here, I define a function that removes all this junk, and use [[info:elisp#Advising Named Functions][advice]]
    935 to filter the arguments given to [[help:shr-urlify][shr-urlify]].  Because this is
    936 relatively low-level, all occurences of duckduckgo's redirects that
    937 are parsed with =shr= are replaced with the clean version.
    938 
    939 #+name: un-duckduckgo-url
    940 #+begin_src emacs-lisp :tangle no
    941   (defun un-duckduckgo-url (args)
    942     "Cleanse a url from duckduckgo's janky redirect.
    943   This takes the same args as `shr-urlify', passed as a list."
    944     (let ((start (nth 0 args))
    945           (url (nth 1 args))
    946           (title (nth 2 args)))
    947       (list start
    948             (let ((unhexed (url-unhex-string url)))
    949               (if (string-match "\\`.*[&\\?]uddg=" unhexed)
    950                   (replace-match "" nil nil unhexed)
    951                 url))
    952             title)))
    953 
    954   (advice-add 'shr-urlify :filter-args #'un-duckduckgo-url)
    955 #+end_src
    956 
    957 *** ERC
    958 
    959 [[info:erc#Top][ERC]] is perhaps the greatest IRC client ever made.  I use [[https://znc.in][ZNC]] on my
    960 server, so I connect to that, and set my password in my [[info:auth#Top][authinfo]] file.
    961 
    962 #+begin_src emacs-lisp
    963   (use-package erc
    964     :defer t
    965     :custom
    966     (erc-server "jamzattack.xyz")
    967     (erc-nick "jamzattack")
    968     (erc-hide-list '("JOIN" "PART" "QUIT"))
    969     :config
    970     <<znc-detach-channel>>
    971     (add-to-list 'erc-modules 'notifications)
    972     (erc-update-modules)
    973     (erc-track-mode))
    974 #+end_src
    975 
    976 **** Detach instead of parting when buffer is killed
    977 
    978 I've just started using [[https://znc.in][ZNC]], an IRC bouncer.  ERC, however tries to
    979 part from a channel when its buffer is killed.  Instead, I want to
    980 detach so that I can reattach later.  Here, I override
    981 [[help:erc-kill-channel][erc-kill-channel]], resulting in the wanted behaviour.
    982 
    983 #+name: znc-detach-channel
    984 #+begin_src emacs-lisp :tangle no
    985   (defun znc-detach-channel ()
    986     "Hook that handles ZNC-specific channel killing behavior"
    987     (when (erc-server-process-alive)
    988       (when-let ((tgt (erc-default-target)))
    989         (erc-server-send (format "DETACH %s" tgt)
    990                          nil tgt))))
    991 
    992   (advice-add 'erc-kill-channel :override #'znc-detach-channel)
    993 #+end_src
    994 
    995 **** ERC notifications
    996 
    997 [[help:erc-notify-enable][erc-notify]] enables notifications for erc conversations.  I only enable
    998 it if the executable "dunst" is found, because it will crash Emacs
    999 unless a notification daemon is active.
   1000 
   1001 #+begin_src emacs-lisp
   1002   (use-package erc-notify
   1003     :after erc
   1004     :config
   1005     (when (executable-find "dunst")
   1006       (erc-notify-enable)))
   1007 #+end_src
   1008 
   1009 *** Info
   1010 
   1011 Rebind M-p and M-n to move by paragraphs.  By default M-n runs
   1012 [[help:clone-buffer][clone-buffer]], which I find to be completely useless.
   1013 
   1014 #+begin_src emacs-lisp
   1015   (use-package info
   1016     :bind
   1017     (:map Info-mode-map
   1018           ("M-p" . backward-paragraph)
   1019           ("M-n" . forward-paragraph)))
   1020 #+end_src
   1021 
   1022 *** Ibuffer
   1023 
   1024 Ibuffer is an interface similar to dired, but for editing your open
   1025 buffers.  I don't use it much now in favour of [[*HELM][Helm]], but it can be
   1026 useful for more complex filtering.
   1027 
   1028 #+begin_src emacs-lisp
   1029   (use-package ibuffer
   1030     :bind
   1031     ("C-x C-b" . ibuffer)
   1032     :init
   1033     (defun ibuffer-helm-major-mode-predicate (buffer)
   1034       "Returns t if BUF is a helm buffer."
   1035       (equal 'helm-major-mode
   1036              (with-current-buffer buffer
   1037                major-mode)))
   1038     :config
   1039     (add-to-list 'ibuffer-maybe-show-predicates
   1040                  #'ibuffer-helm-major-mode-predicate))
   1041 #+end_src
   1042 
   1043 *** Dired
   1044 
   1045 Group directories first.  This works only with GNU ls, so don't use
   1046 this if you use a different version.
   1047 
   1048 #+begin_src emacs-lisp
   1049   (use-package dired
   1050     :defer t
   1051     :config
   1052     (setq dired-listing-switches "-lahv --group-directories-first")
   1053     :init
   1054     (setq delete-by-moving-to-trash t))
   1055 #+end_src
   1056 
   1057 **** Dired-x
   1058 
   1059 I load [[info:dired-x#Top][dired-x]] after dired, to enable some useful commands such as
   1060 [[help:dired-mark-extension][dired-mark-extension]] and [[help:dired-mark-sexp][dired-mark-sexp]].
   1061 
   1062 I bind =C-x C-d= to [[help:dired-jump][dired-jump]], instead of the useless [[help:list-directory][list-directory]].
   1063 
   1064 #+begin_src emacs-lisp
   1065   (use-package dired-x
   1066     :after dired
   1067     :demand t
   1068     :bind
   1069     ("C-x C-d" . dired-jump))
   1070 #+end_src
   1071 
   1072 *** Diffing
   1073 
   1074 **** Ediff
   1075 
   1076 By default, [[info:ediff#Top][Ediff]] tries to open its own frame.  This doesn't work well
   1077 with EXWM, so I disable that feature.
   1078 
   1079 #+begin_src emacs-lisp
   1080   (use-package ediff
   1081     :defer t
   1082     :custom
   1083     (ediff-window-setup-function
   1084      #'ediff-setup-windows-plain))
   1085 #+end_src
   1086 
   1087 **** Smerge
   1088 
   1089 Easily merge git conflicts.  The prefix is =C-c ^= which works fine, but
   1090 I also bind =C-c n= and =C-c p= to go to the next/previous hunk.
   1091 
   1092 #+begin_src emacs-lisp
   1093   (use-package smerge-mode
   1094     :bind
   1095     (:map smerge-mode-map
   1096           ("C-c n" . smerge-next)
   1097           ("C-c p" . smerge-prev)))
   1098 #+end_src
   1099 
   1100 *** Proced
   1101 
   1102 I don't use [[help:proced][proced]] much in favour of [[help:list-processes][list-processes]] (because virtually
   1103 all of my processes are started from Emacs anyway) but I feel more
   1104 comfortable with it opening in the same window for some reason.
   1105 
   1106 #+begin_src emacs-lisp
   1107   (use-package proced
   1108     :defer t
   1109     :config
   1110     (add-to-list 'display-buffer-alist
   1111                  '("\\`\\*Proced\\*\\'" display-buffer-same-window)))
   1112 #+end_src
   1113 
   1114 ** Shells
   1115 
   1116 Shells in Emacs - both shell and eshell settings are here.
   1117 
   1118 *** Shell
   1119 
   1120 I don't want the shell buffer to open a new window, so add an entry in
   1121 [[help:display-buffer-alist][display-buffer-alist]].
   1122 
   1123 #+begin_src emacs-lisp
   1124   (use-package shell
   1125     :defer t
   1126     :config
   1127     (add-to-list 'display-buffer-alist
   1128                  '("\\`\\*shell\\*\\'" display-buffer-same-window)))
   1129 #+end_src
   1130 
   1131 *** Eshell
   1132 
   1133 A bunch of new eshell functions for my convenience; see their
   1134 docstrings or org headings for more details.
   1135 
   1136 Much of my eshell workflow is now housed in [[*Eshell outline mode][Eshell outline mode]], so a
   1137 few customisations have been removed recently.
   1138 
   1139 #+begin_src emacs-lisp
   1140   (use-package eshell
   1141     :custom
   1142     (eshell-history-size 10000)
   1143     (eshell-banner-message "")
   1144     :init
   1145     <<open-or-bury-eshell>>
   1146     :bind
   1147     (:map eshell-mode-map
   1148           ("C-c r" . eshell/r))
   1149     :config
   1150     (require 'esh-mode)
   1151     <<eshell/e>>
   1152     <<eshell/r>>
   1153     <<eshell/ssh>>
   1154     <<eshell/c>>
   1155     <<eshell/h>>
   1156     <<eshell/su>>
   1157     <<eshell/comint>>)
   1158 #+end_src
   1159 
   1160 **** Eshell functions
   1161 
   1162 ***** Edit a file
   1163 
   1164 Instead of opening a file with =emacsclient=, just edit it directly.
   1165 
   1166 #+name: eshell/e
   1167 #+begin_src emacs-lisp :tangle no
   1168   (defun eshell/e (&rest args)
   1169     "Edit a file from eshell."
   1170     (mapcar 'find-file args))
   1171 #+end_src
   1172 
   1173 ***** Comint
   1174 
   1175 A wrapper to start a comint process from eshell.
   1176 
   1177 Used like so:
   1178 #+begin_example sh
   1179 comint ed ~/.bashrc
   1180 #+end_example
   1181 
   1182 #+name: eshell/comint
   1183 #+begin_src emacs-lisp :tangle no
   1184   (defun eshell/comint (&rest args)
   1185     "Start a comint session running ARGS"
   1186     (let ((string (eshell-flatten-and-stringify args))
   1187           (program (executable-find (car args)))
   1188           (program-args (eshell-flatten-and-stringify (cdr args))))
   1189       (switch-to-buffer
   1190        (make-comint string
   1191                     (or program
   1192                         (user-error "Executable %s not found" (car args)))
   1193                     nil
   1194                     program-args))))
   1195 #+end_src
   1196 
   1197 ***** ssh via tramp
   1198 
   1199 A simple ssh wrapper that uses tramp.  ~ssh user@host~ will always be
   1200 run as the current user via local ssh.
   1201 
   1202 #+name: eshell/ssh
   1203 #+begin_src emacs-lisp :tangle no
   1204   (defun eshell/ssh (&rest args)
   1205     "Use tramp to move into an ssh directory.
   1206 
   1207   Usage: ssh [USER@]HOST [PATH]"
   1208     (let ((host (car args))
   1209           (path (or (cadr args) "")))
   1210       (eshell/cd (format "/ssh:%s:%s" host path))))
   1211 #+end_src
   1212 
   1213 ***** su via tramp
   1214 
   1215 A simple sudo wrapper that uses tramp.  Works from remote hosts as
   1216 well.
   1217 
   1218 #+name: eshell/su
   1219 #+begin_src emacs-lisp :tangle no
   1220   (defun eshell/su (&rest args)
   1221     (let ((user (or (car args) "root")))
   1222       (eshell/cd
   1223        (if (string-prefix-p "/ssh:" default-directory)
   1224            (format (replace-regexp-in-string
   1225                     "/ssh:\\(.*@\\)?:?+\\(.*\\):.*" ;regex
   1226                     "/ssh:\\1\\2|sudo:%s@\\2:"	  ;replacement
   1227                     default-directory)		  ;string
   1228                    user)
   1229          (format "/sudo:%s@localhost:" user)))))
   1230 #+end_src
   1231 
   1232 ***** Describe symbol
   1233 
   1234 A wee eshell interface to [[help:helpful-symbol][helpful-symbol]].  Falls back to
   1235 [[help:describe-symbol][describe-symbol]] if the above isn't available somehow.
   1236 
   1237 #+name: eshell/h
   1238 #+begin_src emacs-lisp :tangle no
   1239   (defun eshell/h (symbol-name &rest _ignored)
   1240     "Show help for SYMBOL-NAME.
   1241 
   1242   If `helpful-symbol' is available, use it.  Otherwise, fall back
   1243   to `describe-symbol'."
   1244     (let ((function (if (fboundp 'helpful-symbol)
   1245                         #'helpful-symbol
   1246                       #'describe-symbol)))
   1247       (funcall function (intern symbol-name))))
   1248 #+end_src
   1249 
   1250 ***** Rename eshell buffer
   1251 
   1252 Rename the current eshell.  Bound to =C-c r=, but can also be used from
   1253 eshell with or without an argument.
   1254 #+begin_example
   1255   r "my buffer's new name"
   1256 #+end_example
   1257 
   1258 With an argument, the buffer will be renamed that argument.  This is
   1259 achieved interactively with a prefix argument.
   1260 
   1261 Otherwise, it will be named according to:
   1262 - The current process
   1263 - TRAMP user@host
   1264 - The current working directory
   1265 
   1266 #+name: eshell/r
   1267 #+begin_src emacs-lisp :tangle no
   1268   (defun eshell/r (&optional name &rest _ignored)
   1269     "Rename the current buffer.
   1270 
   1271   This will be (in order):
   1272   - [eshell] the first argument
   1273   - [interactive] numeric prefix arg
   1274   - [interactive] read from minibuffer with non-numeric prefix arg
   1275   - the current process
   1276   - the TRAMP user@host
   1277   - the current working directory
   1278 
   1279   If a buffer of the chosen name already exists, rename it
   1280   uniquely."
   1281     (interactive (list (let ((arg current-prefix-arg))
   1282                          (cond
   1283                           ((numberp arg)
   1284                            arg)
   1285                           (arg
   1286                            (read-string "New name: "))))))
   1287     (setq name
   1288           (if (numberp name)
   1289               ;; If NAME is a number (either from eshell or via prefix
   1290               ;; arg), format it like eshell does.
   1291               (format "<%d>" name)
   1292             ;; Otherwise, add an extra space before.
   1293             (format " %s"
   1294                     (or
   1295                      name
   1296                      (let ((proc (eshell-interactive-process)))
   1297                        (when proc
   1298                          (process-name proc)))
   1299                      (let ((dir (eshell/pwd)))
   1300                        (if (string-match-p tramp-file-name-regexp dir)
   1301                            (replace-regexp-in-string
   1302                             ".*:\\(.*\\):.*" "\\1" dir)
   1303                          (replace-regexp-in-string
   1304                           abbreviated-home-dir "~/" dir)))))))
   1305     (let ((buffer
   1306            (concat eshell-buffer-name name)))
   1307       (rename-buffer buffer (get-buffer buffer))))
   1308 #+end_src
   1309 
   1310 ***** eshell/c
   1311 
   1312 [[help:eshell/c][eshell/c]] is a super beefy function that supersedes [[help:eshell/cat][eshell/cat]].  It
   1313 uses the GUI to its advantage to show:
   1314 - [[help:eshell/img][images]]
   1315 - [[help:eshell/ls][directories]]
   1316 - [[help:eshell/shr][rendered html]]
   1317 - [[help:eshell/fontify][fontified source code]]
   1318 
   1319 #+name: eshell/c
   1320 #+begin_src emacs-lisp :tangle no
   1321   (defun eshell/img (&rest files)
   1322     "Insert FILES into the buffer as images.
   1323 
   1324   If a file does not match `image-file-name-regexp', nothing
   1325   happens."
   1326     (dolist (file (mapcar #'expand-file-name (flatten-tree files)))
   1327       (when (string-match-p (image-file-name-regexp) file)
   1328         (goto-char (1- (point)))
   1329         (insert "\n")
   1330         (insert-image (create-image file nil nil
   1331                                     :max-height (* 2 (/ (window-pixel-height) 3))
   1332                                     :max-width (* 2 (/ (window-pixel-width) 3))))))
   1333     (goto-char (point-max))
   1334     nil)
   1335 
   1336   (defun eshell/shr (&rest files)
   1337     "Insert FILES into the buffer as rendered HTML."
   1338     (dolist (file (mapcar #'expand-file-name (flatten-tree files)))
   1339       (when (string-match-p "\\.html\\'" file)
   1340         (goto-char (1- (point)))
   1341         (shr-insert-document
   1342          (with-temp-buffer
   1343            (insert-file-contents file)
   1344            (libxml-parse-html-region (point-min) (point-max))))))
   1345     (goto-char (point-max))
   1346     nil)
   1347 
   1348   (defun eshell/fontify (&rest files)
   1349     "Insert FILES into the buffer.
   1350 
   1351   Like `eshell/cat', but fontifies the text as it would be if it
   1352   were visited normally."
   1353     (dolist (file (mapcar #'expand-file-name (flatten-tree files)))
   1354       (goto-char (1- (point)))
   1355       (insert "\n")
   1356       (insert
   1357        (with-temp-buffer
   1358          (insert-file-contents file)
   1359          (setq buffer-file-name file)
   1360          (normal-mode)
   1361          (font-lock-ensure)
   1362          (delete-region (1- (point-max)) (point-max))
   1363          (set-buffer-modified-p nil)
   1364          (buffer-string)))
   1365       (goto-char (point-max)))
   1366     nil)
   1367 
   1368   (defun eshell/c (&rest files)
   1369     "My overpowered version of `eshell/cat'.
   1370 
   1371   This command show FILES as:
   1372   - images (`eshell/img')
   1373   - directories (`eshell/ls')
   1374   - rendered html (`eshell/shr')
   1375   - fontified source code (`eshell/fontify')"
   1376     (dolist (file (mapcar (lambda (file)
   1377                             (let ((expanded (expand-file-name file)))
   1378                               (when (file-exists-p file)
   1379                                 expanded)))
   1380                           (flatten-tree files)))
   1381       (cond ((string-match-p (image-file-name-regexp) file)
   1382              (eshell/img file))
   1383             ((file-directory-p file)
   1384              (eshell/ls "-lah" file))
   1385             ((string-match-p "\\.html\\'" file)
   1386              (eshell/shr file))
   1387             (t
   1388              (eshell/fontify file)))))
   1389 #+end_src
   1390 
   1391 *** IELM
   1392 
   1393 A minor tweak for ielm, just binding =C-c C-z= to [[help:quit-window][quit-window]], as in
   1394 [[*SLIME][slime]], [[*Geiser][geiser]], etc.  To open up an ielm buffer, I use [[help:selime-ielm][selime-ielm]] from
   1395 my package [[*Selime][selime]], which opens it in a new window and is bound to =C-c
   1396 C-z= in [[help:selime-mode-map][selime-mode]].
   1397 
   1398 #+begin_src emacs-lisp
   1399   (use-package ielm
   1400     :defer t
   1401     :config
   1402     (define-key ielm-map (kbd "C-c C-z") #'quit-window))
   1403 #+end_src
   1404 
   1405 ** Saving the state of Emacs
   1406 
   1407 Packages that save where you were - recentf saves a list of edited
   1408 files, and desktop saves a list of variables and current buffers.
   1409 
   1410 *** Recentf
   1411 
   1412 This package saves a list of recently visited files.  I've had some
   1413 problems with Helm not loading the recentf list, so it is done here.
   1414 
   1415 #+begin_src emacs-lisp
   1416   (use-package recentf
   1417     :config (recentf-load-list))
   1418 #+end_src
   1419 
   1420 *** Desktop
   1421 
   1422 Save list of buffers and some variables when exiting Emacs.  Don't
   1423 save a list of frames, that just ends up spamming me with extra frames
   1424 everywhere.
   1425 
   1426 #+begin_src emacs-lisp
   1427   (use-package desktop
   1428     :custom
   1429     (desktop-restore-frames nil)
   1430     (history-delete-duplicates t)
   1431     (desktop-save-mode t)
   1432     :config
   1433     (add-to-list 'desktop-globals-to-save 'helm-ff-history)
   1434     (add-to-list 'desktop-globals-to-save 'extended-command-history))
   1435 #+end_src
   1436 
   1437 *** Save Place
   1438 
   1439 Like [[*Desktop][desktop-save-mode]], but saves the place in buffers between Emacs
   1440 sessions, rather than the list of buffers.
   1441 
   1442 #+begin_src emacs-lisp
   1443   (use-package saveplace
   1444     :config
   1445     (save-place-mode t))
   1446 #+end_src
   1447 
   1448 *** Winner-mode
   1449 
   1450 Saves window configurations so that you can use =C-c <left>= to undo
   1451 changes in window arrangement.
   1452 
   1453 #+begin_src emacs-lisp
   1454   (use-package winner
   1455     :config
   1456     (winner-mode))
   1457 #+end_src
   1458 
   1459 ** Interface tweaks
   1460 
   1461 Some settings for the UI of Emacs - mode-line, scroll-bar, etc.
   1462 
   1463 *** Extraneous bars
   1464 
   1465 Section for the three wasteful bars -- tool bar, menu bar, and scroll
   1466 bar.
   1467 
   1468 **** Scroll bar
   1469 
   1470 Disable the scroll bar using =customize=, but set the width in case I
   1471 decide to turn it on.
   1472 
   1473 #+begin_src emacs-lisp
   1474   (use-package scroll-bar
   1475     :custom
   1476     (scroll-bar-mode nil)
   1477     (scroll-bar-width 6 t))
   1478 #+end_src
   1479 
   1480 **** Menu bar
   1481 
   1482 Disable the menu bar.
   1483 
   1484 #+begin_src emacs-lisp
   1485   (use-package menu-bar
   1486     :config
   1487     (menu-bar-mode -1))
   1488 #+end_src
   1489 
   1490 **** Tool bar
   1491 
   1492 Disable the tool bar.
   1493 
   1494 #+begin_src emacs-lisp
   1495   (use-package tool-bar
   1496     :config
   1497     (tool-bar-mode -1))
   1498 #+end_src
   1499 
   1500 *** Mode-line
   1501 
   1502 **** Time
   1503 
   1504 Display the current time in the mode-line, and make it use 24-hour
   1505 time.  I adjust the [[help:world-clock-time-format][time format]] for the [[help:world-clock][world-clock]] so it displays the
   1506 offset from UTC/GMT, and change the list of timezones.
   1507 
   1508 #+begin_src emacs-lisp
   1509   (use-package time
   1510     :custom
   1511     (display-time-24hr-format t)
   1512     (world-clock-time-format "%A\t%d %B %R %Z\t(%z)")
   1513     (world-clock-list
   1514      '(("America/Los_Angeles" "Western US")
   1515        ("America/Chicago" "Central US")
   1516        ("America/New_York" "Eastern US")
   1517        ("Europe/London" "UK")
   1518        ("Europe/Paris" "Central Europe")
   1519        ("Asia/Calcutta" "India")
   1520        ("Asia/Chongqing" "China")
   1521        ("Asia/Seoul" "Korea/Japan")
   1522        ("Australia/Canberra" "Canberra")
   1523        ("Pacific/Auckland" "New Zealand")))
   1524     :config
   1525     (display-time-mode t))
   1526 #+end_src
   1527 
   1528 **** Battery
   1529 
   1530 Show battery information with =C-z b=.  Configuration for showing
   1531 battery status in the mode-line is in a separate [[*Battery info in mode-line][heading]].
   1532 
   1533 #+begin_src emacs-lisp
   1534   (use-package battery
   1535     :config
   1536     <<battery-mode-line>>
   1537     :bind
   1538     ("C-z b" . battery)
   1539     ("<XF86Battery>" . battery))
   1540 #+end_src
   1541 
   1542 ***** Battery info in mode-line
   1543 
   1544 Every time [[help:battery][battery]] is called (with =C-z b=), check if
   1545 [[help:display-battery-mode][display-battery-mode]] should be turned on or off.
   1546 
   1547 I also adjust [[help:battery-mode-line-format][battery-mode-line-format]] to add an extra space between
   1548 the battery and time.  By default, these push up against each other
   1549 which I do not like.
   1550 
   1551 #+name: battery-mode-line
   1552 #+begin_src emacs-lisp :tangle no
   1553   (setq battery-mode-line-format " [%b%p%%]")
   1554 
   1555   (defun set-display-battery-mode-accordingly ()
   1556     "Enable `display-battery-mode' if battery is being used.
   1557   If connected to power, or no battery is detected, disable it."
   1558     (if (and battery-status-function
   1559              (rassoc "discharging" (funcall battery-status-function)))
   1560         (display-battery-mode t)
   1561       (display-battery-mode 0)))
   1562 
   1563   (advice-add 'battery :after #'set-display-battery-mode-accordingly)
   1564 #+end_src
   1565 
   1566 **** Show the column
   1567 
   1568 Show the current column in the mode-line.  This is provided by the
   1569 =simple= package.
   1570 
   1571 #+begin_src emacs-lisp
   1572   (use-package simple
   1573     :config
   1574     (column-number-mode t))
   1575 #+end_src
   1576 
   1577 *** Indicate empty lines
   1578 
   1579 This displays a bunch of little lines in the fringe where there are
   1580 empty lines.  I decided that I want more stuff in my fringe, and have
   1581 been experimenting with it recently.
   1582 
   1583 It's entirely useless in non-editing modes, so I add it only to
   1584 [[help:prog-mode-hook][prog-mode-hook]] and [[help:text-mode-hook][text-mode-hook]].
   1585 
   1586 The state is actually controlled by the buffer-local variable
   1587 [[help:indicate-empty-lines][indicate-empty-lines]].  In order to add it to hooks, I need to define a
   1588 wrapper function (although called [[help:indicate-empty-lines-mode][indicate-empty-lines-mode]], this
   1589 function is not officially a minor mode--I just named it such for
   1590 consistency's sake).
   1591 
   1592 #+begin_src emacs-lisp
   1593   (defun indicate-empty-lines-mode (&optional arg)
   1594     "Indicate empty lines in the fringe.
   1595 
   1596   This is not actually a minor mode, just a wrapper function to set
   1597   the variable `indicate-empty-lines'.
   1598 
   1599   If called interactively, enable indicaty-empty-lines-mode if ARG
   1600   is positive, and disable it if ARG is zero or negative.  If
   1601   called from Lisp, also enable the mode if ARG is omitted or nil,
   1602   and toggle it if ARG is toggle; disable the mode otherwise."
   1603     (interactive (list (or current-prefix-arg 'toggle)))
   1604     (setq indicate-empty-lines
   1605           (cond ((eq arg 'toggle)
   1606                  (not indicate-empty-lines))
   1607                 ((numberp arg)
   1608                  (< 1 arg))
   1609                 (t t))))
   1610 
   1611   (add-hook 'text-mode-hook #'indicate-empty-lines-mode)
   1612   (add-hook 'prog-mode-hook #'indicate-empty-lines-mode)
   1613 #+end_src
   1614 
   1615 *** Keybindings
   1616 
   1617 A couple of keybindings to change the way lines are displayed.
   1618 
   1619 **** Line wrapping
   1620 
   1621 Simple keybinding to wrap/unwrap lines.  This feature is also provided
   1622 by =simple=.
   1623 
   1624 #+begin_src emacs-lisp
   1625   (use-package simple
   1626     :bind
   1627     ("C-c t" . toggle-truncate-lines))
   1628 #+end_src
   1629 
   1630 **** Line numbers
   1631 
   1632 Display line numbers.  I prefer to just use the mode-line because it
   1633 doesn't slow down Emacs as much.
   1634 
   1635 #+begin_src emacs-lisp
   1636   (use-package display-line-numbers
   1637     :bind
   1638     ("C-c l" . display-line-numbers-mode))
   1639 #+end_src
   1640 
   1641 **** Cycle spacing
   1642 
   1643 By default, =M-SPC= is bound to the less powerful [[help:just-one-space][just-one-space]].  I
   1644 rebind that key to [[help:cycle-spacing][cycle-spacing]], which does the same thing but on
   1645 successive invocations switches between one space and no spaces.
   1646 Thus, =M-SPC M-SPC= acts like =M-\= ([[help:delete-horizontal-space][delete-horizontal-space]])
   1647 
   1648 #+begin_src emacs-lisp
   1649   (use-package simple
   1650     :bind
   1651     ("M-SPC" . cycle-spacing))
   1652 #+end_src
   1653 
   1654 *** Minibuffer
   1655 
   1656 I set the variable [[help:enable-recursive-minibuffers][enable-recursive-minibuffers]] to allow recursive
   1657 minibuffers.  e.g. =M-!= rm -rf =C-u M-:= user-emacs-directory =RET= =RET=
   1658 
   1659 The library =mb-depth= provides a [[help:minibuffer-depth-indicate-mode][minor mode]] that that shows how deep
   1660 you are in the minibuffer "stack".
   1661 
   1662 #+begin_src emacs-lisp
   1663   (use-package mb-depth
   1664     :config
   1665     (setq enable-recursive-minibuffers t)
   1666     (minibuffer-depth-indicate-mode))
   1667 #+end_src
   1668 
   1669 ** Environment variables
   1670 
   1671 Set the =$EDITOR= to =emacsclient=.  Because I (almost) only use other
   1672 programs from within Emacs, this works.  If you don't use EXWM it
   1673 would be advisable to set this in =~/.xinitrc=.  Also set =$PAGER= to =cat=
   1674 for programs launched from Emacs, helpful with eshell because some
   1675 programs automatically output to the pager.
   1676 
   1677 #+begin_src emacs-lisp
   1678   (use-package env
   1679     :config
   1680     (setenv "EDITOR" "emacsclient")
   1681     (setenv "PAGER" "cat"))
   1682 #+end_src
   1683 
   1684 ** Windows
   1685 
   1686 Libraries related to Emacs windows.  Not to be confused with the
   1687 operating system.
   1688 
   1689 *** Window
   1690 
   1691 [[help:bury-buffer][bury-buffer]] is a very useful function so I bind it to =C-z C-z=, a
   1692 pretty accessible key.
   1693 
   1694 For purely pedantic reasons, I also bind =C-x _= to [[help:shrink-window][shrink-window]].  Why
   1695 does [[help:shrink-window-horizontally][shrink-window-horizontally]] have a keybinding by default but
   1696 [[help:shrink-window][shrink-window]] doesn't?
   1697 
   1698 A further useful keybinding is for [[help:quit-window][quit-window]], which sometimes isn't
   1699 bound even when it should be.  I bind it to =s-DEL=.
   1700 
   1701 I set the variable [[help:switch-to-prev-buffer-skip][switch-to-prev-buffer-skip]] to a custom function,
   1702 which means that [[help:switch-to-prev-buffer][switch-to-prev/next-buffer]] and [[help:bury-buffer][bury-buffer]] won't
   1703 switch to a buffer that I consider boring.  This includes:
   1704 - helm, helpful, help buffers
   1705 - empty buffers (but _not_ exwm buffers)
   1706 
   1707 #+begin_src emacs-lisp
   1708   (use-package window
   1709     :no-require
   1710     :demand
   1711     :bind
   1712     ("C-z C-z" . bury-buffer)
   1713     ("s-z" . bury-buffer)
   1714     ("C-x _" . shrink-window)
   1715     ("<s-backspace>" . quit-window)
   1716     ("s-s" . next-buffer)
   1717     ("s-d" . previous-buffer)
   1718     :config
   1719     (defun skip-boring-buffer-please (_window buffer _bury-or-kill)
   1720       "Return non-nil if BUFFER is boring.
   1721   A buffer is \"boring\" if one of the following is true:
   1722   - it is in `helm-major-mode', `helpful-mode', or `help-mode'
   1723   - it is empty
   1724   - it is _not_ in `exwm-mode'"
   1725       (or (member (buffer-local-value 'major-mode buffer)
   1726                   '(helm-major-mode
   1727                     helpful-mode
   1728                     help-mode))
   1729           (unless
   1730               (equal (buffer-local-value 'major-mode buffer)
   1731                      'exwm-mode)
   1732             (with-current-buffer buffer
   1733               (= (point-min) (point-max))))))
   1734     (setq switch-to-prev-buffer-skip
   1735           #'skip-boring-buffer-please))
   1736 #+end_src
   1737 
   1738 *** Windmove
   1739 
   1740 Bind =s-{c,h,t,n}= to switch window more easily.  I use dvorak, so this
   1741 is like ={i,j,k,l}= on a qwerty keyboard.  The shifted keys swap rather
   1742 than moving.
   1743 
   1744 #+begin_src emacs-lisp
   1745   (use-package windmove
   1746     :defer t
   1747     :bind
   1748     ("s-c" . windmove-up)
   1749     ("s-h" . windmove-left)
   1750     ("s-t" . windmove-down)
   1751     ("s-n" . windmove-right)
   1752     ("s-C" . windmove-swap-states-up)
   1753     ("s-H" . windmove-swap-states-left)
   1754     ("s-T" . windmove-swap-states-down)
   1755     ("s-N" . windmove-swap-states-right))
   1756 #+end_src
   1757 
   1758 *** Tab-bar
   1759 
   1760 I've started using [[help:tab-bar-mode][tab-bar-mode]] instead of exwm workspaces.  I don't
   1761 like the tab bar to be shown all the time, so I hide it.
   1762 
   1763 I also add advice to show the current tab and index in the echo area.
   1764 Somewhat awkwardly, a similar message is also shown by default when
   1765 [[help:tab-bar-mode][tab-bar-mode]] is nil.  I prefer my less subtle message, but I might
   1766 remove this in the future -- maybe show it in the mode-line instead?
   1767 
   1768 The keybindings =s-g= and =s-r= move to the previous or next tab
   1769 respectively, which fits well with my windmove keybindings.  =s-w= is
   1770 the default keybinding in exwm to switch workspace, so I reuse the key
   1771 to switch tab.
   1772 
   1773 #+begin_src emacs-lisp
   1774   (use-package tab-bar
   1775     :defer t
   1776     :custom
   1777     (tab-bar-show nil)
   1778     (tab-bar-close-button-show nil)
   1779     (tab-bar-new-button-show nil)
   1780     (tab-bar-tab-hints t)
   1781     :bind
   1782     ("s-g" . tab-previous)
   1783     ("s-r" . tab-next)
   1784     ("s-w" . tab-bar-switch-to-tab)
   1785     :config
   1786     (dolist (k (number-sequence 0 9))
   1787       (bind-key (kbd (format "s-%s" k)) 'tab-bar-select-tab))
   1788     (defadvice tab-bar-select-tab
   1789         (after show-tab-name activate)
   1790       "Show the tab name and index+1 in the echo area."
   1791       (message "Switched to tab: %s (%s)"
   1792                (propertize
   1793                 (cdr (assoc 'name (tab-bar--tab)))
   1794                 'face 'error)
   1795                (1+ (tab-bar--current-tab-index)))))
   1796 #+end_src
   1797 
   1798 ** View-mode
   1799 
   1800 I like using view-mode and scroll-lock-mode is kind-of useless, so I
   1801 rebind Scroll_Lock to toggle view-mode and enable view-mode if a
   1802 buffer is read-only.
   1803 
   1804 Also bind some keys to simplify movement.
   1805 
   1806 #+begin_src emacs-lisp
   1807   (use-package view
   1808     :custom (view-read-only t)
   1809     :bind
   1810     ("<Scroll_Lock>" . view-mode)
   1811     (:map view-mode-map
   1812           ("l" . recenter-top-bottom)
   1813           ("f" . forward-sexp)
   1814           ("b" . backward-sexp)))
   1815 #+end_src
   1816 
   1817 ** Fixing some default behaviour
   1818 
   1819 Tweak some default behaviour that pisses me off.
   1820 
   1821 *** Swap yes/no prompt with y/n
   1822 
   1823 Typing yes/no is an inconvenience that can be avoided.  Alias it to
   1824 y/n.  This would be wrapped in =(use-package subr ...)= but that isn't
   1825 requirable.
   1826 
   1827 #+begin_src emacs-lisp
   1828   (defalias 'yes-or-no-p 'y-or-n-p)
   1829   (bind-key "RET" 'y-or-n-p-insert-y y-or-n-p-map)
   1830 #+end_src
   1831 
   1832 *** Enable all the features
   1833 
   1834 Disable the annoying "This is an advanced feature" thing.  It seems so
   1835 dumb that this feature exists.
   1836 
   1837 #+begin_src emacs-lisp
   1838   (use-package novice
   1839     :custom
   1840     (disabled-command-function nil))
   1841 #+end_src
   1842 
   1843 *** Disable audible and visual bell
   1844 
   1845 Don't ring the damn bell.  This is provided by the file "terminal.c"
   1846 which isn't a loadable feature, so use custom instead.
   1847 
   1848 #+begin_src emacs-lisp
   1849   (use-package custom
   1850     :custom
   1851     (ring-bell-function 'ignore))
   1852 #+end_src
   1853 
   1854 ** Theme
   1855 
   1856 Allow themes to be loaded from the [[file:lisp/themes][lisp/themes]] directory, allow all
   1857 themes to be loaded, then load my [[file:lisp/themes/custom-theme.el][custom theme]].
   1858 
   1859 #+begin_src emacs-lisp
   1860   (use-package custom
   1861     :custom
   1862     (custom-theme-directory
   1863      (expand-file-name "lisp/themes" user-emacs-directory))
   1864     (custom-safe-themes t)
   1865     (custom-enabled-themes '(custom)))
   1866 #+end_src
   1867 
   1868 ** Convenience
   1869 
   1870 Some convenience features.
   1871 
   1872 *** Hippie expand
   1873 
   1874 Hippie-expand is a slightly more useful replacement for
   1875 dabbrev-expand.  It can make use of multiple sources, including
   1876 filenames, kill-ring, and dabbrev.
   1877 
   1878 #+begin_src emacs-lisp
   1879   (use-package hippie-exp
   1880     :defer t
   1881     :bind
   1882     ("M-/" . hippie-expand))
   1883 #+end_src
   1884 
   1885 *** Paragraphs
   1886 
   1887 Bind =M-n= and =M-p= to move by paragraph.  I used to do this on a
   1888 per-mode basis, but that got annoying.  These functions are defined in
   1889 =paragraphs.el= which isn't a loadable feature, so I use =(use-package
   1890 emacs)= instead.
   1891 
   1892 #+begin_src emacs-lisp
   1893   (use-package emacs
   1894     :bind
   1895     ("M-n" . forward-paragraph)
   1896     ("M-p" . backward-paragraph))
   1897 #+end_src
   1898 
   1899 *** Project
   1900 
   1901 [[info:emacs#Projects][project.el]] is Emacs' builtin library of convenience functions for
   1902 working on a "project", which is really just a directory with version
   1903 control.
   1904 
   1905 By default, the project-specific Eshells open in another window, so I
   1906 adjust [[help:display-buffer-alist][display-buffer-alist]] to display them in the [[help:display-buffer-same-window][same window]].
   1907 
   1908 #+begin_src emacs-lisp
   1909   (use-package project
   1910     :defer t
   1911     :config
   1912     (add-to-list 'display-buffer-alist
   1913                  '("-eshell\\*\\'" display-buffer-same-window)))
   1914 #+end_src
   1915 
   1916 ** Mail
   1917 
   1918 *** Gnus
   1919 
   1920 I've finally managed to make the switch to gnus.  Frankly, my main
   1921 motivation was to avoid setting up notmuch again with my university
   1922 email.
   1923 
   1924 As far as I can tell, using a maildir with gnus is a hassle -- so I'm
   1925 just using IMAP.
   1926 
   1927 #+begin_src emacs-lisp
   1928   (use-package gnus
   1929     :init
   1930     (setq mail-user-agent 'gnus-user-agent)
   1931     :config
   1932     (setq gnus-select-method
   1933           '(nntp "news.gwene.org"))
   1934     (setq gnus-secondary-select-methods
   1935           '((nnimap "gmail"
   1936                     (nnimap-address "imap.gmail.com"))
   1937             (nnimap "university"
   1938                     (nnimap-address "outlook.office365.com"))
   1939             (nnimap "mail.jamzattack.xyz")
   1940             (nntp "news.eternal-september.org"
   1941                   (nntp-authinfo-file "~/.authinfo.gpg"))))
   1942     (defun gnus-group-set-up-imenu-please ()
   1943       (setq imenu-generic-expression
   1944             '(("Topic" "\\[ \\(.*?\\) -- [0-9]+ \\]" 1)
   1945               ("Unread" "[1-9]+.*: \\(.*\\)" 1))))
   1946     (add-hook 'gnus-group-mode-hook 'gnus-group-set-up-imenu-please)
   1947     :bind
   1948     ("C-z C-n" . gnus-unplugged)
   1949     ("C-z n" . gnus-plugged))
   1950 #+end_src
   1951 
   1952 **** Gnus-sum
   1953 
   1954 Nicer summary & thread formatting.  Credit to [[https://protesilaos.com][Protesilaos Stavrou]]
   1955 
   1956 #+begin_src emacs-lisp
   1957   (use-package gnus-sum
   1958     :defer t
   1959     :custom
   1960     (gnus-summary-line-format "%U%R%z %-16,16&user-date;  %4L:%-30,30f  %B%s\n")
   1961     (gnus-summary-mode-line-format "%p")
   1962     (gnus-sum-thread-tree-false-root "─┬> ")
   1963     (gnus-sum-thread-tree-indent " ")
   1964     (gnus-sum-thread-tree-leaf-with-other "├─> ")
   1965     (gnus-sum-thread-tree-root "")
   1966     (gnus-sum-thread-tree-single-leaf "└─> ")
   1967     (gnus-sum-thread-tree-vertical "│")
   1968     (gnus-ignored-from-addresses
   1969      (mapcar #'regexp-quote
   1970              '("jdb@jamzattack.xyz"
   1971                "beardsleejamie@gmail.com"
   1972                "beardsjami@myvuw.ac.nz"))))
   1973 #+end_src
   1974 
   1975 **** Gnus-msg
   1976 
   1977 Gnus' library for sending messages.  [[help:gnus-posting-styles][gnus-posting-styles]] allows you to
   1978 adjust headers, signatures, etc. based on how you got to the
   1979 composition buffer.  All messages composed from my university mailbox
   1980 will be sent from my university address.  Very nice!
   1981 
   1982 [[info:gnus#Posting Styles][Posting Styles in the gnus manual]]
   1983 
   1984 #+begin_src emacs-lisp
   1985   (use-package gnus-msg
   1986     :defer t
   1987     :custom
   1988     (gnus-posting-styles
   1989      `(("nnimap\\+university:.*"
   1990         (From ,(format "%s <%s@%s>" user-full-name "beardsjami" "myvuw.ac.nz"))
   1991         (signature "Jamie Beardslee (300484191)"))
   1992        ("nnimap\\+gmail:.*"
   1993         (From ,(format "%s <%s@%s>" user-full-name "beardsleejamie" "gmail.com")))
   1994        ("nnimap\\+mail\\.jamzattack\\.xyz:.*"
   1995         (From ,(format "%s <%s@%s>" user-full-name "jdb" "jamzattack.xyz"))))))
   1996 #+end_src
   1997 
   1998 **** Gnus-art
   1999 
   2000 Article stuff.  Gnus tries to use the =smiley= library to convert
   2001 emoticons into images -- I turned it off because it looks terrible.
   2002 
   2003 I also want some buttons to show signature status and alternative MIME
   2004 types, which is achieved with [[help:gnus-buttonized-mime-types][gnus-buttonized-mime-types]].
   2005 
   2006 #+begin_src emacs-lisp
   2007   (use-package gnus-art
   2008     :defer t
   2009     :custom
   2010     (gnus-treat-display-smileys nil)
   2011     (gnus-buttonized-mime-types
   2012      '("multipart/signed" "multipart/alternative")))
   2013 #+end_src
   2014 
   2015 **** Gnus-topic
   2016 
   2017 Gnus can sort your groups by topic, which I enable in
   2018 [[help:gnus-group-mode-hook][gnus-group-mode-hook]].
   2019 
   2020 It shows titles for empty topics by default, which I find to get in
   2021 the way.  I set the variable [[help:gnus-topic-display-empty-topics][gnus-topic-display-empty-topics]] to
   2022 disable this.  Default behaviour can be restored with =T H=.
   2023 
   2024 #+begin_src emacs-lisp
   2025   (use-package gnus-topic
   2026     :defer t
   2027     :custom
   2028     (gnus-topic-display-empty-topics nil)
   2029     :hook
   2030     (gnus-group-mode . gnus-topic-mode))
   2031 #+end_src
   2032 
   2033 **** Gnus-start
   2034 
   2035 Just getting rid of a couple of extra files in $HOME.
   2036 
   2037 - Gnus by default creates =~/.newsrc= in a format compatible with other
   2038   newsreaders, but I don't use any so it's just an extra line in my
   2039   ls.
   2040 - Move the /dribble/ (i.e. auto-save) files to =~/.cache=.
   2041 
   2042 #+begin_src emacs-lisp
   2043   (use-package gnus-start
   2044     :defer t
   2045     :custom
   2046     (gnus-save-newsrc-file nil)
   2047     (gnus-dribble-directory "~/.cache/"))
   2048 #+end_src
   2049 
   2050 **** Gnus-notifications
   2051 
   2052 This library provides desktop notifications for gnus.  To enable
   2053 notifications for a group, set its level to something lower than
   2054 [[help:gnus-notifications-minimum-level][gnus-notifications-minimum-level]] (default 1).  This can be done from
   2055 the =*Group*= buffer with [[help:gnus-group-set-current-level][gnus-group-set-current-level]] (=S l=).
   2056 
   2057 Like [[*ERC notifications][ERC notifications]], I only enable it when =dunst= is installed.
   2058 
   2059 #+begin_src emacs-lisp
   2060   (use-package gnus-notifications
   2061     :when (executable-find "dunst")
   2062     :after gnus-start
   2063     :hook gnus-after-getting-new-news)
   2064 #+end_src
   2065 
   2066 *** Sendmail
   2067 
   2068 Sending mail.  I use [[https://marlam.de/msmtp/][msmtp]] to send mail because it works well with
   2069 multiple smtp servers.  I tried using [[info:smtpmail#Top][smtpmail]] but couldn't get it to
   2070 switch between the two easily.
   2071 
   2072 I set it up to use the from header to determine how to send mail.
   2073 
   2074 #+begin_src emacs-lisp
   2075   (use-package sendmail
   2076     :defer t
   2077     :config
   2078     (setq send-mail-function 'sendmail-send-it
   2079           sendmail-program (or (executable-find "msmtp")
   2080                                sendmail-program)
   2081           mail-envelope-from 'header))
   2082 #+end_src
   2083 
   2084 *** Message
   2085 
   2086 The mode for editing messages.  I bind =C-c C-q= to a function that
   2087 either fills or unfills the message, and =C-c $= to check spelling.
   2088 
   2089 #+begin_src emacs-lisp
   2090   (use-package message
   2091     :config
   2092     (defun fill-message-please (&optional unfill)
   2093       "Fill the whole message.
   2094 
   2095     With prefix arg UNFILL, unfill the message (i.e. paragraphs will
   2096     all be on one line)"
   2097       (interactive "P")
   2098       (let ((fill-column (if unfill
   2099                              (point-max)
   2100                            fill-column)))
   2101         (message-fill-yanked-message)))
   2102     <<my-gnus-add-gcc-header>>
   2103     :hook
   2104     (message-send . my-gnus-add-gcc-header)
   2105     :bind
   2106     (:map message-mode-map
   2107           ("C-c C-q" . fill-message-please)
   2108           ("C-c $" . ispell-message)))
   2109 #+end_src
   2110 
   2111 **** Archive mail from jamzattack.xyz
   2112 
   2113 I can't figure out how to make my postfix server copy messages to
   2114 "Sent", so I do it with gnus.
   2115 
   2116 #+name: my-gnus-add-gcc-header
   2117 #+begin_src emacs-lisp :tangle no
   2118   (defun my-gnus-add-gcc-header ()
   2119     "If message is from anybody@jamzattack.xyz, archive it via IMAP.
   2120   This will also archive it in the default nnfolder+archive group."
   2121     (interactive)
   2122     (let ((new-gcc
   2123            (format-time-string
   2124             "nnfolder+archive:sent.%Y-%m, nnimap+mail.jamzattack.xyz:Sent")))
   2125       (save-excursion
   2126         (goto-char (point-min))
   2127         (ignore-errors
   2128           (when (re-search-forward "^From: \\(.*\\)jamzattack.xyz>?")
   2129             (message-replace-header "Gcc" new-gcc))))))
   2130 #+end_src
   2131 
   2132 *** MIME
   2133 
   2134 Stuff to do with MIME
   2135 
   2136 **** mm-decode
   2137 
   2138 The library responsible for decoding mime parts.  I prefer reading
   2139 text/plain, so discourage the other common alternatives.  I also want
   2140 to verify messages that have a signature, so I set [[help:mm-verify-option][mm-verify-option]].
   2141 
   2142 #+begin_src emacs-lisp
   2143   (use-package mm-decode
   2144     :defer t
   2145     :custom
   2146     (mm-discouraged-alternatives
   2147      '("text/html" "text/richtext"))
   2148     (mm-verify-option 'known))
   2149 #+end_src
   2150 
   2151 **** mml-sec
   2152 
   2153 Yay for encryption.  I set up messages to encrypt to myself as well as
   2154 the recipient, and sign with the sender.
   2155 
   2156 #+begin_src emacs-lisp
   2157   (use-package mml-sec
   2158     :defer t
   2159     :custom
   2160     (mml-secure-openpgp-encrypt-to-self t)
   2161     (mml-secure-openpgp-sign-with-sender t))
   2162 #+end_src
   2163 
   2164 ** Typing
   2165 
   2166 *** Input methods
   2167 
   2168 #+begin_src emacs-lisp
   2169   (use-package quail
   2170     :defer t
   2171     :config
   2172     <<dvorak-keyboard-layout>>
   2173     <<maori-input-method>>
   2174     <<shavian-input-method>>
   2175     <<hangul-input-method>>
   2176     )
   2177 #+end_src
   2178 
   2179 **** Dvorak keyboard layout
   2180 
   2181 Define a dvorak keyboard layout and enable it.
   2182 
   2183 Quail keyboard layouts are laid out in six 30-column blocks.  The
   2184 first and last are above and below the alphanumeric keys.  Each key is
   2185 represented by a pair of its non-shifted and shifted variants i.e. =aA=
   2186 for the =a= key.
   2187 
   2188 This allows me to use another input method's physical layout rather
   2189 than just the keys themselves.
   2190 
   2191 In the =korean-hangul= input method though, characters' positions are
   2192 laid out according to the physical position, so I want that to be
   2193 taken into account.  In other words, I want to use the "qwerty k"
   2194 rather than the "dvorak k".
   2195 
   2196 | Qwerty | Dvorak | Hangul |
   2197 |--------+--------+--------|
   2198 | k      | t      | ㅏ     |
   2199 | r      | p      | ㄱ     |
   2200 
   2201 Unfortunately, whether a keyboard layout actually uses this system is
   2202 totally random.  See the examples in the following table, where a
   2203 "layout dependent" input method means that it uses the keyboard
   2204 translation according to [[help:quail-keyboard-layout][quail-keyboard-layout]].
   2205 
   2206 | Layout            | Layout dependent? | Should be? |
   2207 |-------------------+-------------------+------------|
   2208 | cyrillic-translit | t                 | nil        |
   2209 | programmer-dvorak | nil               | t          |
   2210 | korean-hangul     | nil               | t          |
   2211 | japanese          | nil               | nil        |
   2212 
   2213 Because of this mess, I also define the function
   2214 [[help:toggle-quail-keyboard-layout][toggle-quail-keyboard-layout]], which switches between the two and is
   2215 bound to =s-\=.
   2216 
   2217 #+name: dvorak-keyboard-layout
   2218 #+begin_src emacs-lisp :tangle no
   2219   (push
   2220    (cons "dvorak"
   2221          (concat
   2222           "                              "
   2223           "`~1!2@3#4$5%6^7&8*9(0)[{]}    "   ; numbers
   2224           "  '\",<.>pPyYfFgGcCrRlL/?=+\\|  " ; qwerty
   2225           "  aAoOeEuUiIdDhHtTnNsS-_      "   ; asdf
   2226           "  ;:qQjJkKxXbBmMwWvVzZ        "   ; zxcv
   2227           "                              "))
   2228    quail-keyboard-layout-alist)
   2229 
   2230   (defun toggle-quail-keyboard-layout ()
   2231     "Toggle the keyboard layout between dvorak and qwerty.
   2232 
   2233   This sets `quail-keyboard-layout-type' to the opposite of what is
   2234   currently selected."
   2235     (interactive)
   2236     (if (string-equal quail-keyboard-layout-type "dvorak")
   2237         (quail-set-keyboard-layout "standard")
   2238       (quail-set-keyboard-layout "dvorak"))
   2239     (message "Switched to layout: %s"
   2240              (propertize quail-keyboard-layout-type
   2241                          'face 'bold)))
   2242 
   2243   (bind-key "s-\\" 'toggle-quail-keyboard-layout)
   2244 #+end_src
   2245 
   2246 **** Māori
   2247 
   2248 My own input method for Māori.  It provides prefix and postfix
   2249 variants.
   2250 
   2251 - Postfix:
   2252 | aa  | ā  |
   2253 | a-  | ā  |
   2254 | aaa | aa |
   2255 | a-- | a- |
   2256 
   2257 - Prefix:
   2258 | aa  | ā  |
   2259 | -a  | ā  |
   2260 | aaa | aa |
   2261 | --a | -a |
   2262 
   2263 Hosted [[https://git.jamzattack.xyz/maori-input-method][here]].
   2264 
   2265 #+name: maori-input-method
   2266 #+begin_src emacs-lisp :tangle no
   2267   (use-package maori-input-method
   2268     :straight
   2269     (maori-input-method
   2270      :type git
   2271      :repo "git@jamzattack.xyz:maori-input-method"))
   2272 #+end_src
   2273 
   2274 **** Shavian
   2275 
   2276 My own input method for Shavian.
   2277 
   2278 Hosted [[https://git.jamzattack.xyz/shavian-input-method][here]].
   2279 
   2280 #+name: shavian-input-method
   2281 #+begin_src emacs-lisp :tangle no
   2282   (use-package shavian-input-method
   2283     :straight
   2284     (shavian-input-method
   2285      :type git
   2286      :repo "git@jamzattack.xyz:shavian-input-method"))
   2287 #+end_src
   2288 
   2289 **** Hangul
   2290 
   2291 An adjustment to the hangul input method that uses
   2292 [[help:quail-keyboard-translate][quail-keyboard-translate]] to determine the character, rather than
   2293 assuming the standard layout.
   2294 
   2295 For more information, see [[*Dvorak keyboard layout][this section]] and [[https://blog.jamzattack.xyz/emacs-hangul-input.html][my blog post about the
   2296 subject]].
   2297 
   2298 #+name: hangul-input-method
   2299 #+begin_src emacs-lisp :tangle no
   2300   (with-eval-after-load 'hangul
   2301     (defun hangul2-input-method (key)
   2302       "2-Bulsik input method."
   2303       (setq key (quail-keyboard-translate key))
   2304       (if (or buffer-read-only (not (alphabetp key)))
   2305           (list key)
   2306         (quail-setup-overlays nil)
   2307         (let ((input-method-function nil)
   2308               (echo-keystrokes 0)
   2309               (help-char nil))
   2310           (setq hangul-queue (make-vector 6 0))
   2311           (hangul2-input-method-internal key)
   2312           (unwind-protect
   2313               (catch 'exit-input-loop
   2314                 (while t
   2315                   (let* ((seq (read-key-sequence nil))
   2316                          (cmd (lookup-key hangul-im-keymap seq))
   2317                          key)
   2318                     (cond
   2319                      ((and (stringp seq)
   2320                            (= 1 (length seq))
   2321                            (setq key (quail-keyboard-translate (aref seq 0)))
   2322                            (alphabetp key))
   2323                       (hangul2-input-method-internal key))
   2324                      ((commandp cmd)
   2325                       (call-interactively cmd))
   2326                      (t
   2327                       (setq unread-command-events
   2328                             (nconc (listify-key-sequence seq)
   2329                                    unread-command-events))
   2330                       (throw 'exit-input-loop nil))))))
   2331             (quail-delete-overlays))))))
   2332 #+end_src
   2333 
   2334 **** Fixing various input methods
   2335 
   2336 As said [[*Dvorak keyboard layout][above]], some input methods don't work the way they should with
   2337 a custom [[help:quail-keyboard-layout][quail-keyboard-layout]].
   2338 
   2339 The variable [[help:quail-package-alist][quail-package-alist]] is an alist of the following values:
   2340 | Index | Description                 |
   2341 |-------+-----------------------------|
   2342 |     0 | NAME                        |
   2343 |     1 | TITLE                       |
   2344 |     2 | QUAIL-MAP                   |
   2345 |     3 | GUIDANCE                    |
   2346 |     4 | DOCSTRING                   |
   2347 |     5 | TRANSLATION-KEYS            |
   2348 |     6 | FORGET-LAST-SELECTION       |
   2349 |     7 | DETERMINISTIC               |
   2350 |     8 | KBD-TRANSLATE               |
   2351 |     9 | SHOW-LAYOUT                 |
   2352 |    10 | DECODE-MAP                  |
   2353 |    11 | MAXIMUM-SHORTEST            |
   2354 |    12 | OVERLAY-PLIST               |
   2355 |    13 | UPDATE-TRANSLATION-FUNCTION |
   2356 |    14 | CONVERSION-KEYS             |
   2357 |    15 | SIMPLE                      |
   2358 
   2359 The elements I'm mostly interested in are 8 (=KBD-TRANSLATE=) and 9
   2360 (=SHOW-LAYOUT=).
   2361 
   2362 #+begin_src emacs-lisp
   2363   (with-eval-after-load "quail/cyrillic"	; no kbd-translate
   2364     (setf (nth 8 (assoc "cyrillic-translit" quail-package-alist)) nil
   2365           (nth 9 (assoc "cyrillic-translit" quail-package-alist)) t))
   2366 
   2367   (with-eval-after-load "quail/programmer-dvorak"	; kbd-translate
   2368     (setf (nth 8 (assoc "programmer-dvorak" quail-package-alist)) t
   2369           (nth 9 (assoc "programmer-dvorak" quail-package-alist)) t))
   2370 #+end_src
   2371 
   2372 *** Abbrevs
   2373 
   2374 #+begin_src emacs-lisp
   2375   (use-package abbrev
   2376     :hook
   2377     (text-mode . abbrev-mode)
   2378     :delight
   2379     :config
   2380     (setq save-abbrevs nil)
   2381     <<text-mode-abbrevs>>
   2382     )
   2383 #+end_src
   2384 
   2385 **** My text-mode abbrevs
   2386 
   2387 #+name: text-mode-abbrevs
   2388 #+begin_src emacs-lisp :tangle no
   2389   (use-package text-mode-abbrevs
   2390     :load-path "lisp/abbrev")
   2391 #+end_src
   2392 
   2393 ** Printing
   2394 
   2395 Library for printing things as postscript.
   2396 
   2397 The =header= variables are related to the automatically generated header
   2398 that shows the buffer name, file name, date, and page number.  I end
   2399 up disabling this feature by setting [[help:ps-print-header][ps-print-header]] to =nil=, but
   2400 nonetheless want it to look nicer in case I want to print buffer that
   2401 needs pages numbers.  I can do this with the function
   2402 [[help:please-print-buffer-with-header][please-print-buffer-with-header]] defined [[please-print-buffer][here]].
   2403 
   2404 The [[https://en.wikipedia.org/wiki/N-up][n-up]] variables are for printing multiple pages on a single sheet
   2405 of paper.  I use this via [[help:please-print-buffer-side-by-side][please-print-buffer-side-by-side]] also
   2406 defined [[please-print-buffer][here]].  I set [[help:ps-n-up-margin][ps-n-up-margin]] to 7, which is roughly 2.5mm.
   2407 This allows for two 70-character wide pages to be printed side by
   2408 side.
   2409 
   2410 #+begin_src emacs-lisp
   2411   (use-package ps-print
   2412     :defer t
   2413     :init
   2414     <<please-print-buffer>>
   2415     :config
   2416     (setq ps-print-header nil
   2417           ps-print-header-frame nil
   2418           ps-header-lines 1
   2419           ps-header-font-size ps-font-size
   2420           ps-header-title-font-size ps-font-size
   2421           ps-n-up-border-p nil
   2422           ps-left-margin (/ (* 72 1.0) 2.54) ; 1 cm
   2423           ps-right-margin (/ (* 72 1.0) 2.54) ; 1 cm
   2424           ps-n-up-margin (/ (* 72 0.5) 2.54))) ; 5 mm
   2425 #+end_src
   2426 
   2427 *** Printing functions
   2428 
   2429 [[help:please-print-buffer][please-print-buffer]] is a big printing function that asks a few
   2430 [[help:y-or-n-p][y-or-n-p]]s to determine some commonly used settings.
   2431 
   2432 A couple of separate functions for invidual options are also defined:
   2433 [[help:please-print-buffer-with-header][please-print-buffer-with-header]] and [[help:please-print-buffer-side-by-side][please-print-buffer-side-by-side]].
   2434 
   2435 I autoload [[help:ps-print-preprint][ps-print-preprint]] rather than using [[help:require][require]], as this goes
   2436 in the =:init= section.
   2437 
   2438 #+name: please-print-buffer
   2439 #+begin_src emacs-lisp :tangle no
   2440   (autoload 'ps-print-preprint "ps-print")
   2441 
   2442   (defun please-print-buffer (&optional file color header side-by-side)
   2443     "Print the current BUFFER.
   2444 
   2445   FILE is a filename to save the generated postscript in.  If this
   2446   is provided, it will NOT be sent to the printer.
   2447 
   2448   The arguments COLOR and SIDE-BY-SIDE are straightforward -- they
   2449   will be determined via `y-or-n-p'.
   2450 
   2451   HEADER works weirdly interactively -- I don't usually want the
   2452   header printed so the `y-or-n-p' asks whether to remove it."
   2453     (interactive
   2454      (list
   2455       ;; `ps-print-preprint' needs a list or number argument
   2456       (ps-print-preprint (when (y-or-n-p "Save to file? ") 1))
   2457       (y-or-n-p "Color? ")
   2458       (not (y-or-n-p "Remove header? "))
   2459       (y-or-n-p "Side by side? ")))
   2460     (let* ((ps-font-size
   2461             (if side-by-side
   2462                 '(10 . 12)
   2463               ps-font-size))
   2464            (ps-n-up-printing
   2465             (if side-by-side
   2466                 2
   2467               1))
   2468            (ps-print-header header)
   2469            (ps-print-color-p (if color
   2470                                  t
   2471                                'black-white)))
   2472       (ps-print-buffer-with-faces file)))
   2473 
   2474   (defun please-print-buffer-side-by-side (file &optional color)
   2475     "Print the current buffer, split into two subpages.
   2476 
   2477   This calls `ps-print-buffer-with-faces' with the variable
   2478   `ps-n-up-printing' set to 2."
   2479     (interactive
   2480      (list (ps-print-preprint current-prefix-arg)
   2481            (y-or-n-p "Color? ")))
   2482     (please-print-buffer file color ps-print-header t))
   2483 
   2484   (defun please-print-buffer-with-header (file &optional color)
   2485     "Print the current buffer with a header.
   2486 
   2487   This calls `ps-print-buffer-with-faces' with the variable
   2488   `ps-print-header' set to t."
   2489     (interactive
   2490      (list (ps-print-preprint current-prefix-arg)
   2491            (y-or-n-p "Color? ")))
   2492     (please-print-buffer file color t))
   2493 #+end_src
   2494 
   2495 * Local packages
   2496 
   2497 Not necessarily /my/ packages, but packages that are in the [[file:lisp/][lisp]]
   2498 directory.
   2499 
   2500 ** Internet
   2501 
   2502 A selection of packages to facilitate searching and browsing the web
   2503 within Emacs.
   2504 
   2505 *** Library-genesis
   2506 
   2507 My custom package for searching library genesis.  I bind =C-z l= to a
   2508 search.
   2509 
   2510 Located [[file:lisp/library-genesis/library-genesis.el][here]].
   2511 
   2512 #+begin_src emacs-lisp
   2513   (use-package library-genesis
   2514     :load-path "lisp/library-genesis"
   2515     :bind
   2516     ("C-z l" . library-genesis-search))
   2517 #+end_src
   2518 
   2519 *** Reddit-browse
   2520 
   2521 This is a very minimal package to ease the use of reddit within eww.
   2522 It uses the old reddit mobile site, which works well with eww.
   2523 
   2524 Located [[file:lisp/reddit-browse/reddit-browse.el][here]].
   2525 
   2526 #+begin_src emacs-lisp
   2527   (use-package reddit-browse
   2528     :load-path "lisp/reddit-browse"
   2529     :custom
   2530     (reddit-subreddit-list '("emacs" "lisp" "lispmemes"
   2531                              "vxjunkies" "linux" "nethack"
   2532                              "cello" "throwers"))
   2533     :bind
   2534     ("C-z r" . reddit-goto-subreddit))
   2535 #+end_src
   2536 
   2537 ** Toggle touchpad
   2538 
   2539 A simple package I wrote to toggle the touchpad/trackpoint on my
   2540 ThinkPad.
   2541 
   2542 Located [[file:lisp/toggle-touchpad/toggle-touchpad.el][here]].
   2543 
   2544 #+begin_src emacs-lisp
   2545   (use-package toggle-touchpad
   2546     :load-path "lisp/toggle-touchpad"
   2547     :bind
   2548     ("<XF86TouchpadToggle>" . toggle-touchpad)
   2549     ("C-z \\" . toggle-touchpad))
   2550 #+end_src
   2551 
   2552 ** Arch Linux settings
   2553 
   2554 This file just adds a few [[help:auto-mode-alist][auto-mode-alist]] entries for systemd and
   2555 pacman files.
   2556 
   2557 Located [[file:lisp/arch-linux-settings/arch-linux-settings.el][here]].
   2558 
   2559 #+begin_src emacs-lisp
   2560   (use-package arch-linux-settings
   2561     :load-path "lisp/arch-linux-settings")
   2562 #+end_src
   2563 
   2564 ** Custom EXWM config
   2565 
   2566 My custom settings for EXWM - not much different from the
   2567 [[help:exwm-config-default][exwm-config-default]], but doesn't get in my way as much.  It provides
   2568 the function [[help:custom-exwm-config][custom-exwm-config]] which is run when exwm starts.
   2569 
   2570 Note: this doesn't actually start EXWM, so this needs to be done in
   2571 your [[file:~/.xinitrc][xinitrc]].
   2572 
   2573 Located [[file:lisp/exwm/custom-exwm-config.el][here]].
   2574 
   2575 #+begin_src emacs-lisp
   2576   (use-package custom-exwm-config
   2577     :load-path "lisp/exwm"
   2578     :commands custom-exwm-config
   2579     :hook
   2580     (exwm-init . custom-exwm-config))
   2581 #+end_src
   2582 
   2583 ** Miscellaneous functions
   2584 
   2585 A number of functions that don't necessarily have a proper home.  Bind
   2586 =C-c p= to open the pdf output of a typesetting program, and =C-h M-a= to
   2587 run the external "apropos" command (not to be confused with Elisp
   2588 apropos).
   2589 
   2590 Located [[file:lisp/my-misc-defuns/my-misc-defuns.el][here]].
   2591 
   2592 #+begin_src emacs-lisp
   2593   (use-package my-misc-defuns
   2594     :load-path "lisp/my-misc-defuns"
   2595     :bind
   2596     ("C-M-\\" . indent-region-or-defun-please)
   2597     ("C-h M-a" . system-apropos)
   2598     ("C-c p" . open-pdf-of-current-file)
   2599     ("C-z C-p" . jamzattack-pastebin))
   2600 #+end_src
   2601 
   2602 ** Custom Helm bookmarks
   2603 
   2604 This package defines a macro to create new bookmark sources, and adds
   2605 a few.
   2606 
   2607 Located [[file:lisp/helm/custom-helm-bookmark.el][here]].
   2608 
   2609 #+begin_src emacs-lisp
   2610   (use-package custom-helm-bookmark
   2611     :load-path "lisp/helm"
   2612     :after helm
   2613     :custom
   2614     (helm-bookmark-default-filtered-sources
   2615      '(helm-source-bookmark-university
   2616        helm-source-bookmark-gnus
   2617        helm-source-bookmark-config
   2618        helm-source-bookmark-org-misc
   2619        helm-source-bookmark-elisp
   2620        helm-source-bookmark-downloads
   2621        helm-source-bookmark-magit
   2622        helm-source-bookmark-dired
   2623        helm-source-bookmark-info
   2624        helm-source-bookmark-man
   2625        helm-source-bookmark-other
   2626        helm-source-bookmark-set)))
   2627 #+end_src
   2628 
   2629 ** Minibuffer hacks
   2630 
   2631 A very tiny package, just defining two functions to make the
   2632 minibuffer a bit nicer.
   2633 
   2634 [[help:increase-minibuffer-size-please][increase-minibuffer-size-please]] increases the font size a bit, I add
   2635 it to [[help:minibuffer-setup-hook][minibuffer-setup-hook]].
   2636 
   2637 [[help:exit-minibuffer-other-window][exit-minibuffer-other-window]] exits the minibuffer in another window.
   2638 This requires Emacs 28, as it uses [[help:other-window-prefix][other-window-prefix]].  I bind it to
   2639 =M-RET=.  e.g. =M-x eww emacs M-RET= will open [[help:eww][eww]] in another window.
   2640 
   2641 #+begin_src emacs-lisp
   2642   (use-package minibuffer-hacks
   2643     :load-path "lisp/minibuffer-hacks"
   2644     :bind
   2645     (:map minibuffer-local-map
   2646           ("M-RET" . exit-minibuffer-other-window))
   2647     :hook
   2648     (minibuffer-setup . increase-minibuffer-size-please))
   2649 #+end_src
   2650 
   2651 ** Custom bitmaps
   2652 
   2653 The default fringe bitmaps aren't that pretty, so I define a few of my
   2654 own.
   2655 
   2656 Currently, it's only:
   2657 - left/right arrows (truncated lines)
   2658 - left/right curly arrows (wrapped lines)
   2659 
   2660 #+begin_src emacs-lisp
   2661   (use-package my-bitmaps
   2662     :load-path "lisp/bitmaps"
   2663     :hook
   2664     (server-after-make-frame . my-bitmaps-enable)
   2665     (window-setup . my-bitmaps-enable))
   2666 #+end_src
   2667 
   2668 * Third party packages
   2669 
   2670 This is where the packages installed with [[https://github.com/raxod502/straight.el][straight.el]] are located.
   2671 All of these use the =:straight= keyword, so that they are downloaded if
   2672 they aren't already.
   2673 
   2674 ** epkg
   2675 
   2676 Since I don't use the built-in =package= library, [[https://emacsmirror.net/][epkg]] is a nice
   2677 replacement for the UI (paired with [[*Straight][straight]] for installing).  It
   2678 provides functions to describe, list, search by author.
   2679 
   2680 One of the biggest advantages is that is shows way more information in
   2681 the *describe* buffer, including:
   2682 - dependencies
   2683 - reverse dependencies
   2684 - downloads
   2685 - github stars
   2686 - when last updated
   2687 
   2688 It also comes with an [[info:epkg#Top][info manual]], yippee!
   2689 
   2690 #+begin_src emacs-lisp
   2691   (use-package epkg
   2692     :straight t
   2693     :config
   2694     <<epkg-straight>>
   2695     :bind
   2696     ([remap describe-package] . epkg-describe-package)
   2697     ([remap finder-by-keyword] . epkg-list-matching-packages))
   2698 #+end_src
   2699 
   2700 *** "Install with straight" button
   2701 
   2702 Advise [[help:epkg-describe-package][epkg-describe-package]] to add a button for installation with
   2703 straight.
   2704 
   2705 #+name: epkg-straight
   2706 #+begin_src emacs-lisp :tangle no
   2707   (defun add-straight-button-to-epkg-describe (pkg &rest _ignored)
   2708     (when (member pkg (straight-recipes-list))
   2709       (let ((inhibit-read-only t))
   2710         (with-current-buffer (help-buffer)
   2711           (goto-char (point-min))
   2712           (forward-line 1)
   2713           (insert "\n")
   2714           (insert-button (format "Install %s with straight"
   2715                                  (propertize pkg 'face '(:inherit bold)))
   2716                          'action `(lambda (&rest _ignored)
   2717                                     (straight-use-package ',(intern pkg))))
   2718           (insert "\n")))))
   2719 
   2720   (advice-add 'epkg-describe-package :after #'add-straight-button-to-epkg-describe)
   2721 #+end_src
   2722 
   2723 ** HELM
   2724 
   2725 Rebind a few keys in order to make use of Helm's features.  Stuff like
   2726 [[help:find-file][find-file]] and [[help:switch-to-buffer][switch-to-buffer]].  Also remap =C-x k= to [[help:kill-this-buffer][kill-this-buffer]],
   2727 because I use [[help:helm-mini][helm-mini]] to kill other buffers.
   2728 
   2729 I also bind =M-C-y= to [[help:helm-show-kill-ring][helm-show-kill-ring]].  I tried to use this to
   2730 replace [[help:yank-pop][yank-pop]] but the latter is too engrained in my fingers.
   2731 
   2732 #+begin_src emacs-lisp
   2733   (use-package helm
   2734     :straight t
   2735     :custom
   2736     (helm-completion-style 'emacs)
   2737     (helm-describe-variable-function 'helpful-variable)
   2738     (helm-describe-function-function 'helpful-callable)
   2739     (helm-buffer-max-length 24)
   2740     (helm-split-window-preferred-function
   2741      #'helm-split-window-please)
   2742     (helm-ff-keep-cached-candidates nil)
   2743     (helm-external-programs-associations
   2744      '(("midi" . "timidity")
   2745        ("png" . "sxiv")
   2746        ("jpg" . "sxiv")
   2747        ("gif" . "mpv -L")
   2748        ("mp4" . "mpv")
   2749        ("mkv" . "mpv")
   2750        ("avi" . "mpv")
   2751        ("webm" . "mpv")
   2752        ("ps" . "zathura")
   2753        ("pdf" . "zathura")))
   2754     (helm-ff-cache-mode-lighter-sleep "")
   2755     (helm-ff-cache-mode-lighter-updating "")
   2756     :init
   2757     <<kill-this-buffer-please>>
   2758     :config
   2759     <<helm-split-window-please>>
   2760     <<un-helmify>>
   2761     (require 'helm-config)
   2762     (delight '((helm-mode "")))
   2763     (helm-mode t)
   2764     :bind
   2765     ([remap execute-extended-command] . helm-M-x)
   2766     ("<menu><menu>" . helm-M-x)
   2767     ("M-o" . helm-occur)
   2768     ("s-b" . helm-mini)
   2769     ([remap switch-to-buffer] . helm-mini)
   2770     ("C-x k" . kill-this-buffer-please)
   2771     ([remap find-file] . helm-find-files)
   2772     ([remap bookmark-jump] . helm-filtered-bookmarks)
   2773     ("M-C-y" . helm-show-kill-ring)
   2774     (:map helm-map
   2775           ("C-x C-t" . helm-toggle-resplit-and-swap-windows)
   2776           ("C-t" . transpose-chars)
   2777           ("C-h c" . describe-key-briefly)))
   2778 #+end_src
   2779 
   2780 *** un-helmifying some commands
   2781 
   2782 Helm provides the variable [[help:helm-completing-read-handlers-alist][helm-completing-read-handlers-alist]] to
   2783 determine which commands use helm for completing-read.
   2784 
   2785 I disable helm for [[info:emacs#Highlight Interactively][hi-lock]] functions, as they read a face name which
   2786 is pretty slow, and [[help:insert-char][insert-char]].
   2787 
   2788 #+name: un-helmify
   2789 #+begin_src emacs-lisp :tangle no
   2790   (with-eval-after-load 'helm-mode
   2791     (dolist (f '(highlight-symbol-at-point
   2792                  highlight-regexp
   2793                  highlight-lines-matching-regexp
   2794                  highlight-phrase
   2795                  insert-char))
   2796       (add-to-list 'helm-completing-read-handlers-alist
   2797                    (list f))))
   2798 #+end_src
   2799 
   2800 *** Functions
   2801 
   2802 **** Kill buffer
   2803 
   2804 I rebind =C-x k= to kill the current buffer, because [[help:helm-mini][helm-mini]] is so
   2805 useful.
   2806 
   2807 #+name: kill-this-buffer-please
   2808 #+begin_src emacs-lisp :tangle no
   2809   (defun kill-this-buffer-please ()
   2810     "Actually kill this buffer, unlike `kill-this-buffer' which
   2811   sometimes doesn't work."
   2812     (interactive)
   2813     (kill-buffer (current-buffer)))
   2814 #+end_src
   2815 
   2816 **** Split window
   2817 
   2818 The way Helm splits windows can get in the way a bit.  This more
   2819 predictable function selects the largest non-exwm window.
   2820 
   2821 #+name: helm-split-window-please
   2822 #+begin_src emacs-lisp :tangle no
   2823   (defun helm-split-window-please (window)
   2824     "If the frame only has one window, split it.  Otherwise, select
   2825   the largest non-exwm window."
   2826     (if (one-window-p t)
   2827         (split-window (selected-window) nil
   2828                       (if (> (window-pixel-width) (window-pixel-height))
   2829                           'right
   2830                         'below))
   2831       (select-window
   2832        ;; Reworking of `get-largest-window', doesn't choose an exwm
   2833        ;; window.
   2834        (let ((best-size 0)
   2835              best-window size)
   2836          (dolist (window (window-list-1 nil 'nomini))
   2837            (when (and (not (window-dedicated-p window))
   2838                       (not (eq window (selected-window)))
   2839                       (not (equal
   2840                             (buffer-local-value
   2841                              'major-mode (window-buffer window))
   2842                             'exwm-mode)))
   2843              (setq size (* (window-pixel-height window)
   2844                            (window-pixel-width window)))
   2845              (when (> size best-size)
   2846                (setq best-size size)
   2847                (setq best-window window))))
   2848          best-window))))
   2849 #+end_src
   2850 
   2851 *** Helm Imenu
   2852 
   2853 Helm's interface to [[help:imenu][imenu]].  It shows more information than imenu does,
   2854 and also provides a way to access an imenu for multiple buffers.
   2855 
   2856 #+begin_src emacs-lisp
   2857   (use-package helm-imenu
   2858     :straight helm
   2859     :defer t
   2860     :bind
   2861     ("C-c i" . helm-imenu)
   2862     ("C-c I" . helm-imenu-in-all-buffers))
   2863 #+end_src
   2864 
   2865 *** Helm man
   2866 
   2867 Remap =C-h C-m= to [[help:helm-man-woman][helm-man-woman]], a Helm interface for selecting
   2868 manpages.
   2869 
   2870 #+begin_src emacs-lisp
   2871   (use-package helm-man
   2872     :defer t
   2873     :straight helm
   2874     :custom
   2875     (man-width 80)
   2876     :bind
   2877     (:map help-map
   2878           ("C-m" . helm-man-woman)))
   2879 #+end_src
   2880 
   2881 *** Helm system packages
   2882 
   2883 Provides an abstraction layer for viewing and installing system
   2884 packages.
   2885 
   2886 #+begin_src emacs-lisp
   2887   (use-package helm-system-packages
   2888     :straight t
   2889     :bind
   2890     (:map help-map
   2891           ("C-p" . helm-system-packages)))
   2892 #+end_src
   2893 
   2894 *** Helm eww
   2895 
   2896 Some Helm functions for eww.  I replace all the default functions with
   2897 the Helm alternatives [[*EWW][here]].
   2898 
   2899 #+begin_src emacs-lisp
   2900   (use-package helm-eww
   2901     :straight t
   2902     :bind
   2903     ("C-x r e" . helm-eww-bookmarks))
   2904 #+end_src
   2905 
   2906 *** Helm org
   2907 
   2908 =C-c i= in org-mode runs the function [[help:helm-org-in-buffer-headings][helm-org-in-buffer-headings]].
   2909 
   2910 I'm not quite sure about the mechanics, but org-mode's imenu sometimes
   2911 works exactly like this (just a [[help:completing-read][completing-read]] of all headings), and
   2912 sometimes only shows the first couple.
   2913 
   2914 #+begin_src emacs-lisp
   2915   (use-package helm-org
   2916     :straight t
   2917     :after org
   2918     :bind
   2919     (:map org-mode-map
   2920           ("C-c i" . helm-org-in-buffer-headings)))
   2921 #+end_src
   2922 
   2923 *** Helm color
   2924 
   2925 The default action of [[help:helm-colors][helm-colors]] is [[help:customize-face][customize-face]].  I rarely find it
   2926 more convenient than [[help:describe-face][describe-face]], so I replace it.
   2927 
   2928 Note: this comes with [[*HELM][Helm]].
   2929 
   2930 #+begin_src emacs-lisp
   2931   (use-package helm-color
   2932     :defer t
   2933     :config
   2934     (setf (alist-get 'action helm-source-customize-face)
   2935           '(("Describe". (lambda (line)
   2936                            (describe-face
   2937                             (intern (car (split-string line))))))
   2938             ("Customize". (lambda (line)
   2939                             (customize-face
   2940                              (intern (car (split-string line))))))
   2941             ("Copy name" . (lambda (line)
   2942                              (kill-new (car (split-string line " " t))))))))
   2943 #+end_src
   2944 
   2945 *** Helm epa mode
   2946 
   2947 Since quite recently ([2020-08-15 Sat 07:22], commit =caf78b98=) helm
   2948 has included an interface for [[info:epa#Top][epa]].  I enable it, of course, not only
   2949 because Helm makes for a convenient completing-read, but because the
   2950 stock key/recipient selection _sucks_.
   2951 
   2952 #+begin_src emacs-lisp
   2953   (use-package helm-misc
   2954     :after helm epa
   2955     :config
   2956     (helm-epa-mode))
   2957 #+end_src
   2958 
   2959 *** Helm Files
   2960 
   2961 Helm binds =C-c d= to delete the selected file(s) when reading a file
   2962 name.  This interferes with my [[*Insert Date][own keybindings]], so I move it to =C-c
   2963 C-d=.
   2964 
   2965 #+begin_src emacs-lisp
   2966   (use-package helm-files
   2967     :defer t
   2968     :config
   2969     (define-key helm-find-files-map (kbd "C-c d") nil)
   2970     (define-key helm-find-files-map (kbd "C-c C-d") #'helm-ff-persistent-delete))
   2971 #+end_src
   2972 
   2973 *** Helm grep
   2974 
   2975 Appropriating a couple of keybindings from [[help:helm-find-files][helm-find-files]], having
   2976 some multi-file search functions easily accessible is quite handy.
   2977 
   2978 #+begin_src emacs-lisp
   2979   (use-package helm-grep
   2980     :bind
   2981     ("M-g a" . helm-do-grep-ag)
   2982     ("M-g g" . helm-grep-do-git-grep))
   2983 #+end_src
   2984 
   2985 ** Helpful
   2986 
   2987 Helpful gives a whole lot more information than =describe-*=.  I also
   2988 bind =C-h SPC= to [[help:helpful-at-point][helpful-at-point]], just to save a keypress here and
   2989 there.  The =:straight= recipe uses my fork, which doesn't depend on
   2990 =f.el=.  (I know it's minor, but I'd rather not load the extra library).
   2991 
   2992 #+begin_src emacs-lisp
   2993   (use-package helpful
   2994     :straight
   2995     (helpful :type git
   2996              :flavor melpa
   2997              :host gitlab
   2998              :repo "jamzattack/helpful"
   2999              :branch "no-f")
   3000     :init
   3001     <<helpful-overrides>>
   3002     :config
   3003     <<helpful-edit-source-temporarily>>
   3004     <<helpful-copy-to-kill-ring>>
   3005     :bind
   3006     (:map help-map
   3007           ("f" . helpful-callable)
   3008           ("v" . helpful-variable)
   3009           ("o" . helpful-symbol)
   3010           ("k" . helpful-key)
   3011           ("SPC" . helpful-at-point))
   3012     (:map helpful-mode-map
   3013           ("e" . helpful-edit-source-temporarily)
   3014           ("w" . helpful-copy-to-kill-ring)))
   3015 #+end_src
   3016 
   3017 *** Edit source
   3018 
   3019 A function that opens up a new buffer with the source shown in the
   3020 current =helpful= buffer.
   3021 
   3022 This now works with both Elisp and C source code.
   3023 
   3024 #+name: helpful-edit-source-temporarily
   3025 #+begin_src emacs-lisp :tangle no
   3026   (defun helpful-edit-source-temporarily ()
   3027     "Edit the source shown in the current helpful buffer.
   3028 
   3029   This pops open a buffer with only the symbol's source, rather
   3030   than taking you to its file.
   3031 
   3032   Works with both elisp and C source code."
   3033     (interactive)
   3034     (unless (derived-mode-p 'helpful-mode)
   3035       (user-error "Not in a helpful buffer"))
   3036     (save-excursion
   3037       (let* ((buffer
   3038               (get-buffer-create
   3039                (format "*%s (source)*"
   3040                        helpful--sym)))
   3041              (min (progn
   3042                     (goto-char (point-min))
   3043                     (or (re-search-forward "^Source Code$" nil t)
   3044                         (error "No source available"))
   3045                     (forward-line 1)
   3046                     (point)))
   3047              (max (progn
   3048                     (goto-char min)
   3049                     (end-of-defun)
   3050                     (point)))
   3051              (primitive-p
   3052               (helpful--primitive-p helpful--sym helpful--callable-p)))
   3053         (copy-to-buffer buffer
   3054                         min
   3055                         max)
   3056         (pop-to-buffer buffer)
   3057         (if primitive-p
   3058             (c-mode)
   3059           (emacs-lisp-mode)))))
   3060 #+end_src
   3061 
   3062 *** Save symbol to kill ring
   3063 
   3064 #+name: helpful-copy-to-kill-ring
   3065 #+begin_src emacs-lisp :tangle no
   3066   (defun helpful-copy-to-kill-ring (buffer)
   3067     "Copy the callable or variable of BUFFER to the kill ring.
   3068 
   3069   Called interactively, BUFFER is the current buffer or, with
   3070   prefix arg, read from the minibuffer."
   3071     (interactive (list
   3072                   (if current-prefix-arg
   3073                       (read-buffer "Copy symbol from buffer: "
   3074                                    (current-buffer)
   3075                                    t
   3076                                    (lambda (buffer)
   3077                                      (with-current-buffer buffer
   3078                                        (derived-mode-p 'helpful-mode))))
   3079                     (current-buffer))))
   3080     (with-current-buffer buffer
   3081       (unless (eq major-mode 'helpful-mode)
   3082         (user-error "%s is not a helpful buffer" (buffer-name buffer)))
   3083       (kill-new (symbol-name helpful--sym))
   3084       (message "\"%s\" saved to kill ring." helpful--sym)))
   3085 #+end_src
   3086 
   3087 *** Replacing some describe-* functions with helpful
   3088 
   3089 Here I redefine and advise some functions that use the builtin
   3090 =describe-*= functions, and make them use the helpful versions.
   3091 
   3092 Currently, this affects:
   3093 - [[help:org-link--open-help][org links]]
   3094 - [[help:erc-button-describe-symbol][erc buttons]]
   3095 - [[help:transient--describe-function][transient menus]] (magit)
   3096 
   3097 #+name: helpful-overrides
   3098 #+begin_src emacs-lisp :tangle no
   3099   (defun helpful-string (str)
   3100     "Describe the symbol named STR.
   3101   This uses the `helpful' library instead of `describe-*'.  If STR
   3102   doesn't name an existing symbol, call `apropos' on it."
   3103     (let ((symbol (intern-soft str)))
   3104       (if symbol
   3105           (helpful-symbol symbol)
   3106         (apropos str))))
   3107 
   3108   ;; Org links
   3109   (with-eval-after-load 'ol
   3110     (advice-add 'org-link--open-help
   3111                 :override #'helpful-string))
   3112 
   3113   ;; Erc buttons
   3114   (with-eval-after-load 'erc-button
   3115     (advice-add 'erc-button-describe-symbol
   3116                 :override #'helpful-string))
   3117 
   3118   ;; Transient help
   3119   (with-eval-after-load 'transient
   3120     (advice-add 'transient--describe-function
   3121                 :override #'helpful-callable))
   3122 #+end_src
   3123 
   3124 ** Major Modes
   3125 
   3126 *** Nov.el - epub in emacs
   3127 
   3128 Read epub files in Emacs.  I set this up as the default mode for
   3129 epubs, and set the default width to 80 columns.
   3130 
   3131 #+begin_src emacs-lisp
   3132   (use-package nov
   3133     :straight t
   3134     :custom
   3135     (nov-text-width 80)
   3136     (nov-variable-pitch nil)
   3137     :mode ("\\.epub\\'" . nov-mode)
   3138     :commands nov-bookmark-jump-handler
   3139     :config
   3140     <<nov-set-text-width>>
   3141     :bind
   3142     (:map nov-mode-map
   3143           ([remap set-fill-column] . nov-set-width)))
   3144 #+end_src
   3145 
   3146 **** Set text width in nov buffer
   3147 
   3148 #+begin_src emacs-lisp :tangle no
   3149   (defun nov-set-width (width)
   3150     "Set the nov rendering width to WIDTH.
   3151 
   3152   If prefix arg is a number, use it.  Otherwise, read number from
   3153   the minibuffer."
   3154     (interactive (list
   3155                   (if (numberp current-prefix-arg)
   3156                       current-prefix-arg
   3157                     (read-number "Set width: "
   3158                                  (- (window-width) 5)))))
   3159     (when (derived-mode-p 'nov-mode)
   3160       (setq nov-text-width width)
   3161       (nov-render-document)))
   3162 #+end_src
   3163 
   3164 *** PDF-tools
   3165 
   3166 Majorly increases performance when viewing pdfs within Emacs, and
   3167 provides some note-taking facilities.
   3168 
   3169 #+begin_src emacs-lisp
   3170   (use-package pdf-tools
   3171     :straight t
   3172     :magic ("%PDF" . pdf-view-mode)
   3173     :custom
   3174     (pdf-links-browse-uri-function #'pdf-links-open-please)
   3175     :hook
   3176     (pdf-view-mode . auto-revert-mode)
   3177     :config
   3178     <<pdf-links-open-please>>
   3179     (pdf-tools-install))
   3180 #+end_src
   3181 
   3182 **** Custom link handler
   3183 
   3184 Awkward hacky workaround to get LilyPond's links to open properly.
   3185 
   3186 #+name: pdf-links-open-please
   3187 #+begin_src emacs-lisp :tangle no
   3188   (defun pdf-links-open-please (uri)
   3189     "Open \"textedit://\" links via `find-file', and jump to the
   3190   right point.  I use this because lilypond output contains such
   3191   links."
   3192     (if (string-match "textedit://" uri)
   3193         (let* ((path
   3194                 ;; get rid of textedit://
   3195                 (replace-regexp-in-string
   3196                  "\\`textedit://" "" uri))
   3197                (split
   3198                 (split-string path ":"))
   3199                (file
   3200                 (url-unhex-string
   3201                  (apply #'concat (butlast split 3))))
   3202                (extras
   3203                 (reverse (cdr split)))
   3204                (line
   3205                 (string-to-number (caddr extras)))
   3206                (column
   3207                 (string-to-number (car extras)))
   3208                (buffer (find-file-noselect file)))
   3209           (pop-to-buffer buffer)
   3210           (goto-char (point-min))
   3211           (forward-line (1- line))
   3212           (move-to-column column))
   3213       (pdf-links-browse-uri-default uri)))
   3214 #+end_src
   3215 
   3216 *** LilyPond-mode
   3217 
   3218 I mirror the lilypond-mode source on my git server, in case I need to
   3219 use it on a system where lilypond isn't installed.
   3220 
   3221 Located [[https://git.jamzattack.xyz/lilypond-mode][here]].
   3222 
   3223 #+begin_src emacs-lisp
   3224   (use-package lilypond-mode
   3225     :straight
   3226     (lilypond-mode :type git
   3227                    :repo "git@jamzattack.xyz:lilypond-mode.git")
   3228     :delight
   3229     (LilyPond-mode "ly" :major)
   3230     :init
   3231     (defalias 'lilypond-mode 'LilyPond-mode)
   3232     <<custom-lilypond-setup>>
   3233     :config
   3234     <<lilypond-insert-repeat>>
   3235     :custom
   3236     (LilyPond-midi-command "timidity -Oj")
   3237     (LilyPond-all-midi-command "timidity -Oj -ia")
   3238     :mode ("\\.ly\\'" . LilyPond-mode)
   3239     :hook (LilyPond-mode . custom-lilypond-setup))
   3240 #+end_src
   3241 
   3242 **** Custom lilypond setup
   3243 
   3244 A few miscellaneous things to add to [[help:LilyPond-mode-hook][LilyPond-mode-hook]].
   3245 
   3246 - Loads local variables ([[help:LilyPond-mode][LilyPond-mode]] missed this somehow)
   3247 - Set [[help:compile-command][compile-command]] if there's no Makefile or local variable
   3248 - Sets the [[help:comment-column][comment-column]] to 0
   3249 - Sets up imenu regexps to show:
   3250   - Bar numbers
   3251   - Page numbers
   3252   - Movement numbers
   3253   - TODOs
   3254   - Definitions
   3255 - Turns on [[help:display-fill-column-indicator-mode][display-fill-column-indicator-mode]] if it's available
   3256   (currently on master branch)
   3257 
   3258 #+name: custom-lilypond-setup
   3259 #+begin_src emacs-lisp :tangle no
   3260   (defun custom-lilypond-setup ()
   3261     "Sets a bunch of things up for `LilyPond-mode'."
   3262     (interactive)
   3263     (hack-local-variables)
   3264     (unless (or (file-exists-p "Makefile")
   3265                 (local-variable-p 'compile-command (current-buffer)))
   3266       (setq-local compile-command
   3267                   (format "lilypond \"%s\"" buffer-file-name)))
   3268     (setq-local comment-column 0)
   3269     (setq-local imenu-generic-expression
   3270                 '(("Bar" "^% bar \\([0-9]+\\)" 1)
   3271                   ("Page" "^% PAGE \\([A-Z0-9]+\\)" 1)
   3272                   ("Movement" "^% \\([Mm]o?ve?m?e?n?t\\) \\([A-Za-z0-9]+\\)" 2)
   3273                   ("TODO" "^%?.*TODO[: ]?*\\(.*\\)" 1)
   3274                   ("Variables" "^\\([a-zA-Z]+\\) *=" 1)))
   3275     (when (fboundp 'display-fill-column-indicator-mode)
   3276       (setq fill-column 80)
   3277       (display-fill-column-indicator-mode)))
   3278 #+end_src
   3279 
   3280 **** Fancy repeat command
   3281 
   3282 Nice little command to insert a repeat.  Wraps the region in =\repeat=
   3283 and prompts for the type and number.
   3284 
   3285 #+name: lilypond-insert-repeat
   3286 #+begin_src emacs-lisp :tangle no
   3287   (defun lilypond-insert-repeat (&optional beg end)
   3288     "Insert a repeat around BEG and END.
   3289   Interactively, this is the selected region.  Prompt for the
   3290   type (volta or unfold) and number."
   3291     (interactive "*r")
   3292     (setq beg (copy-marker (or beg (point)))
   3293           end (copy-marker (or end (1+ (point)))))
   3294     (save-excursion
   3295       (goto-char beg)
   3296       (insert (format "\\repeat %s %s {\n"
   3297                       (completing-read "Repeat type: " '("volta" "unfold"))
   3298                       (read-number "Repeat number: " 2)))
   3299       (goto-char end)
   3300       (insert "}\n")
   3301       (indent-region beg (1+ end))))
   3302 
   3303   (define-key LilyPond-mode-map (kbd "C-c C-r") #'lilypond-insert-repeat)
   3304 #+end_src
   3305 
   3306 *** Markdown
   3307 
   3308 A very featureful major mode for markdown files.  I only really use it
   3309 for looking at READMEs though, so I add [[help:view-mode][view-mode]] to the hook.
   3310 
   3311 #+begin_src emacs-lisp
   3312   (use-package markdown-mode
   3313     :straight t
   3314     :mode "\\.md\\'"
   3315     :hook (markdown-mode . view-mode))
   3316 #+end_src
   3317 
   3318 *** GNU APL mode
   3319 
   3320 I've been trying to learn a bit of APL recently, and [[help:gnu-apl-mode][gnu-apl-mode]] is
   3321 an excellent way to get into it.  It tries to use the super modifier
   3322 to insert special characters, but I use it for my own functions so I
   3323 set the prefix to ". ".
   3324 
   3325 #+begin_src emacs-lisp
   3326   (use-package gnu-apl-mode
   3327     :straight t
   3328     :mode
   3329     "\\.apl'"
   3330     :custom
   3331     (gnu-apl-interactive-mode-map-prefix ". ")
   3332     (gnu-apl-mode-map-prefix ". "))
   3333 #+end_src
   3334 
   3335 ** Programming
   3336 
   3337 *** Geiser
   3338 
   3339 Interact with scheme in a powerful and emacsy way.  I set the scheme
   3340 program name (which isn't actually a part of geiser) to whichever
   3341 scheme is installed, in order of preference.
   3342 
   3343 #+begin_src emacs-lisp
   3344   (use-package geiser
   3345     :straight t
   3346     :defer t
   3347     :delight
   3348     (scheme-mode "scm" :major)
   3349     (geiser-repl-mode "SCM>" :major)
   3350     (geiser-autodoc-mode)
   3351     :hook
   3352     (geiser-repl-mode . paredit-mode)
   3353     :custom
   3354     (scheme-program-name
   3355      (or (executable-find "guile3.0")
   3356          (executable-find "guile")
   3357          (executable-find "chez")
   3358          (executable-find "mit-scheme")
   3359          "scheme"))
   3360     (geiser-default-implementation 'guile)
   3361     (geiser-repl-history-filename "~/.cache/geiser/history"))
   3362 #+end_src
   3363 
   3364 *** SLIME
   3365 
   3366 Interact with Common Lisp in a powerful and emacsy way.  I set the
   3367 default Lisp program, add some fancier stuff such as a nicer REPl, and
   3368 move the history file out of =$HOME=.
   3369 
   3370 #+begin_src emacs-lisp
   3371   (use-package slime
   3372     :straight t
   3373     :delight
   3374     (lisp-mode "cl" :major)
   3375     (slime-repl-mode "CL>" :major)
   3376     (slime-mode)
   3377     (slime-autodoc-mode)
   3378     :init
   3379     (autoload 'slime-switch-to-output-buffer "slime-repl")
   3380     (defun disable-slime-completion ()
   3381       (setq slime-completion-at-point-functions
   3382             '(slime-simple-completion-at-point)))
   3383     :hook (slime-connected . disable-slime-completion)
   3384     :custom
   3385     (inferior-lisp-program
   3386      (or (executable-find "sbcl")
   3387          (executable-find "ccl")
   3388          (executable-find "clisp")
   3389          "lisp"))
   3390     (slime-contribs '(slime-fancy))
   3391     (slime-repl-history-file "~/.cache/slime/history")
   3392     (common-lisp-hyperspec-root
   3393      (if (file-exists-p "/usr/share/doc/HyperSpec/")
   3394          "file:///usr/share/doc/HyperSpec/"
   3395        "http://clhs.lisp.se/"))
   3396     (slime-auto-start 'ask)
   3397     :bind
   3398     (:map slime-mode-map
   3399           ("C-c C-z" . slime-switch-to-output-buffer))
   3400     :config
   3401     (with-eval-after-load 'slime-repl
   3402       (bind-key "C-c C-z" #'quit-window slime-repl-mode-map)))
   3403 #+end_src
   3404 
   3405 *** Paredit
   3406 
   3407 Efficient and clever editing commands for working with s-expressions.
   3408 Only fully enabled in Lisp modes, but I also define a few useful keys
   3409 globally.
   3410 
   3411 - [ ] TODO replace all these hooks with [[help:lisp-data-mode-hook][lisp-data-mode]], added in 28.
   3412 
   3413 #+begin_src emacs-lisp
   3414   (use-package paredit
   3415     :straight t
   3416     :defer t
   3417     :delight
   3418     :bind
   3419     ("M-\"" . paredit-meta-doublequote)
   3420     ("M-(" . paredit-open-round)
   3421     ("C-(" . paredit-backward-slurp-sexp)
   3422     ("C-)" . paredit-forward-slurp-sexp)
   3423     ("C-{" . paredit-backward-barf-sexp)
   3424     ("C-}" . paredit-forward-barf-sexp)
   3425     (:map paredit-mode-map
   3426           ("M-R" . paredit-splice-sexp-killing-backward))
   3427     :hook
   3428     (emacs-lisp-mode . paredit-mode)
   3429     (lisp-interaction-mode . paredit-mode)
   3430     (ielm-mode . paredit-mode)
   3431     (eval-expression-minibuffer-setup . paredit-mode)
   3432     (lisp-mode . paredit-mode)
   3433     (slime-repl-mode . paredit-mode)
   3434     (scheme-mode . paredit-mode))
   3435 #+end_src
   3436 
   3437 *** Elf-mode
   3438 
   3439 Major mode for viewing ELF files (compiled binaries).  I don't use it
   3440 often, but it's nice to be able to see what a program does sometimes.
   3441 
   3442 #+begin_src emacs-lisp
   3443   (use-package elf-mode
   3444     :straight t
   3445     :magic ("\dELF" . elf-mode))
   3446 #+end_src
   3447 
   3448 *** Macrostep
   3449 
   3450 A library that I've been using for quite some time because it's a
   3451 dependency of [[*SLIME][slime]].  But I figure I should give it a keybinding --
   3452 =C-c M-e= to enter [[help:macrostep-mode][macrostep-mode]].
   3453 
   3454 #+begin_src emacs-lisp
   3455   (use-package macrostep
   3456     :straight t
   3457     :bind
   3458     (:map emacs-lisp-mode-map
   3459           ("C-c M-e" . macrostep-expand)))
   3460 #+end_src
   3461 
   3462 *** Regexp Expand
   3463 
   3464 A neat little package that shows the regexp at point in the [[help:rx][rx]] format
   3465 with a nice inline interface.  Kind of like [[https://github.com/joddie/macrostep][macrostep]].
   3466 
   3467 #+begin_src emacs-lisp
   3468   (use-package regexp-expand
   3469     :straight
   3470     (regexp-expand :type git
   3471                    :host github
   3472                    :repo "danielmartin/regexp-expand")
   3473     :bind
   3474     (:map emacs-lisp-mode-map
   3475           ("C-c M-r" . regexp-expand)))
   3476 #+end_src
   3477 
   3478 *** Selime                                                             :mine:
   3479 
   3480 This is my package to make Elisp evaluation and documentation lookup a
   3481 bit more like Slime.  It's often not necessary, but sometimes I find
   3482 myself using =C-c C-d C-f= to describe an Elisp function, etc.
   3483 
   3484 Hosted [[https://git.jamzattack.xyz/selime][here]].
   3485 
   3486 #+begin_src emacs-lisp
   3487   (use-package selime
   3488     :straight
   3489     (selime :type git
   3490             :flavor melpa
   3491             :repo "git@jamzattack.xyz:selime.git")
   3492     :hook (emacs-lisp-mode . selime-mode))
   3493 #+end_src
   3494 
   3495 *** Lex-hl                                                             :mine:
   3496 
   3497 My little package to highlight lexically bound variables.  It provides
   3498 a minor mode to add keybindings, but I bind them in [[help:emacs-lisp-mode-map][elisp mode map]]
   3499 instead of using a [[help:emacs-lisp-mode-hook][hook]], so that loading is deferred.
   3500 
   3501 Hosted [[https://git.jamzattack.xyz/lex-hl][here]].
   3502 
   3503 #+begin_src emacs-lisp
   3504   (use-package lex-hl
   3505     :straight
   3506     (lex-hl :type git
   3507             :flavor melpa
   3508             :repo "git@jamzattack.xyz:lex-hl.git")
   3509     :bind
   3510     (:map emacs-lisp-mode-map
   3511           ("C-c `" . lex-hl-unhighlight)
   3512           ("C-c '" . lex-hl-top-level)
   3513           ("C-c ," . lex-hl-prompt)
   3514           ("C-c ." . lex-hl-nearest)))
   3515 #+end_src
   3516 
   3517 *** LilyPond auto-insert                                               :mine:
   3518 
   3519 My own package to handle auto-insertions for LilyPond-mode.  I add it
   3520 to [[help:LilyPond-mode-hook][LilyPond-mode-hook]].
   3521 
   3522 Hosted [[https://git.jamzattack.xyz/lilypond-auto-insert][here]].
   3523 
   3524 #+begin_src emacs-lisp
   3525   (use-package lilypond-auto-insert
   3526     :straight
   3527     (lilypond-auto-insert :type git
   3528                           :flavor melpa
   3529                           :repo "git@jamzattack.xyz:lilypond-auto-insert.git")
   3530     :after lilypond-mode
   3531     :custom
   3532     (lilypond-auto-insert-language "english")
   3533     :bind
   3534     (:map LilyPond-mode-map
   3535           ("C-c a" . lilypond-auto-insert)))
   3536 #+end_src
   3537 
   3538 *** Lua Mode
   3539 
   3540 I don't know much lua, but when I downloaded some lua scripts for mpv
   3541 I was dismayed that a lua-mode isn't included with lua itself.
   3542 Anyway, I just want it for font-lock, so it only needs an entry in
   3543 [[help:auto-mode-alist][auto-mode-alist]] with use-package's =:mode= argument.
   3544 
   3545 #+begin_src emacs-lisp
   3546   (use-package lua-mode
   3547     :straight t
   3548     :mode ("\\.lua\\'" . lua-mode))
   3549 #+end_src
   3550 
   3551 ** Extra org packages
   3552 
   3553 *** Html export
   3554 
   3555 Export to html.
   3556 
   3557 #+begin_src emacs-lisp
   3558   (use-package htmlize
   3559     :straight t
   3560     :defer t)
   3561 #+end_src
   3562 
   3563 *** Org web tools
   3564 
   3565 This package parses a web page and transforms it into beautiful
   3566 org-mode.  I use it in my package [[*Plumb][plumb]].
   3567 
   3568 #+begin_src emacs-lisp
   3569   (use-package org-web-tools
   3570     :straight t
   3571     :defer t)
   3572 #+end_src
   3573 
   3574 *** Org Elisp index                                                    :mine:
   3575 
   3576 My package to insert a function/variable index from an elisp file into
   3577 an org buffer.  See its homepage for a demo.
   3578 
   3579 Hosted [[https://git.jamzattack.xyz/org-el-index][here]].
   3580 
   3581 #+begin_src emacs-lisp
   3582   (use-package org-el-index
   3583     :straight
   3584     (org-el-index :type git
   3585                   :repo "git@jamzattack.xyz:org-el-index.git")
   3586     :after org
   3587     :bind
   3588     (:map org-mode-map
   3589           ("C-c M-i" . org-el-index-file-small)))
   3590 #+end_src
   3591 
   3592 ** EXWM - Emacs X Window Manager
   3593 
   3594 Manipulate X windows as Emacs buffers.  As mentioned [[*Custom EXWM config][earlier]], you need
   3595 to enable exwm (via [[help:exwm-init][exwm-init]]) when creating the Emacs frame.
   3596 
   3597 #+begin_src emacs-lisp
   3598   (use-package exwm
   3599     :straight
   3600     (exwm :type git
   3601           :host github
   3602           :repo "ch11ng/exwm")
   3603     :defer t)
   3604 #+end_src
   3605 
   3606 *** Desktop-environment (useful with EXWM)
   3607 
   3608 This package sets up volume keys, brightness keys, and a screen
   3609 locker.  I like i3lock, and want it to use my theme's background
   3610 colour.
   3611 
   3612 #+begin_src emacs-lisp
   3613   (use-package desktop-environment
   3614     :straight t
   3615     :delight
   3616     :hook
   3617     (exwm-init . desktop-environment-mode)
   3618     :config
   3619     <<custom-screenlock-command>>
   3620     (defadvice desktop-environment-lock-screen
   3621         (before change-bg-color activate)
   3622       (custom-screenlock-command))
   3623     (desktop-environment-mode))
   3624 #+end_src
   3625 
   3626 **** Change screenlock command based on theme colour
   3627 
   3628 #+name: custom-screenlock-command
   3629 #+begin_src emacs-lisp :tangle no
   3630   (defun custom-screenlock-command ()
   3631     "Change the value of `desktop-environment-screenlock-command'
   3632   to run i3lock with the background colour of the current theme."
   3633     (let ((color (face-attribute 'default :background)))
   3634       (setq desktop-environment-screenlock-command
   3635             (format "i3lock -c '%s' -n"
   3636                     (with-temp-buffer
   3637                       (insert (if
   3638                                   (= (length color) 7)
   3639                                   color
   3640                                 "#000000"))
   3641                       (beginning-of-line)
   3642                       (delete-char 1)
   3643                       (buffer-string))))))
   3644 #+end_src
   3645 
   3646 ** "Applications"
   3647 
   3648 *** Vterm
   3649 
   3650 A performant terminal emulator in Emacs.  Unfortunately, it still
   3651 doesn't play nice with complicated things such as NetHack.
   3652 
   3653 #+begin_src emacs-lisp
   3654   (use-package vterm
   3655     :straight t
   3656     :defer t
   3657     :config
   3658     <<eshell/vterm>>)
   3659 #+end_src
   3660 
   3661 **** Launch a vterm from eshell
   3662 
   3663 The function =eshell/vterm= starts a program in vterm from eshell.
   3664 
   3665 #+name: eshell/vterm
   3666 #+begin_src emacs-lisp :tangle no
   3667   (defun eshell/vterm (&rest args)
   3668     "Launch a program from eshell using vterm."
   3669     (let ((vterm-shell
   3670            (eshell-flatten-and-stringify args)))
   3671       (vterm)))
   3672 #+end_src
   3673 
   3674 *** Music
   3675 
   3676 The following packages all provide some interface to playing music.  I
   3677 create my own [[help:my-music-map][keymap]], and bind it to =s-m=, so that I have somewhere to
   3678 put all my music keybindings.
   3679 
   3680 #+begin_src emacs-lisp
   3681   (defvar my-music-map (make-sparse-keymap)
   3682     "My keymap for playing music.")
   3683 
   3684   (global-set-key (kbd "s-m") my-music-map)
   3685 #+end_src
   3686 
   3687 **** Libmpdee
   3688 
   3689 An mpd library.  I use it only for random/shuffle, because [[*MPDel][MPDel]]
   3690 doesn't support that somehow.
   3691 
   3692 #+begin_src emacs-lisp
   3693   (use-package libmpdee
   3694     :straight t
   3695     :when (executable-find "mpd")
   3696     :bind
   3697     (:map my-music-map
   3698           ("z" . mpd-toggle-random)))
   3699 #+end_src
   3700 
   3701 **** MPDel
   3702 
   3703 A small and flexible mpd client.  I bind a bunch of keys in [[help:my-music-map][my music
   3704 map]], loosely based on [[help:mpdel-core-map][mpdel-core-map]] because I used to just use that
   3705 directly.
   3706 
   3707 #+begin_src emacs-lisp
   3708   (use-package mpdel
   3709     :straight t
   3710     :when (executable-find "mpd")
   3711     :bind
   3712     (:map my-music-map
   3713           ;; Playing music
   3714           ("SPC" . libmpdel-playback-play-pause)
   3715           ("n" . libmpdel-playback-next)
   3716           ("p" . libmpdel-playback-previous)
   3717           ("f" . mpdel-song-normal-increment)
   3718           ("b" . mpdel-song-normal-decrement)
   3719           ;; Choosing music
   3720           ("l" . mpdel-playlist-open)
   3721           ("L" . mpdel-core-open-stored-playlists)
   3722           ("a" . mpdel-core-open-albums)
   3723           (":" . mpdel-browser-open)
   3724           ("?" . mpdel-song-open)
   3725           ;; Searching
   3726           ("s s" . mpdel-core-search-by-title)
   3727           ("s l" . mpdel-core-search-by-album)
   3728           ("s r" . mpdel-core-search-by-artist)
   3729           ;; Volume
   3730           ("+" . mpdel-core-volume-increase)
   3731           ("-" . mpdel-core-volume-decrease)))
   3732 #+end_src
   3733 
   3734 **** Eradio
   3735 
   3736 A very simple internet radio player.  It just prompts for a station
   3737 from [[help:eradio-channels][eradio-channels]] and streams it via a specified media player.
   3738 
   3739 I've started getting into electronic music, so I've added a few
   3740 synth/vaporwave/lofi channels, as well as the national news and
   3741 classical channels.
   3742 
   3743 #+begin_src emacs-lisp
   3744   (use-package eradio
   3745     :straight t
   3746     :config
   3747     (setq eradio-channels
   3748           '(("RNZ Concert" . "https://radionz-ice.streamguys.com/concert.mp3")
   3749             ("RNZ National" . "https://radionz-ice.streamguys.com/national.mp3")
   3750             ("Paekakariki FM" . "https://icecast.groundtruth.co.nz/paekakfm.mp3")
   3751             ("Melancholy Corner" . "https://melancholy.xyz:8443/mp3")
   3752             ("SG Radio" . "http://stream.laut.fm/synthesizergreatest")
   3753             ("Chilled Cow" . "https://youtube.com/watch?v=5qap5aO4i9A")
   3754             ("Darksynth" . "https://stream.laut.fm/darksynthradio"))
   3755           eradio-player '("mpv" "--no-video" "--speed=1" "--no-terminal"))
   3756     :bind
   3757     (:map my-music-map
   3758           ("r" . eradio-play)
   3759           ("DEL" . eradio-stop)))
   3760 #+end_src
   3761 
   3762 *** Transmission
   3763 
   3764 An Emacs front-end for the [[http://www.transmissionbt.com/][Transmission]] BitTorrent daemon.  In the [[*EWW][EWW]]
   3765 section, I bind the function [[help:transmission-add-url-at-point][transmission-add-url-at-point]] in
   3766 eww-mode.
   3767 
   3768 #+begin_src emacs-lisp
   3769   (use-package transmission
   3770     :straight t
   3771     :when (executable-find "transmission-daemon")
   3772     :defer t
   3773     :commands transmission-mode
   3774     :init
   3775     (defun transmission-add-url-at-point (url &optional directory)
   3776       "Adds torrent if point is on a magnet or torrent link.
   3777   With prefix arg, prompt for DIRECTORY in which to download."
   3778       (interactive (list (shr-url-at-point nil)
   3779                          (when current-prefix-arg
   3780                            (read-directory-name "Download in: " "~/Downloads/"))))
   3781       (transmission-add url directory))
   3782     (defun open-transmission-in-this-window ()
   3783       "Open the transmission buffer in the selected window.
   3784   This also sets the buffer's default directory to ~/Downloads."
   3785       (interactive)
   3786       (let ((buffer (get-buffer-create "*transmission*")))
   3787         (with-current-buffer buffer
   3788           (unless (derived-mode-p 'transmission-mode)
   3789             (transmission-mode)
   3790             (revert-buffer))
   3791           (setq default-directory "~/Downloads/"))
   3792         (pop-to-buffer-same-window buffer)))
   3793     :bind
   3794     ("C-z C-t" . open-transmission-in-this-window))
   3795 #+end_src
   3796 
   3797 *** Elpher
   3798 
   3799 Elpher is a gopher and gemini browser for Emacs.
   3800 
   3801 I add an entry in [[help:browse-url-handlers][browse-url-handlers]] so that gopher links are opened
   3802 in Elpher (this does not work from eww).  This requires creating a new
   3803 function which can accept the extra arguments.
   3804 
   3805 #+begin_src emacs-lisp
   3806   (use-package elpher
   3807     :straight t
   3808     :defer t
   3809     :commands elpher-go
   3810     :bind
   3811     (:map elpher-mode-map
   3812           <<elpher-keybindings>>
   3813           )
   3814     :init
   3815     (defun elpher-go-please (url &rest _ignore)
   3816       "Like `elpher-go', but allows extra arguments.
   3817   This is useful for `browse-url-handlers'"
   3818       (elpher-go url))
   3819     (with-eval-after-load 'browse-url
   3820       (add-to-list 'browse-url-handlers
   3821                    '("\\`\\(gopher\\|gemini\\)://" . elpher-go-please))))
   3822 #+end_src
   3823 
   3824 **** Elpher keybindings
   3825 
   3826 #+name: elpher-keybindings
   3827 #+begin_src emacs-lisp :tangle no
   3828   ("l" . elpher-back)
   3829   ("t" . elpher-back-to-start)
   3830   ("g" . elpher-reload)
   3831   ("G" . elpher-go)
   3832   ("w" . elpher-copy-link-url)
   3833   ("W" . elpher-copy-current-url)
   3834   ("v" . elpher-view-raw)
   3835 #+end_src
   3836 
   3837 **** Elpher org-link support                                           :mine:
   3838 
   3839 My own library to provide org-link support for elpher.
   3840 
   3841 Hosted [[https://git.jamzattack.xyz/ol-elpher][here]].
   3842 
   3843 #+begin_src emacs-lisp
   3844   (use-package ol-elpher
   3845     :straight
   3846     (ol-elpher :type git
   3847                :repo "git@jamzattack.xyz:ol-elpher.git")
   3848     :after ol)
   3849 #+end_src
   3850 
   3851 *** EBDB
   3852 
   3853 EBDB is a contact management system for Emacs.  BBDB is used more
   3854 often, but I chose EBDB because it has plenty of
   3855 [[info:ebdb#Top][documentation]].
   3856 
   3857 I set up =ebdb-gnus= and =ebdb-message= to activate when gnus and
   3858 message are loaded, because EBDB provides integration with these
   3859 libraries.  By default, it gets in the way a lot -- opening up buffers
   3860 of contacts whenever you read or write mail.
   3861 
   3862 - Setting [[help:ebdb-mua-pop-up][ebdb-mua-pop-up]] to nil means that
   3863   a buffer will only show in gnus when =; ;= is pressed.
   3864 - Setting
   3865   [[help:ebdb-completion-display-record][ebdb-completion-display-record]]
   3866   to nil stops the buffer from showing when using address completion
   3867   while composing mail.
   3868 
   3869 I also prefer to keep my contacts file encrypted, so I set
   3870 [[help:ebdb-sources][ebdb-sources]] accordingly.
   3871 
   3872 #+begin_src emacs-lisp
   3873   (use-package ebdb
   3874     :straight
   3875     (:host github :repo "girzel/ebdb")
   3876     :defer t
   3877     :custom
   3878     (ebdb-mua-pop-up nil)
   3879     (ebdb-sources
   3880      (expand-file-name
   3881       "ebdb.gpg" user-emacs-directory))
   3882     (ebdb-completion-display-record nil)
   3883     :init
   3884     (with-eval-after-load 'gnus
   3885       (require 'ebdb-gnus))
   3886     (with-eval-after-load 'message
   3887       (require 'ebdb-message)))
   3888 #+end_src
   3889 
   3890 *** Magit
   3891 
   3892 I've finally been convinced that Magit is the one true way to use git.
   3893 Currently, the config is quite simple -- open magit in the selected
   3894 window, and show 20 recent commits instead of 10.  I also make sure
   3895 that the [[help:magit-section-visibility-indicator][indicators]] show in the fringe, because it reverts to an
   3896 ellipsis when magit is loaded before the windowed frame (i.e. from
   3897 desktop as a daemon).
   3898 
   3899 #+begin_src emacs-lisp
   3900   (use-package magit
   3901     :straight t
   3902     :custom
   3903     (magit-display-buffer-function
   3904      #'magit-display-buffer-same-window-except-diff-v1)
   3905     (magit-log-section-commit-count 20)
   3906     :bind
   3907     ("C-x g" . magit-status)
   3908     :config
   3909     (with-eval-after-load 'exwm
   3910       (setq magit-section-visibility-indicator
   3911             '(magit-fringe-bitmap> . magit-fringe-bitmapv))))
   3912 #+end_src
   3913 
   3914 ** Appearance
   3915 
   3916 *** Rainbow-delimiters
   3917 
   3918 Minor mode that highlights parentheses well.
   3919 
   3920 #+begin_src emacs-lisp
   3921   (use-package rainbow-delimiters
   3922     :straight t
   3923     :defer t
   3924     :hook (prog-mode . rainbow-delimiters-mode))
   3925 #+end_src
   3926 
   3927 *** Dimmer (dim inactive buffers)
   3928 
   3929 Dims inactive buffers, so that you can more clearly see which window
   3930 you're in (sometimes the mode-line just doesn't cut it).
   3931 
   3932 Note: if this package ever stops working, try [[https://github.com/mina86/auto-dim-other-buffers.el/][auto-dim-other-buffers]]
   3933 before writing your own!
   3934 
   3935 #+begin_src emacs-lisp
   3936   (use-package dimmer
   3937     :straight t
   3938     :hook
   3939     (after-init . dimmer-mode)
   3940     :config
   3941     (setq dimmer-fraction 0.3)
   3942     (dimmer-configure-org)
   3943     (dimmer-configure-magit)
   3944     (dimmer-configure-helm)
   3945     (dimmer-configure-gnus))
   3946 #+end_src
   3947 
   3948 ** Quality of life
   3949 
   3950 *** Edwina
   3951 
   3952 Edwina provides some rudimentary [[https://dwm.suckless.org][dwm]] emulation.  The function
   3953 [[help:edwina-setup-dwm-keys][edwina-setup-dwm-keys]] binds similar keys to what dwm actually uses.
   3954 
   3955 #+begin_src emacs-lisp
   3956   (use-package edwina
   3957     :straight t
   3958     :defer t
   3959     :config
   3960     (edwina-setup-dwm-keys 'super))
   3961 #+end_src
   3962 
   3963 *** Plumb                                                              :mine:
   3964 
   3965 A way to open URLs the way I want.  I bind it to =C-z d=.  Some commands
   3966 from this package are bound in the [[*EWW][EWW]] section.
   3967 
   3968 Hosted [[https://git.jamzattack.xyz/plumb][here]].
   3969 
   3970 #+begin_src emacs-lisp
   3971   (use-package plumb
   3972     :straight
   3973     (plumb :type git
   3974            :flavor melpa
   3975            :repo "git@jamzattack.xyz:plumb.git")
   3976     :bind
   3977     ("C-z d" . plumb)
   3978     ("C-z C-d" . plumb))
   3979 #+end_src
   3980 
   3981 *** Narrow-x                                                           :mine:
   3982 
   3983 My own package providing a few extra [[info:emacs#Narrowing][narrowing]] commands.
   3984 
   3985 Hosted [[https://git.jamzattack.xyz/narrow-x][here]].
   3986 
   3987 #+begin_src emacs-lisp
   3988   (use-package narrow-x
   3989     :straight
   3990     (narrow-x :type git
   3991               :repo "git@jamzattack.xyz:narrow-x.git")
   3992     :bind
   3993     (:map narrow-map
   3994           ("h" . narrow-to-paragraph)
   3995           ("M-n" . narrow-to-next-paragraph)
   3996           ("M-p" . narrow-to-prev-paragraph)
   3997           ("DEL" . narrow-to-prev-page)
   3998           ("SPC" . narrow-to-next-page)
   3999           ("b" . narrow-to-prev-defun)
   4000           ("f" . narrow-to-next-defun)))
   4001 #+end_src
   4002 
   4003 *** Search-query                                                       :mine:
   4004 
   4005 My own search query package.  It simply provides a few functions so
   4006 that I don't need to use DuckDuckGo's bangs, and for websites that
   4007 don't have a bang.
   4008 
   4009 Unfortunately, [[https://invidio.us][invidio.us]] is due to [[https://omar.yt/posts/stepping-away-from-open-source][shut down]] on <2020-09-01 Tue>, so
   4010 I use an ever-changing mirror.
   4011 
   4012 Hosted [[https://git.jamzattack.xyz/search-query][here]].
   4013 
   4014 #+begin_src emacs-lisp
   4015   (use-package search-query
   4016     :straight
   4017     (search-query :type git
   4018                   :repo "git@jamzattack.xyz:search-query")
   4019     :custom
   4020     (search-query-tpb-mirror "piratebay.live")
   4021     (search-query-invidious-mirror "invidious.snopyta.org")
   4022     :bind
   4023     ("C-z a" . search-archwiki)
   4024     ("C-z t" . search-tpb)
   4025     ("C-z y" . search-invidious)		; just youtube really
   4026     ("C-z w" . search-wikipedia)
   4027     ("C-z C-w" . search-wiktionary)
   4028     ("C-z C-e" . search-etymonline))
   4029 #+end_src
   4030 
   4031 *** Insert Date                                                        :mine:
   4032 
   4033 My wee package to insert the date/time in a few different formats.  I
   4034 define =C-c d= as a prefix map.
   4035 
   4036 Hosted [[https://git.jamzattack.xyz/insert-date][here]].
   4037 
   4038 #+begin_src emacs-lisp
   4039   (use-package insert-date
   4040     :straight
   4041     (insert-date :type git
   4042                  :repo "git@jamzattack.xyz:insert-date.git")
   4043     :bind
   4044     (:prefix "C-c d" :prefix-map insert-date-map
   4045              ("v" . insert-date-version)
   4046              ("d" . insert-date-only-date)
   4047              ("t" . insert-date-only-time)
   4048              ("l" . insert-date-locale)
   4049              ("b" . insert-date-both)
   4050              ("i" . insert-date-iso8601)))
   4051 #+end_src
   4052 
   4053 *** Itch                                                               :mine:
   4054 
   4055 A simple package to switch to and create temporary buffers in a
   4056 particular major mode.
   4057 
   4058 Hosted [[https://git.jamzattack.xyz/itch][here]].
   4059 
   4060 #+begin_src emacs-lisp
   4061   (use-package itch
   4062     :straight (itch :type git
   4063                     :repo "git@jamzattack.xyz:itch")
   4064     :bind
   4065     ("s-a" . itch-fundamental)
   4066     ("s-A" . itch-switch-to-fundamental)
   4067     ("s-u" . itch-elisp)
   4068     ("s-U" . itch-switch-to-elisp)
   4069     ("s-e" . itch-eshell)
   4070     ("s-E" . itch-switch-to-eshell)
   4071     ("s-o" . itch-org)
   4072     ("s-O" . itch-switch-to-org))
   4073 #+end_src
   4074 
   4075 ** Dired
   4076 
   4077 A couple of packages that enhance dired.
   4078 
   4079 *** Dired-async
   4080 
   4081 Make dired run actions in the background.  This is in the package
   4082 =async=.
   4083 
   4084 #+begin_src emacs-lisp
   4085   (use-package dired-async
   4086     :straight async
   4087     :defer t
   4088     :config
   4089     (dired-async-mode))
   4090 #+end_src
   4091 
   4092 *** Dired-subtree
   4093 
   4094 Recursively list directories and cycle like org-mode.  Bind =TAB= to
   4095 show/hide a subtree, and disable the predefined faces.  Part of the
   4096 =dired-hacks= package.
   4097 
   4098 #+begin_src emacs-lisp
   4099   (use-package dired-subtree
   4100     :straight dired-hacks
   4101     :after dired
   4102     :demand t
   4103     :custom
   4104     (dired-subtree-use-backgrounds nil)
   4105     :bind
   4106     (:map dired-mode-map
   4107           ("TAB" . dired-subtree-cycle)))
   4108 #+end_src
   4109 
   4110 ** Eshell
   4111 
   4112 Eshell packages
   4113 
   4114 *** Eshell outline mode                                                :mine:
   4115 
   4116 My own package to integrate [[help:outline-minor-mode][outline-minor-mode]] with [[*Eshell][eshell]].
   4117 
   4118 Hosted [[https://git.jamzattack.xyz/eshell-outline][here]].
   4119 
   4120 #+begin_src emacs-lisp
   4121   (use-package eshell-outline
   4122     :straight
   4123     (eshell-outline :type git
   4124                     :flavor melpa
   4125                     :repo "git@jamzattack.xyz:eshell-outline.git")
   4126     :hook (eshell-mode . eshell-outline-mode))
   4127 #+end_src
   4128 
   4129 *** Fish completion
   4130 
   4131 Fish completion allows eshell and shell buffers to use [[https://fishshell.com/][fish]]
   4132 completion.  I only enable it when fish is installed.
   4133 
   4134 #+begin_src emacs-lisp
   4135   (use-package fish-completion
   4136     :straight t
   4137     :after eshell
   4138     :when (executable-find "fish")
   4139     :config
   4140     (global-fish-completion-mode))
   4141 #+end_src
   4142 
   4143 ** System-packages
   4144 
   4145 System-packages allows updating, installing, and removing programs
   4146 installed with your system's package manager.
   4147 
   4148 #+begin_src emacs-lisp
   4149   (use-package system-packages
   4150     :straight t
   4151     :defer t)
   4152 #+end_src
   4153 
   4154 ** Not really useful
   4155 
   4156 *** Lorem Ipsum
   4157 
   4158 A /Lorem Ipsum/ generator.
   4159 
   4160 #+begin_src emacs-lisp
   4161   (use-package lorem-ipsum
   4162     :straight t
   4163     :defer t)
   4164 #+end_src
   4165 
   4166 # Local Variables:
   4167 # indent-tabs-mode: nil
   4168 # End: