emacs.d

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

config.org (133936B)


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