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: