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