emacs.d

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

config.org (133616B)


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