diff options
author | Aryadev Chavali <aryadev@aryadevchavali.com> | 2024-10-20 20:31:11 +0100 |
---|---|---|
committer | Aryadev Chavali <aryadev@aryadevchavali.com> | 2024-10-20 20:37:11 +0100 |
commit | 4c64e8de0a4aeb3b2520280e356174eafe066c7a (patch) | |
tree | bd016a944b89ee0298b73d218dcca4b2c48bb6f4 | |
parent | f9a53852d93e346fbb14db870bdfd6d1c33e4ed6 (diff) | |
download | dotfiles-4c64e8de0a4aeb3b2520280e356174eafe066c7a.tar.gz dotfiles-4c64e8de0a4aeb3b2520280e356174eafe066c7a.tar.bz2 dotfiles-4c64e8de0a4aeb3b2520280e356174eafe066c7a.zip |
Move around some sections to make flow clearer
-rw-r--r-- | Emacs/.config/emacs/config.org | 3119 |
1 files changed, 1575 insertions, 1544 deletions
diff --git a/Emacs/.config/emacs/config.org b/Emacs/.config/emacs/config.org index d7e919a..068c753 100644 --- a/Emacs/.config/emacs/config.org +++ b/Emacs/.config/emacs/config.org @@ -261,10 +261,9 @@ never used before, 3 seems to be a reasonable default. ("ravenmaiden" 6) (_ 3)))) #+end_src -* Core packages -Here I configure packages, internal and external, which define either -critical infrastructure for the rest of the configuration or provide -key functionality disassociated from any specific environment. +* Essential packages +External and internal packages absolutely necessary for the rest of +this configuration. ** General - Bindings package Vanilla Emacs has the ~bind-key~ function (and the ~bind-key*~ macro) for this, but [[*Evil - Vim Emulation][Evil]] has it's own @@ -468,8 +467,9 @@ a package for porting Vim's modal editing style to Emacs, called evil However there are a lot of packages in Vim that provide greater functionality, for example tpope's "vim-surround". Emacs has these capabilities out of the box, but there are further packages which -integrate them into Evil. -*** Evil core +integrate them into Evil. These are setup later in [[*Evil +additions][Evil additions]]. + Setup the evil package, with some opinionated settings: + Switch ~evil-upcase~ and ~evil-downcase~ because I use ~evil-upcase~ more @@ -529,116 +529,6 @@ Setup the evil package, with some opinionated settings: "e" #'transpose-sexps "l" #'transpose-lines)) #+end_src -*** Evil surround -A port for vim-surround, providing the ability to mutate delimiters -around some text. - -#+begin_src emacs-lisp -(use-package evil-surround - :after evil - :straight t - :config - (global-evil-surround-mode)) -#+end_src -*** Evil commentary -A port of vim-commentary, providing generalised commenting of objects. - -#+begin_src emacs-lisp -(use-package evil-commentary - :after evil - :straight t - :config - (evil-commentary-mode)) -#+end_src -*** Evil multi cursor -Setup for multi cursors in Evil mode, which is a bit of very nice -functionality. Don't let evil-mc setup it's own keymap because it -uses 'gr' as its prefix, which I don't like. - -#+begin_src emacs-lisp -(use-package evil-mc - :after evil - :straight t - :init - (defvar evil-mc-key-map (make-sparse-keymap)) - :general - (nmap - :infix "gz" - "m" 'evil-mc-make-all-cursors - "u" 'evil-mc-undo-last-added-cursor - "q" 'evil-mc-undo-all-cursors - "s" 'evil-mc-pause-cursors - "r" 'evil-mc-resume-cursors - "f" 'evil-mc-make-and-goto-first-cursor - "l" 'evil-mc-make-and-goto-last-cursor - "h" 'evil-mc-make-cursor-here - "j" 'evil-mc-make-cursor-move-next-line - "k" 'evil-mc-make-cursor-move-prev-line - "N" 'evil-mc-skip-and-goto-next-cursor - "P" 'evil-mc-skip-and-goto-prev-cursor - "n" 'evil-mc-skip-and-goto-next-match - "p" 'evil-mc-skip-and-goto-prev-match - "I" 'evil-mc-make-cursor-in-visual-selection-beg - "A" 'evil-mc-make-cursor-in-visual-selection-end - "d" #'evil-mc-make-and-goto-next-match) - :config - (global-evil-mc-mode)) -#+end_src -*** Evil multi edit -Evil-ME provides a simpler parallel editing experience within the same -buffer. I use it in-tandem with Evil-MC, where I use Evil-ME for -textual changes and Evil-MC for more complex motions. - -#+begin_src emacs-lisp -(use-package evil-multiedit - :straight t - :defer t - :init - (setq evil-multiedit-scope 'visible) - :general - (:states '(normal visual) - :keymaps 'override - "M-e" #'evil-multiedit-match-and-next - "M-E" #'evil-multiedit-match-and-prev)) -#+end_src -*** Evil collection -Provides a community based set of keybindings for most modes in -Emacs. I don't necessarily like all my modes having these bindings -though, as I may disagree with some. So I use it in a mode to mode basis. - -#+begin_src emacs-lisp -(use-package evil-collection - :straight t - :after evil) -#+end_src -*** Evil number -Increment/decrement a number at point like Vim does, but use bindings -that don't conflict with Emacs default. - -#+begin_src emacs-lisp -(use-package evil-numbers - :straight t - :defer t - :general - (nmmap - "+" #'evil-numbers/inc-at-pt - "-" #'evil-numbers/dec-at-pt)) -#+end_src -*** Evil goggles -Make it easier to notice edits and changes using Vim motions! -#+begin_src emacs-lisp -(use-package evil-goggles - :straight t - :after evil - :init - (setq evil-goggles-duration 0.1 - evil-goggles-blocking-duration 0.1 - evil-goggles-async-duration 0.9 - evil-goggles-default-face 'pulsar-cyan) - :config - (evil-goggles-mode) - (evil-goggles-use-diff-faces)) -#+end_src ** Text Completion Emacs is a text based interface. All commands use textual input, operate on text and produce text as output. A classic command is @@ -804,20 +694,6 @@ From https://jmthornton.net/blog/p/consult-line-isearch-history, taken (advice-add #'consult-line :after #'consult-line-isearch-history)) #+end_src -*** Amx -Amx is a fork of Smex that works to enhance the -~execute-extended-command~ interface. It provides a lot of niceties -such as presenting the key bind when looking for a command. - -#+begin_src emacs-lisp -(use-package amx - :straight t - :defer 2 - :init - (setq amx-backend 'auto) - :config - (amx-mode)) -#+end_src *** Orderless Orderless sorting method for completion, probably one of the best things ever. @@ -842,8 +718,7 @@ just setup some evil binds for company. :defer t :straight t :hook - (prog-mode-hook . company-mode) - (eshell-mode-hook . company-mode) + ((prog-mode-hook eshell-mode-hook) . company-mode) :general (imap :keymaps 'company-mode-map @@ -852,212 +727,11 @@ just setup some evil binds for company. "M-j" #'company-select-next "M-k" #'company-select-previous)) #+end_src -** Pretty symbols -Prettify symbols mode allows users to declare "symbols" that replace -text within certain modes. It's eye candy in most cases, but can aid -comprehension for symbol heavy languages. - -This configures a ~use-package~ keyword which makes declaring pretty -symbols for language modes incredibly easy. Checkout my [[*Emacs -lisp][Emacs lisp]] configuration for an example. - -#+begin_src emacs-lisp -(use-package prog-mode - :demand t - :init - (setq prettify-symbols-unprettify-at-point t) - :config - (with-eval-after-load "use-package-core" - (add-to-list 'use-package-keywords ':pretty) - (defun use-package-normalize/:pretty (_name-symbol _keyword args) - args) - - (defun use-package-handler/:pretty (name _keyword args rest state) - (use-package-concat - (use-package-process-keywords name rest state) - (mapcar - #'(lambda (arg) - (let ((mode (car arg)) - (rest (cdr arg))) - `(add-hook - ',mode - #'(lambda nil - (setq prettify-symbols-alist ',rest) - (prettify-symbols-mode))))) - args))))) -#+end_src - -Here's a collection of keywords and possible associated symbols for -any prog language of choice. Mostly for reference and copying. - -#+begin_example -("null" . "Ø") -("list" . "ℓ") -("string" . "𝕊") -("char" . "ℂ") -("int" . "ℤ") -("float" . "ℝ") -("!" . "¬") -("for" . "Σ") -("return" . "≡") -("reduce" . "↓") -("map" . "→") -("some" . "∃") -("every" . "∃") -("lambda" . "λ") -("function" . "ƒ") -("<=" . "≤") -(">=" . "≥") -#+end_example -** Tabs -Tabs in vscode are just like buffers in Emacs but way slower and -harder to use. Tabs in Emacs are essentially window layouts, similar -to instances in Tmux. With this setup I can use tabs quite -effectively. - -#+begin_src emacs-lisp -(use-package tab-bar - :defer t - :hook (after-init-hook . tab-bar-mode) - :init - (setq tab-bar-close-button-show nil - tab-bar-format '(tab-bar-format-history - tab-bar-format-tabs tab-bar-separator) - tab-bar-show 1 - tab-bar-auto-width t - tab-bar-auto-width-max '((100) 20) - tab-bar-auto-width-min '((20) 2)) - :general - (tab-leader - "R" #'tab-rename - "c" #'tab-close - "d" #'tab-close - "f" #'tab-detach - "h" #'tab-move-to - "j" #'tab-next - "k" #'tab-previous - "l" #'tab-move - "n" #'tab-new - "r" #'tab-switch - "w" #'tab-window-detach)) -#+end_src -** Registers -#+begin_src emacs-lisp -(use-package register - :config - (defmacro +register/jump-to (reg) - `(proc (interactive) - (jump-to-register ,reg))) - :general - (nmmap - "m" #'point-to-register - "'" #'jump-to-register - "g1" (+register/jump-to "1") - "g2" (+register/jump-to "2") - "g3" (+register/jump-to "3") - "g4" (+register/jump-to "4") - "g5" (+register/jump-to "5") - "g6" (+register/jump-to "6") - "g7" (+register/jump-to "7") - "g8" (+register/jump-to "8") - "g9" (+register/jump-to "9"))) -#+end_src -** Auto typing -Snippets are a pretty nice way of automatically inserting code. Emacs -provides a few packages by default to do this, but there are great -packages to install as well. - -Abbrevs and skeletons make up a popular solution within Emacs default. -Abbrevs are for simple expressions wherein the only input is the key, -and the output is some Elisp function. They provide a lot of inbuilt -functionality and are quite useful. Skeletons, on the other hand, are -for higher level insertions with user influence. - -The popular external solution is Yasnippet. Yasnippet is a great -package for snippets, which I use heavily in programming and org-mode. -Here I setup the global mode for yasnippet and a collection of -snippets for ease of use. -*** Abbrevs -Just define a few abbrevs for various date-time operations. Also -define a macro that will assume a function for the expansion, helping -with abstracting a few things away. - -#+begin_src emacs-lisp -(use-package abbrev - :defer t - :hook - (prog-mode-hook . abbrev-mode) - (text-mode-hook . abbrev-mode) - :init - (defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs) - `(progn - ,@(mapcar #'(lambda (abbrev) - `(define-abbrev - ,abbrev-table - ,(car abbrev) - "" - (proc (insert ,(cadr abbrev))))) - abbrevs))) - (setq save-abbrevs nil) - :config - (+abbrev/define-abbrevs - global-abbrev-table - ("sdate" - (format-time-string "%Y-%m-%d" (current-time))) - ("stime" - (format-time-string "%H:%M:%S" (current-time))) - ("sday" - (format-time-string "%A" (current-time))) - ("smon" - (format-time-string "%B" (current-time))))) -#+end_src -*** Auto insert -Allows inserting text immediately upon creating a new buffer with a -given name, similar to template. Supports skeletons for inserting -text. To make it easier for later systems to define their own auto -inserts, I define a ~use-package~ keyword (~:auto-insert~) which -allows one to define an entry for ~auto-insert-alist~. - -#+begin_src emacs-lisp -(use-package autoinsert - :demand t - :hook (after-init-hook . auto-insert-mode) - :config - (with-eval-after-load "use-package-core" - (add-to-list 'use-package-keywords ':auto-insert) - (defun use-package-normalize/:auto-insert (_name-symbol _keyword args) - args) - (defun use-package-handler/:auto-insert (name _keyword args rest state) - (use-package-concat - (use-package-process-keywords name rest state) - (mapcar - #'(lambda (arg) - `(add-to-list - 'auto-insert-alist - ',arg)) - args))))) -#+end_src -*** Yasnippet -Look at the snippets [[file:../.config/yasnippet/snippets/][folder]] -for all snippets I've got. - -#+begin_src emacs-lisp -(use-package yasnippet - :straight t - :defer t - :hook - (prog-mode-hook . yas-minor-mode) - (text-mode-hook . yas-minor-mode) - :general - (insert-leader - "i" #'yas-insert-snippet) - :config - (yas-load-directory (no-littering-expand-etc-file-name - "yasnippet/snippets"))) -#+end_src -*** Hydra +** Hydra Hydra is a great package by =abo-abo= (yes the same guy who made ivy -and swiper). There are two use-package declarations here: one for +and swiper). Though not absolutely essential it provides an easy +interface option for keybindings which enhances their discoverability +and ease of use. There are two use-package declarations here: one for ~hydra~ itself, and the other for ~use-package-hydra~ which provides the keyword ~:hydra~ in use-package declarations. @@ -1068,157 +742,9 @@ the keyword ~:hydra~ in use-package declarations. (use-package use-package-hydra :straight t) #+end_src -** Helpful -Helpful provides a modern interface for some common help commands. I -replace ~describe-function~, ~describe-variable~ and ~describe-key~ by -their helpful counterparts. - -#+begin_src emacs-lisp -(use-package helpful - :straight t - :defer t - :commands (helpful-callable helpful-variable) - :general - ([remap describe-function] #'helpful-callable - [remap describe-variable] #'helpful-variable - [remap describe-key] #'helpful-key) - :display - ("\\*helpful.*" - (display-buffer-at-bottom) - (inhibit-duplicate-buffer . t) - (window-height . 0.25)) - :config - (evil-define-key 'normal helpful-mode-map "q" #'quit-window)) -#+end_src -** Avy and Ace -Avy is a package that provides "jump" functions. Given some input, -the current buffer is scanned and any matching candidates are given a -tag which the user can input to perform some action (usually moving -the cursor to that point). -*** Avy core -Setup avy with leader. As I use ~avy-goto-char-timer~ a lot, use the -~C-s~ bind which replaces isearch. Switch isearch to M-s in case I -need to use it. - -#+begin_src emacs-lisp -(use-package avy - :straight t - :defer t - :general - (nmmap - :keymaps 'override - "C-s" #'avy-goto-char-timer - "M-s" #'isearch-forward - "gp" #'avy-copy-region - "gP" #'avy-move-region - "gl" #'avy-goto-line - "gw" #'avy-goto-word-1)) -#+end_src -*** Ace window -Though evil provides a great many features in terms of window -management, ace window can provide some nicer chords for higher -management of windows (closing, switching, etc). - -#+begin_src emacs-lisp -(use-package ace-window - :straight t - :defer t - :custom - (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) - :general - (nmmap - [remap evil-window-next] #'ace-window)) -#+end_src -*** Ace link -Avy-style link following! - -#+begin_src emacs-lisp -(use-package ace-link - :straight t - :defer t - :general - (nmmap - :keymaps 'override - "gL" #'ace-link)) -#+end_src -** Save place -Saves current place in a buffer permanently, so on revisiting the file -(even in a different Emacs instance) you go back to the place you were -at last. - -#+begin_src emacs-lisp -(use-package saveplace - :demand t - :config - (save-place-mode)) -#+end_src -** Recentf -Recentf provides a method of keeping track of recently opened files. - -#+begin_src emacs-lisp -(use-package recentf - :defer t - :hook (after-init-hook . recentf-mode) - :general - (file-leader - "r" #'recentf)) -#+end_src -** Memory-report -New feature of Emacs-29, gives a rough report of memory usage with -some details. Useful to know on a long Emacs instance what could be -eating up memory. - -#+begin_src emacs-lisp -(use-package memory-report - :defer t - :general - (leader - "qm" #'memory-report)) -#+end_src -** Drag Stuff -Drag stuff around, like my favourite russian programmer (Tsoding). -Useful mechanism which works better than any vim motion. - -#+begin_src emacs-lisp -(use-package drag-stuff - :straight t - :defer t - :general - (nmmap - "C-M-h" #'drag-stuff-left - "C-M-j" #'drag-stuff-down - "C-M-k" #'drag-stuff-up - "C-M-l" #'drag-stuff-right)) -#+end_src -** Searching common directories -Using [[file:elisp/search.el][search.el]] I can search a set of -directories particularly efficiently. - -#+begin_src emacs-lisp -(use-package search - :defer t - :load-path "elisp/" - :general - (search-leader - "a" #'+search/search-all) - (file-leader - "p" #'+search/find-file)) -#+end_src -** Separedit -Edit anything anywhere all at once! - -#+begin_src emacs-lisp -(use-package separedit - :defer t - :straight t - :general - (leader "e" #'separedit) - :init - (setq separedit-default-mode 'org-mode - separedit-remove-trailing-spaces-in-comment t)) -#+end_src * Aesthetics -General look and feel of Emacs (mostly disabling stuff I don't like). +General look and feel of Emacs, perhaps the most important of all the +sections here. ** Themes I have both a dark and light theme for differing situations. Here I configure a timer which ensures I have a light theme during the day @@ -1530,1030 +1056,66 @@ Nice set of icons, for even more emojis. (insert-leader "e" #'all-the-icons-insert)) #+end_src -* Applications -Emacs is an operating system, now with a good text editor through -[[*Evil - Vim emulation][Evil]]. Let's configure some apps for it. -** EWW -Emacs Web Wowser is the inbuilt text based web browser for Emacs. It -can render images and basic CSS styles but doesn't have a JavaScript -engine, which makes sense as it's primarily a text interface. - -#+begin_src emacs-lisp -(use-package eww - :defer t - :general - (app-leader - "w" #'eww) - (nmmap - :keymaps 'eww-mode-map - "w" #'evil-forward-word-begin - "Y" #'eww-copy-page-url) - :config - (with-eval-after-load "evil-collection" - (evil-collection-eww-setup))) -#+end_src -** Magit -Magit is *the* git porcelain for Emacs, which perfectly encapsulates -the git CLI. It's so good that some people are use Emacs just for it. -It's difficult to describe it well without using it and it integrates -so well with Emacs that there is very little need to use the git CLI -ever. - -In this case I just need to setup the bindings for it. Also, define -an auto insert for commit messages so that I don't need to write -everything myself. - -#+begin_src emacs-lisp -(use-package transient - :defer t - :straight (:host github :repo "magit/transient" :tag "v0.7.5")) - -(use-package magit - :straight (:host github :repo "magit/magit" :tag "v4.1.0") - :defer t - :display - ("magit:.*" - (display-buffer-same-window) - (inhibit-duplicate-buffer . t)) - ("magit-diff:.*" - (display-buffer-below-selected)) - ("magit-log:.*" - (display-buffer-same-window)) - :general - (leader - "g" '(magit-dispatch :which-key "Magit")) - (code-leader - "b" #'magit-blame) - :init - (setq vc-follow-symlinks t - magit-blame-echo-style 'lines - magit-copy-revision-abbreviated t) - :config - (with-eval-after-load "evil" - (evil-set-initial-state 'magit-status-mode 'motion)) - (with-eval-after-load "evil-collection" - (evil-collection-magit-setup))) -#+end_src -** Calendar -Calendar is a simple inbuilt application that helps with date -functionalities. I add functionality to copy dates from the calendar -to the kill ring and bind it to "Y". - -#+begin_src emacs-lisp -(use-package calendar - :defer t - :commands (+calendar/copy-date +calendar/toggle-calendar) - :display - ("\\*Calendar\\*" - (display-buffer-at-bottom) - (inhibit-duplicate-buffer . t) - (window-height . 0.17)) - :general - (nmmap - :keymaps 'calendar-mode-map - "Y" #'+calendar/copy-date) - (app-leader - "d" #'calendar) - :config - (with-eval-after-load "evil-collection" - (evil-collection-calendar-setup)) - (defun +calendar/copy-date () - "Copy date under cursor into kill ring." - (interactive) - (if (use-region-p) - (call-interactively #'kill-ring-save) - (let ((date (calendar-cursor-to-date))) - (when date - (setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))) - (kill-new (format-time-string "%Y-%m-%d" date))))))) -#+end_src -** Mail -Mail is a funny thing; most people use it just for business or -advertising and it's come out of use in terms of personal -communication in the west for the most part (largely due to "social" -media applications). However, this isn't true for the open source and -free software movement who heavily use mail for communication. - -Integrating mail into Emacs helps as I can send source code and -integrate it into my workflow just a bit better. There are a few -ways of doing this, both in built and via package. -*** Notmuch -Notmuch is an application for categorising some local mail system. -It's really fast, has tons of customisable functionality and has good -integration with Emacs. I use ~mbsync~ separately to pull my mail -from the remote server. - -#+begin_src emacs-lisp -(use-package notmuch - :straight t - :defer t - :commands (notmuch +mail/flag-thread) - :general - (app-leader "m" #'notmuch) - (nmap - :keymaps 'notmuch-search-mode-map - "f" #'+mail/flag-thread) - :init - (defconst +mail/local-dir (no-littering-expand-var-file-name "mail/")) - (setq notmuch-show-logo nil - notmuch-search-oldest-first nil - notmuch-hello-sections '(notmuch-hello-insert-saved-searches - notmuch-hello-insert-alltags - notmuch-hello-insert-recent-searches) - notmuch-archive-tags '("-inbox" "-unread" "+archive") - message-auto-save-directory +mail/local-dir - message-directory +mail/local-dir) - (with-eval-after-load "evil-collection" - (evil-collection-notmuch-setup)) - :config - (defun +mail/flag-thread (&optional unflag beg end) - (interactive (cons current-prefix-arg (notmuch-interactive-region))) - (notmuch-search-tag - (notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end) - (when (eq beg end) - (notmuch-search-next-thread)))) -#+end_src -*** Smtpmail -Setup the smtpmail package, which is used when sending mail. Mostly -custom configuration for integration with other parts of Emacs' mail -system. - -#+begin_src emacs-lisp -(use-package smtpmail - :defer t - :commands mail-send - :init - (setq-default - smtpmail-smtp-server "mail.aryadevchavali.com" - smtpmail-smtp-user "aryadev" - smtpmail-smtp-service 587 - smtpmail-stream-type 'starttls - send-mail-function #'smtpmail-send-it - message-send-mail-function #'smtpmail-send-it)) -#+end_src -*** Mail signature using fortune -Generate a mail signature using the ~fortune~ executable. Pretty -cool! - -#+begin_src emacs-lisp -(use-package fortune - :init - (setq fortune-dir "/usr/share/fortune" - fortune-file "/usr/share/fortune/cookie") - :config - (defvar +mail/signature "---------------\nAryadev Chavali\n---------------\n%s") - (defun +mail/make-signature () - (interactive) - (format +mail/signature - (with-temp-buffer - (let ((fortune-buffer-name (current-buffer))) - (fortune-in-buffer t) - (if (bolp) (delete-char -1)) - (buffer-string))))) - (add-hook 'message-setup-hook - (lambda nil (setq message-signature (+mail/make-signature))))) -#+end_src -** Dired -Dired: Directory editor for Emacs. An incredibly nifty piece of -software which deeply integrates with Emacs as a whole. I can't think -of a better file management tool than this. - -Here I setup dired with a few niceties -+ Hide details by default (no extra stuff from ~ls~) -+ Omit dot files by default (using ~dired-omit-mode~) -+ If I have two dired windows open, moving or copying files in one - dired instance will automatically target the other dired window - (~dired-dwim~) -+ If opening an application on a PDF file, suggest ~zathura~ -+ Examine all the subdirectories within the same buffer - (~+dired/insert-all-subdirectories~) - -#+begin_src emacs-lisp -(use-package dired - :defer t - :commands (dired find-dired) - :hook - (dired-mode-hook . auto-revert-mode) - (dired-mode-hook . dired-hide-details-mode) - (dired-mode-hook . dired-omit-mode) - :init - (setq-default dired-listing-switches "-AFBlu --group-directories-first" - dired-omit-files "^\\." ; dotfiles - dired-omit-verbose nil - dired-dwim-target t - dired-kill-when-opening-new-dired-buffer t) - (with-eval-after-load "evil-collection" - (evil-collection-dired-setup)) - :general - (nmmap - :keymaps 'dired-mode-map - "SPC" nil - "SPC ," nil - "(" #'dired-hide-details-mode - ")" #'dired-omit-mode - "T" #'dired-create-empty-file - "H" #'dired-up-directory - "L" #'dired-find-file) - (leader - "D" #'dired-jump) - (dir-leader - "f" #'find-dired - "d" #'dired - "D" #'dired-other-window - "i" #'image-dired - "b" `(,(proc (interactive) - (dired "~/Text/Books/")) - :which-key "Open Books")) - (local-leader - :keymaps 'dired-mode-map - "i" #'dired-maybe-insert-subdir - "I" #'+dired/insert-all-subdirectories - "o" #'dired-omit-mode - "k" #'dired-prev-subdir - "j" #'dired-next-subdir - "K" #'dired-kill-subdir - "m" #'dired-mark-files-regexp - "u" #'dired-undo) - :config - (add-multiple-to-list dired-guess-shell-alist-user - '("\\.pdf\\'" "zathura") - '("\\.epub\\'" "zathura") - '("\\.jpg\\'" "feh") - '("\\.png\\'" "feh") - '("\\.webm\\'" "mpv") - '("\\.mp[34]\\'" "mpv") - '("\\.mkv\\'" "mpv")) - (defun +dired/insert-all-subdirectories () - "Insert all subdirectories currently viewable." - (interactive) - (dired-mark-directories nil) - (mapc #'dired-insert-subdir (dired-get-marked-files)) - (dired-unmark-all-marks))) -#+end_src -*** image-dired -Image dired is a little cherry on top for Dired: the ability to look -through swathes of images in a centralised fashion while still being -able to do all the usual dired stuff as well is really cool. - -#+begin_src emacs-lisp -(use-package dired - :defer t - :init - (setq image-dired-external-viewer "nsxiv") - :general - (nmmap - :keymaps 'image-dired-thumbnail-mode-map - "h" #'image-dired-backward-image - "l" #'image-dired-forward-image - "j" #'image-dired-next-line - "k" #'image-dired-previous-line - "H" #'image-dired-display-previous - "L" #'image-dired-display-next - "RET" #'image-dired-display-this - "m" #'image-dired-mark-thumb-original-file - "q" #'quit-window)) -#+end_src -*** fd-dired -Uses fd for finding file results in a directory: ~find-dired~ -> -~fd-dired~. - -#+begin_src emacs-lisp -(use-package fd-dired - :straight t - :after dired - :general - (dir-leader - "g" #'fd-dired)) -#+end_src -*** wdired -Similar to [[*(Rip)grep][wgrep]] =wdired= provides -the ability to use Emacs motions and editing on file names. This -makes stuff like mass renaming and other file management tasks way -easier than even using the mark based system. - -#+begin_src emacs-lisp -(use-package wdired - :after dired - :hook (wdired-mode-hook . undo-tree-mode) - :general - (nmmap - :keymaps 'dired-mode-map - "W" #'wdired-change-to-wdired-mode) - (nmmap - :keymaps 'wdired-mode-map - "ZZ" #'wdired-finish-edit - "ZQ" #'wdired-abort-changes) - :config - (eval-after-load "evil" - ;; 2024-09-07: Why does evil-set-initial-state returning a list of modes for - ;; normal state make eval-after-load evaluate as if it were an actual - ;; expression? - (progn (evil-set-initial-state 'wdired-mode 'normal) - nil))) -#+end_src -*** dired-rsync -Rsync is a great way of transferring files around *nix machines, and I -use dired for all my file management concerns. So I should be able to -rsync stuff around if I want. - -#+begin_src emacs-lisp -(use-package dired-rsync - :straight t - :after dired - :general - (nmmap - :keymaps 'dired-mode-map - "M-r" #'dired-rsync)) -#+end_src -** EShell -*** Why EShell? -EShell is an integrated shell environment for Emacs, written in Emacs -Lisp. Henceforth I will argue that it is the best shell/command -interpreter to use in Emacs, so good that you should eschew any second -class terminal emulators (~term~, ~shell~, etc). - -EShell is unlike the other alternatives in Emacs as it's a /shell/ -first, not a terminal emulator (granted, with the ability to spoof -some aspects of a terminal emulator). - -The killer benefits of EShell (which would appeal particularly to an -Emacs user) are a direct consequence of EShell being written in Emacs -Lisp: -- strong integration with Emacs utilities (such as ~dired~, - ~find-file~, any read functions, etc) -- very extensible, easy to write new commands which leverage Emacs - commands as well as external utilities -- agnostic of platform: "eshell/cd" will call the underlying change - directory function for you, so commands will (usually) mean the same - thing regardless of platform - - this means as long as Emacs can run on an operating system, one - may run EShell -- mixing of Lisp and shell commands, with piping! - -However, my favourite feature of EShell is the set of evaluators that -run on command input. Some of the benefits listed above come as a -consequence of this powerful feature. - -The main evaluator for any expression for EShell evaluates an -expression by testing the first symbol against different namespaces. -The namespaces are ordered such that if a symbol is not found in one, -the next namespace is tested. These namespaces are: -- alias (defined in the [[file:.config/eshell/aliases][aliases - file]]) -- "built-in" command i.e. in the ~eshell/~ namespace of functions -- external command -- Lisp function - -You can direct EShell to use these latter two namespaces: any -expression delimited by parentheses is considered a Lisp expression, -and any expression delimited by curly braces is considered an external -command. You may even pipe the results of one into another, allowing -a deeper level of integration between Emacs Lisp and the shell! -*** EShell basics -Setup some niceties of any shell program and some evil-like movements -for easy shell usage, both in and out of insert mode. - -NOTE: This mode doesn't allow you to set maps the normal way; you need -to set keybindings on eshell-mode-hook, otherwise it'll just overwrite -them. - -#+begin_src emacs-lisp -(use-package eshell - :defer t - :display - ("\\*eshell\\*" - (display-buffer-same-window) - (reusable-frames . t)) - :init - (defun +eshell/banner-message () - (concat (shell-command-to-string "fortune") "\n")) - - (setq eshell-cmpl-ignore-case t - eshell-cd-on-directory t - eshell-cd-shows-directory nil - eshell-highlight-prompt nil - eshell-banner-message '(+eshell/banner-message)) - - (defun +eshell/good-clear () - (interactive) - (eshell/clear-scrollback) - (eshell-send-input)) - - (add-hook - 'eshell-mode-hook - (defun +eshell/--setup-keymap nil - (interactive) - (general-def - :states '(normal insert visual) - :keymaps 'eshell-mode-map - "M-j" #'eshell-next-prompt - "M-k" #'eshell-previous-prompt - "C-j" #'eshell-next-matching-input-from-input - "C-k" #'eshell-previous-matching-input-from-input) - - (local-leader - :keymaps 'eshell-mode-map - "g" (proc (interactive) - (let ((buffer (current-buffer))) - (eshell/goto) - (with-current-buffer buffer - (eshell-send-input)))) - "l" (proc (interactive) - (eshell-return-to-prompt) - (insert "ls") - (eshell-send-input)) - "c" #'+eshell/good-clear - "k" #'eshell-kill-process)))) -#+end_src -*** EShell prompt -Here I use my external library -[[file:elisp/eshell-prompt.el][eshell-prompt]], which provides a -dynamic prompt for EShell. Current features include: -- Git repository details (with difference from remote and number of - modified files) -- Current date and time -- A coloured prompt character which changes colour based on the exit - code of the previous command - -NOTE: I don't defer this package because it doesn't use any EShell -internals without autoloading. +** Pretty symbols +Prettify symbols mode allows users to declare "symbols" that replace +text within certain modes. It's eye candy in most cases, but can aid +comprehension for symbol heavy languages. -#+begin_src emacs-lisp -(use-package eshell-prompt - :load-path "elisp/" - :config - (setq eshell-prompt-function #'+eshell-prompt/make-prompt)) -#+end_src -*** EShell additions -Using my external library -[[file:elisp/eshell-additions.el][eshell-additions]], I get a few new -internal EShell commands and a command to open EShell at the current -working directory. +This configures a ~use-package~ keyword which makes declaring pretty +symbols for language modes incredibly easy. Checkout my [[*Emacs +lisp][Emacs lisp]] configuration for an example. -NOTE: I don't defer this package because it must be loaded *before* -EShell is. This is because any ~eshell/*~ functions need to be loaded -before launching it. #+begin_src emacs-lisp -(use-package eshell-additions +(use-package prog-mode :demand t - :load-path "elisp/" - :general - (shell-leader - "t" #'+eshell/open) - (leader - "T" #'+eshell/at-cwd)) -#+end_src -*** EShell syntax highlighting -This package external package adds syntax highlighting to EShell -(disabling it for remote work). Doesn't require a lot of config -thankfully. - -#+begin_src emacs-lisp -(use-package eshell-syntax-highlighting - :straight t - :after eshell - :hook (eshell-mode-hook . eshell-syntax-highlighting-mode)) -#+end_src -** VTerm -There are a few times when EShell doesn't cut it, particularly in the -domain of TUI applications like ~cfdisk~. Emacs comes by default with -some terminal emulators that can run a system wide shell like SH or -ZSH (~shell~ and ~term~ for example), but they're pretty terrible. -~vterm~ is an external package using a shared library for terminal -emulation, and is much better than the default Emacs stuff. - -Since my ZSH configuration enables vim emulation, using ~evil~ on top -of it would lead to some weird states. Instead, use the Emacs state -so vim emulation is completely controlled by the shell. -#+begin_src emacs-lisp -(use-package vterm - :straight t - :general - (shell-leader - "v" #'vterm) - :init - (with-eval-after-load "evil" - (evil-set-initial-state 'vterm-mode 'emacs))) -#+end_src -** (Rip)grep -Grep is a great piece of software, a necessary tool in any Linux -user's inventory. Out of the box Emacs has a family of functions -utilising grep which present results in a -[[*Compilation][compilation]] buffer: ~grep~ searches files, ~rgrep~ -searches files in a directory using the ~find~ program and ~zgrep~ -searches archives. - -Ripgrep is a program that attempts to perform better than grep, and it -does. This is because of many optimisations, such as reading -=.gitignore= to exclude certain files from being searched. The -ripgrep package provides utilities to search projects and files. Of -course, this requires installing the rg binary which is available in -most distribution nowadays. -*** Grep -#+begin_src emacs-lisp -(use-package grep - :defer t - :display - ("^\\*grep.*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.35) - (reusable-frames . t)) - :general - (search-leader - "g" #'grep-this-file - "c" #'grep-config-file - "d" #'rgrep) - (nmmap - :keymaps 'grep-mode-map - "0" #'evil-beginning-of-line - "q" #'quit-window - "i" #'wgrep-change-to-wgrep-mode - "c" #'recompile) - (nmmap - :keymaps 'wgrep-mode-map - "q" #'evil-record-macro - "ZZ" #'wgrep-finish-edit - "ZQ" #'wgrep-abort-changes) - :config - ;; Without this wgrep doesn't work properly - (evil-set-initial-state 'grep-mode 'normal) - - (defun grep-file (query filename) - (grep (format "grep --color=auto -nIiHZEe \"%s\" -- %s" - query filename))) - - (defun grep-this-file () - (interactive) - (let ((query (read-string "Search for: "))) - (if (buffer-file-name (current-buffer)) - (grep-file query (buffer-file-name (current-buffer))) - (let ((temp-file (make-temp-file "temp-grep"))) - (write-region (point-min) (point-max) temp-file) - (grep-file query temp-file))))) - - (defun grep-config-file () - (interactive) - (let ((query (read-string "Search for: " "^[*]+ .*"))) - (grep-file query (concat user-emacs-directory "config.org"))))) -#+end_src -*** rg -#+begin_src emacs-lisp -(use-package rg - :straight t - :defer t - :commands (+rg/project-todo) - :display - ("^\\*\\*ripgrep\\*\\*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.35)) - :general - (search-leader - "r" #'rg) - (:keymaps 'project-prefix-map - "t" `(+rg/project-todo :which-key "Project TODOs")) - (nmmap - :keymaps 'rg-mode-map - "c" #'rg-recompile - "C" #'rg-rerun-toggle-case - "]]" #'rg-next-file - "[[" #'rg-prev-file - "q" #'quit-window - "i" #'wgrep-change-to-wgrep-mode) - :init - (setq rg-group-result t - rg-hide-command t - rg-show-columns nil - rg-show-header t - rg-custom-type-aliases nil - rg-default-alias-fallback "all" - rg-buffer-name "*ripgrep*") - :config - (defun +rg/project-todo () - (interactive) - (rg "TODO" "*" - (if (project-current) - (project-root (project-current)) - default-directory))) - (evil-set-initial-state 'rg-mode 'normal)) -#+end_src -** WAIT Elfeed -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -Elfeed is the perfect RSS feed reader, integrated into Emacs -perfectly. I've got a set of feeds that I use for a large variety of -stuff, mostly media and entertainment. I've also bound "<leader> ar" -to elfeed for loading the system. - -#+begin_src emacs-lisp -(use-package elfeed - :straight t - :general - (app-leader "r" #'elfeed) - (nmmap - :keymaps 'elfeed-search-mode-map - "gr" #'elfeed-update - "s" #'elfeed-search-live-filter - "<return>" #'elfeed-search-show-entry) - :init - (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) - - (setq +rss/feed-urls - '(("Arch Linux" - "https://www.archlinux.org/feeds/news/" - News Technology) - ("The Onion" - "https://www.theonion.com/rss" - Social) - ("Protesilaos Stavrou" - "https://www.youtube.com/@protesilaos" - YouTube Technology) - ("Tsoding Daily" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" - YouTube Technology) - ("Tsoding" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" - YouTube Technology) - ("Nexpo" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw" - YouTube Stories) - ("3B1B" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw" - YouTube) - ("Fredrik Knusden" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w" - YouTube Stories) - ("Barely Sociable" - "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw" - YouTube Stories) - ("Atrocity Guide" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw" - YouTube Stories) - ("Hacker News" - "https://news.ycombinator.com/rss" - Social News Technology) - ("Hacker Factor" - "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2" - Social))) - :config - (with-eval-after-load "evil-collection" - (evil-collection-elfeed-setup)) - - (setq elfeed-feeds (cl-map 'list #'(lambda (item) - (append (list (nth 1 item)) (cdr (cdr item)))) - +rss/feed-urls)) - - (advice-add 'elfeed-search-show-entry :after #'+elfeed/dispatch-entry) - - (defun +elfeed/dispatch-entry (entry) - "Process each type of entry differently. - e.g., you may want to open HN entries in eww." - (let ((url (elfeed-entry-link entry))) - (pcase url - ((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch")) - (mpv-play-url url)) - (_ (eww url)))))) -#+end_src -** IBuffer -IBuffer is the dired of buffers. Nothing much else to be said. - -#+begin_src emacs-lisp -(use-package ibuffer - :defer t - :general - (buffer-leader - "i" #'ibuffer) - :config - (with-eval-after-load "evil-collection" - (evil-collection-ibuffer-setup))) -#+end_src -** Proced -Emacs has two systems for process management: -+ proced: a general 'top' like interface which allows general - management of linux processes -+ list-processes: a specific Emacs based system that lists processes - spawned by Emacs (similar to a top for Emacs specifically) - -Core Proced config, just a few bindings and evil collection setup. - -#+begin_src emacs-lisp -(use-package proced - :defer t - :general - (app-leader - "p" #'proced) - (nmap - :keymaps 'proced-mode-map - "za" #'proced-toggle-auto-update) - :display - ("\\*Proced\\*" - (display-buffer-at-bottom) - (window-height . 0.25)) - :init - (setq proced-auto-update-interval 5) - :config - (with-eval-after-load "evil-collection" - (evil-collection-proced-setup))) -#+end_src -** Calculator -~calc-mode~ is a calculator system within Emacs that provides a -diverse array of mathematical operations. It uses reverse polish -notation, but there is a standard infix algebraic notation mode so -don't be too shocked. It can do a surprising amount of stuff, such -as: -+ finding derivatives/integrals of generic equations -+ matrix operations -+ finding solutions for equations, such as for finite degree multi - variable polynomials - -Perhaps most powerful is ~embedded-mode~. This allows one to perform -computation within a non ~calc-mode~ buffer. Surround any equation -with dollar signs and call ~(calc-embedded)~ with your cursor on it to -compute it. It'll replace the equation with the result it computed. -This is obviously incredibly useful; I don't even need to leave the -current buffer to perform some quick mathematics in it. - -#+begin_src emacs-lisp -(use-package calc - :defer t - :display - ("*Calculator*" - (display-buffer-at-bottom) - (window-height . 0.2)) - :general - (app-leader - "c" #'calc-dispatch) :init - (setq calc-algebraic-mode t) + (setq prettify-symbols-unprettify-at-point t) :config - (with-eval-after-load "evil-collection" - (evil-collection-calc-setup))) -#+end_src -** Zone -Emacs' out of the box screensaver software. - -#+begin_src emacs-lisp -(use-package zone - :defer t - :commands (zone) - :general - (leader - "z" #'zone) - :init - - (setq zone-programs - [zone-pgm-drip - zone-pgm-drip-fretfully])) -#+end_src -** (Wo)man -Man pages are the user manuals for most software on Linux. Of course, -Emacs comes out of the box with a renderer for man pages and some -searching capabilities. - -2023-08-17: `Man-notify-method' is the reason the `:display' record -doesn't work here. I think it's to do with how Man pages are rendered -or something, but very annoying as it's a break from standards! - -2024-10-08: Man pages are rendered via a separate process, which is -why this is necessary. - -#+begin_src emacs-lisp -(use-package man - :defer t - :init - (setq Man-notify-method 'thrifty) - :display - ("\\*Man.*" - (display-buffer-reuse-mode-window display-buffer-same-window) - (mode . Man-mode)) - :general - (file-leader - "m" #'man) ;; kinda like "find man page" - (nmmap - :keymaps 'Man-mode-map - "RET" #'man-follow)) -#+end_src -** Info -Info is GNU's attempt at better man pages. Most Emacs packages have -info pages so I'd like nice navigation options. - -#+begin_src emacs-lisp -(use-package info - :defer t - :general - (nmmap - :keymaps 'Info-mode-map - "h" #'evil-backward-char - "k" #'evil-previous-line - "l" #'evil-forward-char - "H" #'Info-history-back - "L" #'Info-history-forward - "C-j" #'Info-forward-node - "C-k" #'Info-backward-node - "RET" #'Info-follow-nearest-node - "m" #'Info-menu - "C-o" #'Info-history-back - "s" #'Info-search - "S" #'Info-search-case-sensitively - "i" #'Info-index - "a" #'info-apropos - "gj" #'Info-next - "gk" #'Info-prev - "g?" #'Info-summary - "q" #'quit-window) - :init - (with-eval-after-load "evil" - (evil-set-initial-state 'Info-mode 'normal))) -#+end_src -** WAIT gif-screencast -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -Little application that uses =gifsicle= to make essentially videos of -Emacs. Useful for demonstrating features. - -#+begin_src emacs-lisp -(use-package gif-screencast - :straight t - :general - (app-leader - "x" #'gif-screencast-start-or-stop) - :init - (setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/"))) -#+end_src -** Image-mode -Image mode, for viewing images. Supports tons of formats, easy to use -and integrates slickly into image-dired. Of course, + (with-eval-after-load "use-package-core" + (add-to-list 'use-package-keywords ':pretty) + (defun use-package-normalize/:pretty (_name-symbol _keyword args) + args) -#+begin_src emacs-lisp -(use-package image-mode - :defer t - :general - (nmmap - :keymaps 'image-mode-map - "+" #'image-increase-size - "-" #'image-decrease-size - "a" #'image-toggle-animation - "p+" #'image-increase-speed - "p-" #'image-increase-speed - "h" #'image-backward-hscroll - "j" #'image-next-line - "k" #'image-previous-line - "l" #'image-forward-hscroll)) -#+end_src -** empv -Emacs MPV bindings, with very cool controls for queuing files for -playing. -#+begin_src emacs-lisp -(use-package empv - :straight t - :defer t - :init - (setq empv-audio-dir (expand-file-name "~/Media/audio") - empv-video-dir (expand-file-name "~/Media/videos") - empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" - "flac" "aac" "opus") - empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" - "webm")) - :hydra - (empv-hydra - nil "Hydra for EMPV" - ("(" #'empv-chapter-prev - "chapter-prev" :column "playback") - (")" #'empv-chapter-next - "chapter-next" :column "playback") - ("0" #'empv-volume-up - "volume-up" :column "playback") - ("9" #'empv-volume-down - "volume-down" :column "playback") - ("[" #'empv-playback-speed-down - "playback-speed-down" :column "playback") - ("]" #'empv-playback-speed-up - "playback-speed-up" :column "playback") - ("_" #'empv-toggle-video - "toggle-video" :column "playback") - ("q" #'empv-exit - "exit" :column "playback") - ("s" #'empv-seek - "seek" :column "playback") - ("t" #'empv-toggle - "toggle" :column "playback") - ("x" #'empv-chapter-select - "chapter-select" :column "playback") - ("N" #'empv-playlist-prev - "playlist-prev" :column "playlist") - ("C" #'empv-playlist-clear - "playlist-clear" :column "playlist") - ("n" #'empv-playlist-next - "playlist-next" :column "playlist") - ("p" #'empv-playlist-select - "playlist-select" :column "playlist") - ("S" #'empv-playlist-shuffle - "playlist-shuffle" :column "playlist") - ("a" #'empv-play-audio - "play-audio" :column "play") - ("R" #'empv-play-random-channel - "play-random-channel" :column "play") - ("d" #'empv-play-directory - "play-directory" :column "play") - ("f" #'empv-play-file - "play-file" :column "play") - ("o" #'empv-play-or-enqueue - "play-or-enqueue" :column "play") - ("r" #'empv-play-radio - "play-radio" :column "play") - ("v" #'empv-play-video - "play-video" :column "play") - ("i" #'empv-display-current - "display-current" :column "misc") - ("l" #'empv-log-current-radio-song-name - "log-current-radio-song-name" :column "misc") - ("c" #'empv-copy-path - "copy-path" :column "misc") - ("Y" #'empv-youtube-last-results - "youtube-last-results" :column "misc") - ("y" #'empv-youtube - "youtube" :column "misc")) - :general - (app-leader - "e" #'empv-hydra/body)) -#+end_src -** Grand Unified Debugger (GUD) -GUD is a system for debugging, hooking into processes and -providing an interface to the user all in Emacs. Here I define a -hydra which provides a ton of the useful =gud= keybindings that exist -in an Emacs-only map. -#+begin_src emacs-lisp -(use-package gud - :general - :after hydra - :hydra - (gud-hydra - (:hint nil) "Hydra for GUD" - ("<" #'gud-up "Up" - :column "Control Flow") - (">" #'gud-down "Down" - :column "Control Flow") - ("b" #'gud-break "Break" - :column "Breakpoints") - ("d" #'gud-remove "Remove" - :column "Breakpoints") - ("f" #'gud-finish "Finish" - :column "Control Flow") - ("J" #'gud-jump "Jump" - :column "Control Flow") - ("L" #'gud-refresh "Refresh" - :column "Misc") - ("n" #'gud-next "Next" - :column "Control Flow") - ("p" #'gud-print "Print" - :column "Misc") - ("c" #'gud-cont "Cont" - :column "Control Flow") - ("s" #'gud-step "Step" - :column "Control Flow") - ("t" #'gud-tbreak "Tbreak" - :column "Control Flow") - ("u" #'gud-until "Until" - :column "Control Flow") - ("v" #'gud-go "Go" - :column "Control Flow") - ("w" #'gud-watch "Watch" - :column "Breakpoints") - ("TAB" #'gud-stepi "Stepi" - :column "Control Flow")) - :general - (code-leader "d" #'gud-hydra/body - "D" #'gud-gdb)) + (defun use-package-handler/:pretty (name _keyword args rest state) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (arg) + (let ((mode (car arg)) + (rest (cdr arg))) + `(add-hook + ',mode + #'(lambda nil + (setq prettify-symbols-alist ',rest) + (prettify-symbols-mode))))) + args))))) #+end_src -** WAIT esup -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -I used to be able to just use -[[file:elisp/profiler-dotemacs.el][profile-dotemacs.el]], when my -Emacs config was smaller, but now it tells me very little information -about where my setup is inefficient due to the literate config. Just -found this ~esup~ thing and it works perfectly, exactly how I would -prefer getting this kind of information. It runs an external Emacs -instance and collects information from it, so it doesn't require -restarting Emacs to profile, and I can compile my configuration in my -current instance to test it immediately. -2023-10-16: Unless I'm doing some optimisations or tests, I don't -really need this in my config at all times. Enable when needed. +Here's a collection of keywords and possible associated symbols for +any prog language of choice. Mostly for reference and copying. -#+begin_src emacs-lisp -(use-package esup - :straight t - :defer t - :general - (leader - "qe" #'esup)) -#+end_src -* Text modes -Standard packages and configurations for text-mode and its derived -modes. +#+begin_example +("null" . "Ø") +("list" . "ℓ") +("string" . "𝕊") +("char" . "ℂ") +("int" . "ℤ") +("float" . "ℝ") +("!" . "¬") +("for" . "Σ") +("return" . "≡") +("reduce" . "↓") +("map" . "→") +("some" . "∃") +("every" . "∃") +("lambda" . "λ") +("function" . "ƒ") +("<=" . "≤") +(">=" . "≥") +#+end_example +* Text packages +Standard packages and configurations for dealing with text, usually +prose. ** Flyspell Flyspell allows me to spell check text documents. I use it primarily in org mode, as that is my preferred prose writing software, but I @@ -2572,31 +1134,16 @@ into text-mode. (mode-leader "s" #'flyspell-mode)) #+end_src -** Undo tree -Undo tree sits on top of Emacs' undo capabilities. It provides a nice -visual for the history of a buffer and is a great way to produce -branches of edits. This history may be saved to and loaded from the -disk, which makes Emacs a quasi version control system in and of -itself. The only feature left is describing changes... - -#+begin_src emacs-lisp -(use-package undo-tree - :demand t - :straight t - :general - (leader - "u" #'undo-tree-visualize) - :init - (setq undo-tree-auto-save-history t - undo-tree-history-directory-alist backup-directory-alist) - :config - (global-undo-tree-mode)) -#+end_src ** Whitespace -Deleting whitespace, highlighting when going beyond the 80th character -limit, all good stuff. I don't want to highlight whitespace for -general mode categories (Lisp shouldn't really have an 80 character -limit), so set it for specific modes need the help. +I hate inconsistencies in whitespace. If I'm using tabs, I better be +using them everywhere, and if I'm using whitespace, it better be well +formed. Furthermore, hard character limits are important (enforced by +[[*Filling and displaying fills][auto-fill-mode]]) which is why I like +to have some kind of highlighting option. + +I don't want to highlight whitespace for general mode categories (Lisp +shouldn't really have an 80 character limit), so set it for specific +modes that need the help. #+begin_src emacs-lisp (use-package whitespace @@ -2609,12 +1156,13 @@ limit), so set it for specific modes need the help. :hook (before-save-hook . whitespace-cleanup) ((c-mode-hook c++-mode-hook haskell-mode-hook python-mode-hook - org-mode-hook text-mode-hook js-mode-hook) + org-mode-hook text-mode-hook js-mode-hook + nasm-mode-hook) . whitespace-mode) :init (setq whitespace-line-column nil - whitespace-style '(face empty spaces tabs newline trailing lines-char - tab-mark))) + whitespace-style '(face empty spaces tabs newline trailing + lines-char tab-mark))) #+end_src ** Filling and displaying fills The fill-column is the number of characters that should be in a single @@ -2625,17 +1173,34 @@ use 70 fill columns while code should stick to 80. #+begin_src emacs-lisp (use-package emacs + :hook + (text-mode-hook . auto-fill-mode) :init (setq-default fill-column 80) (add-hook 'text-mode-hook (proc (setq-local fill-column 70))) - :hook - (text-mode-hook . auto-fill-mode) ((c-mode-hook c++-mode-hook haskell-mode-hook python-mode-hook org-mode-hook text-mode-hook js-mode-hook) . display-fill-column-indicator-mode)) #+end_src +** Visual line mode +When dealing with really long lines I have a specific taste. I don't +want text to just go off the screen, such that I have to move the +cursor forward in the line to see later content - I want line +wrapping. Emacs provides ~truncate-lines~ for line wrapping but it +cuts words, which isn't very nice as that cut word spans two lines. +Instead I want Emacs to cut by word, which is where visual-line-mode +comes in. Since I may experience really long lines anywhere, it +should be enabled globally. + +#+begin_src emacs-lisp +(use-package emacs + :demand t + :config + (global-visual-line-mode t)) +#+end_src ** Show-paren-mode -Show parenthesis for Emacs +When the cursor is over a parenthesis, highlight the other member of +the pair. #+begin_src emacs-lisp (use-package paren @@ -2686,9 +1251,34 @@ easy to guess what text I'd use. (insert-leader "p" #'lorem-ipsum-insert-paragraphs)) #+end_src +** Auto insert +Allows inserting text immediately upon creating a new buffer with a +given name, similar to template. Supports skeletons for inserting +text. To make it easier for later systems to define their own auto +inserts, I define a ~use-package~ keyword (~:auto-insert~) which +allows one to define an entry for ~auto-insert-alist~. + +#+begin_src emacs-lisp +(use-package autoinsert + :demand t + :hook (after-init-hook . auto-insert-mode) + :config + (with-eval-after-load "use-package-core" + (add-to-list 'use-package-keywords ':auto-insert) + (defun use-package-normalize/:auto-insert (_name-symbol _keyword args) + args) + (defun use-package-handler/:auto-insert (name _keyword args rest state) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (arg) + `(add-to-list + 'auto-insert-alist + ',arg)) + args))))) +#+end_src * Programming packages -Packages that help with programming in general, providing IDE like -capabilities. +Packages that help with programming. ** Eldoc Eldoc presents documentation to the user upon placing ones cursor upon any symbol. This is very useful when programming as it: @@ -2771,7 +1361,6 @@ to be set. :keymaps 'eglot-mode-map "f" #'eglot-format "a" #'eglot-code-actions - "r" #'eglot-rename "R" #'eglot-reconnect) :init (setq eglot-auto-shutdown t @@ -2831,14 +1420,9 @@ description I give won't do it justice. #+begin_src emacs-lisp (use-package aggressive-indent :straight t - :demand t - :config - (add-multiple-to-list aggressive-indent-excluded-modes - 'c-mode 'c++-mode 'cc-mode - 'asm-mode 'js-mode 'typescript-mode - 'tsx-mode 'typescript-ts-mode 'tsx-ts-mode - 'tuareg-mode) - (global-aggressive-indent-mode)) + :hook (python-mode-hook . aggressive-indent-mode) + :hook (emacs-lisp-mode-hook . aggressive-indent-mode) + :hook (lisp-mode-hook . aggressive-indent-mode)) #+end_src ** Compilation Compilation mode is an incredibly useful subsystem of Emacs which @@ -2858,7 +1442,7 @@ so you can actually read the text. :display ("\\*compilation\\*" (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.1) + (window-height . 0.3) (reusable-frames . t)) :hydra (move-error-hydra @@ -2871,7 +1455,9 @@ so you can actually read the text. "k" #'move-error-hydra/previous-error) (code-leader "c" #'compile - "C" #'recompile) + "r" #'recompile) + (nmap + "M-r" #'recompile) (:keymaps 'compilation-mode-map "g" nil) ;; by default this is recompile (nmmap @@ -3556,6 +2142,13 @@ the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself. :defer t :mode ("\\.bin" "\\.out")) #+end_src +** NASM +#+begin_src emacs-lisp +(use-package nasm-mode + :straight t + :defer t + :mode ("\\.asm" . nasm-mode)) +#+end_src ** C/C++ Setup for C and C++ modes, using Emacs' default package: cc-mode. *** cc-mode @@ -4438,3 +3031,1441 @@ appropriately. (funcall method indent-point state)))))))) (setq-default lisp-indent-function #'+oreo/lisp-indent-function)) #+end_src +* Applications +Emacs is an operating system, now with a good text editor through +[[*Evil - Vim emulation][Evil]]. Let's configure some apps for it. +** EWW +Emacs Web Wowser is the inbuilt text based web browser for Emacs. It +can render images and basic CSS styles but doesn't have a JavaScript +engine, which makes sense as it's primarily a text interface. + +#+begin_src emacs-lisp +(use-package eww + :defer t + :general + (app-leader + "w" #'eww) + (nmmap + :keymaps 'eww-mode-map + "w" #'evil-forward-word-begin + "Y" #'eww-copy-page-url) + :config + (with-eval-after-load "evil-collection" + (evil-collection-eww-setup))) +#+end_src +** Magit +Magit is *the* git porcelain for Emacs, which perfectly encapsulates +the git CLI. It's so good that some people are use Emacs just for it. +It's difficult to describe it well without using it and it integrates +so well with Emacs that there is very little need to use the git CLI +ever. + +In this case I just need to setup the bindings for it. Also, define +an auto insert for commit messages so that I don't need to write +everything myself. + +#+begin_src emacs-lisp +(use-package transient + :defer t + :straight (:host github :repo "magit/transient" :tag "v0.7.5")) + +(use-package magit + :straight (:host github :repo "magit/magit" :tag "v4.1.0") + :defer t + :display + ("magit:.*" + (display-buffer-same-window) + (inhibit-duplicate-buffer . t)) + ("magit-diff:.*" + (display-buffer-below-selected)) + ("magit-log:.*" + (display-buffer-same-window)) + :general + (leader + "g" '(magit-dispatch :which-key "Magit")) + (code-leader + "b" #'magit-blame) + :init + (setq vc-follow-symlinks t + magit-blame-echo-style 'lines + magit-copy-revision-abbreviated t) + :config + (with-eval-after-load "evil" + (evil-set-initial-state 'magit-status-mode 'motion)) + (with-eval-after-load "evil-collection" + (evil-collection-magit-setup))) +#+end_src +** Calendar +Calendar is a simple inbuilt application that helps with date +functionalities. I add functionality to copy dates from the calendar +to the kill ring and bind it to "Y". + +#+begin_src emacs-lisp +(use-package calendar + :defer t + :commands (+calendar/copy-date +calendar/toggle-calendar) + :display + ("\\*Calendar\\*" + (display-buffer-at-bottom) + (inhibit-duplicate-buffer . t) + (window-height . 0.17)) + :general + (nmmap + :keymaps 'calendar-mode-map + "Y" #'+calendar/copy-date) + (app-leader + "d" #'calendar) + :config + (with-eval-after-load "evil-collection" + (evil-collection-calendar-setup)) + (defun +calendar/copy-date () + "Copy date under cursor into kill ring." + (interactive) + (if (use-region-p) + (call-interactively #'kill-ring-save) + (let ((date (calendar-cursor-to-date))) + (when date + (setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))) + (kill-new (format-time-string "%Y-%m-%d" date))))))) +#+end_src +** Mail +Mail is a funny thing; most people use it just for business or +advertising and it's come out of use in terms of personal +communication in the west for the most part (largely due to "social" +media applications). However, this isn't true for the open source and +free software movement who heavily use mail for communication. + +Integrating mail into Emacs helps as I can send source code and +integrate it into my workflow just a bit better. There are a few +ways of doing this, both in built and via package. +*** Notmuch +Notmuch is an application for categorising some local mail system. +It's really fast, has tons of customisable functionality and has good +integration with Emacs. I use ~mbsync~ separately to pull my mail +from the remote server. + +#+begin_src emacs-lisp +(use-package notmuch + :straight t + :defer t + :commands (notmuch +mail/flag-thread) + :general + (app-leader "m" #'notmuch) + (nmap + :keymaps 'notmuch-search-mode-map + "f" #'+mail/flag-thread) + :init + (defconst +mail/local-dir (no-littering-expand-var-file-name "mail/")) + (setq notmuch-show-logo nil + notmuch-search-oldest-first nil + notmuch-hello-sections '(notmuch-hello-insert-saved-searches + notmuch-hello-insert-alltags + notmuch-hello-insert-recent-searches) + notmuch-archive-tags '("-inbox" "-unread" "+archive") + message-auto-save-directory +mail/local-dir + message-directory +mail/local-dir) + (with-eval-after-load "evil-collection" + (evil-collection-notmuch-setup)) + :config + (defun +mail/flag-thread (&optional unflag beg end) + (interactive (cons current-prefix-arg (notmuch-interactive-region))) + (notmuch-search-tag + (notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end) + (when (eq beg end) + (notmuch-search-next-thread)))) +#+end_src +*** Smtpmail +Setup the smtpmail package, which is used when sending mail. Mostly +custom configuration for integration with other parts of Emacs' mail +system. + +#+begin_src emacs-lisp +(use-package smtpmail + :defer t + :commands mail-send + :init + (setq-default + smtpmail-smtp-server "mail.aryadevchavali.com" + smtpmail-smtp-user "aryadev" + smtpmail-smtp-service 587 + smtpmail-stream-type 'starttls + send-mail-function #'smtpmail-send-it + message-send-mail-function #'smtpmail-send-it)) +#+end_src +*** Mail signature using fortune +Generate a mail signature using the ~fortune~ executable. Pretty +cool! + +#+begin_src emacs-lisp +(use-package fortune + :init + (setq fortune-dir "/usr/share/fortune" + fortune-file "/usr/share/fortune/cookie") + :config + (defvar +mail/signature "---------------\nAryadev Chavali\n---------------\n%s") + (defun +mail/make-signature () + (interactive) + (format +mail/signature + (with-temp-buffer + (let ((fortune-buffer-name (current-buffer))) + (fortune-in-buffer t) + (if (bolp) (delete-char -1)) + (buffer-string))))) + (add-hook 'message-setup-hook + (lambda nil (setq message-signature (+mail/make-signature))))) +#+end_src +** Dired +Dired: Directory editor for Emacs. An incredibly nifty piece of +software which deeply integrates with Emacs as a whole. I can't think +of a better file management tool than this. + +Here I setup dired with a few niceties ++ Hide details by default (no extra stuff from ~ls~) ++ Omit dot files by default (using ~dired-omit-mode~) ++ If I have two dired windows open, moving or copying files in one + dired instance will automatically target the other dired window + (~dired-dwim~) ++ If opening an application on a PDF file, suggest ~zathura~ ++ Examine all the subdirectories within the same buffer + (~+dired/insert-all-subdirectories~) + +#+begin_src emacs-lisp +(use-package dired + :defer t + :commands (dired find-dired) + :hook + (dired-mode-hook . auto-revert-mode) + (dired-mode-hook . dired-hide-details-mode) + (dired-mode-hook . dired-omit-mode) + :init + (setq-default dired-listing-switches "-AFBlu --group-directories-first" + dired-omit-files "^\\." ; dotfiles + dired-omit-verbose nil + dired-dwim-target t + dired-kill-when-opening-new-dired-buffer t) + (with-eval-after-load "evil-collection" + (evil-collection-dired-setup)) + :general + (nmmap + :keymaps 'dired-mode-map + "SPC" nil + "SPC ," nil + "(" #'dired-hide-details-mode + ")" #'dired-omit-mode + "T" #'dired-create-empty-file + "H" #'dired-up-directory + "L" #'dired-find-file) + (leader + "D" #'dired-jump) + (dir-leader + "f" #'find-dired + "d" #'dired + "D" #'dired-other-window + "i" #'image-dired + "b" `(,(proc (interactive) + (dired "~/Text/Books/")) + :which-key "Open Books")) + (local-leader + :keymaps 'dired-mode-map + "i" #'dired-maybe-insert-subdir + "I" #'+dired/insert-all-subdirectories + "o" #'dired-omit-mode + "k" #'dired-prev-subdir + "j" #'dired-next-subdir + "K" #'dired-kill-subdir + "m" #'dired-mark-files-regexp + "u" #'dired-undo) + :config + (add-multiple-to-list dired-guess-shell-alist-user + '("\\.pdf\\'" "zathura") + '("\\.epub\\'" "zathura") + '("\\.jpg\\'" "feh") + '("\\.png\\'" "feh") + '("\\.webm\\'" "mpv") + '("\\.mp[34]\\'" "mpv") + '("\\.mkv\\'" "mpv")) + (defun +dired/insert-all-subdirectories () + "Insert all subdirectories currently viewable." + (interactive) + (dired-mark-directories nil) + (mapc #'dired-insert-subdir (dired-get-marked-files)) + (dired-unmark-all-marks))) +#+end_src +*** image-dired +Image dired is a little cherry on top for Dired: the ability to look +through swathes of images in a centralised fashion while still being +able to do all the usual dired stuff as well is really cool. + +#+begin_src emacs-lisp +(use-package dired + :defer t + :init + (setq image-dired-external-viewer "nsxiv") + :general + (nmmap + :keymaps 'image-dired-thumbnail-mode-map + "h" #'image-dired-backward-image + "l" #'image-dired-forward-image + "j" #'image-dired-next-line + "k" #'image-dired-previous-line + "H" #'image-dired-display-previous + "L" #'image-dired-display-next + "RET" #'image-dired-display-this + "m" #'image-dired-mark-thumb-original-file + "q" #'quit-window)) +#+end_src +*** fd-dired +Uses fd for finding file results in a directory: ~find-dired~ -> +~fd-dired~. + +#+begin_src emacs-lisp +(use-package fd-dired + :straight t + :after dired + :general + (dir-leader + "g" #'fd-dired)) +#+end_src +*** wdired +Similar to [[*(Rip)grep][wgrep]] =wdired= provides +the ability to use Emacs motions and editing on file names. This +makes stuff like mass renaming and other file management tasks way +easier than even using the mark based system. + +#+begin_src emacs-lisp +(use-package wdired + :after dired + :hook (wdired-mode-hook . undo-tree-mode) + :general + (nmmap + :keymaps 'dired-mode-map + "W" #'wdired-change-to-wdired-mode) + (nmmap + :keymaps 'wdired-mode-map + "ZZ" #'wdired-finish-edit + "ZQ" #'wdired-abort-changes) + :config + (eval-after-load "evil" + ;; 2024-09-07: Why does evil-set-initial-state returning a list of modes for + ;; normal state make eval-after-load evaluate as if it were an actual + ;; expression? + (progn (evil-set-initial-state 'wdired-mode 'normal) + nil))) +#+end_src +*** dired-rsync +Rsync is a great way of transferring files around *nix machines, and I +use dired for all my file management concerns. So I should be able to +rsync stuff around if I want. + +#+begin_src emacs-lisp +(use-package dired-rsync + :straight t + :after dired + :general + (nmmap + :keymaps 'dired-mode-map + "M-r" #'dired-rsync)) +#+end_src +** EShell +*** Why EShell? +EShell is an integrated shell environment for Emacs, written in Emacs +Lisp. Henceforth I will argue that it is the best shell/command +interpreter to use in Emacs, so good that you should eschew any second +class terminal emulators (~term~, ~shell~, etc). + +EShell is unlike the other alternatives in Emacs as it's a /shell/ +first, not a terminal emulator (granted, with the ability to spoof +some aspects of a terminal emulator). + +The killer benefits of EShell (which would appeal particularly to an +Emacs user) are a direct consequence of EShell being written in Emacs +Lisp: +- strong integration with Emacs utilities (such as ~dired~, + ~find-file~, any read functions, etc) +- very extensible, easy to write new commands which leverage Emacs + commands as well as external utilities +- agnostic of platform: "eshell/cd" will call the underlying change + directory function for you, so commands will (usually) mean the same + thing regardless of platform + - this means as long as Emacs can run on an operating system, one + may run EShell +- mixing of Lisp and shell commands, with piping! + +However, my favourite feature of EShell is the set of evaluators that +run on command input. Some of the benefits listed above come as a +consequence of this powerful feature. + +The main evaluator for any expression for EShell evaluates an +expression by testing the first symbol against different namespaces. +The namespaces are ordered such that if a symbol is not found in one, +the next namespace is tested. These namespaces are: +- alias (defined in the [[file:.config/eshell/aliases][aliases + file]]) +- "built-in" command i.e. in the ~eshell/~ namespace of functions +- external command +- Lisp function + +You can direct EShell to use these latter two namespaces: any +expression delimited by parentheses is considered a Lisp expression, +and any expression delimited by curly braces is considered an external +command. You may even pipe the results of one into another, allowing +a deeper level of integration between Emacs Lisp and the shell! +*** EShell basics +Setup some niceties of any shell program and some evil-like movements +for easy shell usage, both in and out of insert mode. + +NOTE: This mode doesn't allow you to set maps the normal way; you need +to set keybindings on eshell-mode-hook, otherwise it'll just overwrite +them. + +#+begin_src emacs-lisp +(use-package eshell + :defer t + :display + ("\\*eshell\\*" + (display-buffer-same-window) + (reusable-frames . t)) + :init + (defun +eshell/banner-message () + (concat (shell-command-to-string "fortune") "\n")) + + (setq eshell-cmpl-ignore-case t + eshell-cd-on-directory t + eshell-cd-shows-directory nil + eshell-highlight-prompt nil + eshell-banner-message '(+eshell/banner-message)) + + (defun +eshell/good-clear () + (interactive) + (eshell/clear-scrollback) + (eshell-send-input)) + + (add-hook + 'eshell-mode-hook + (defun +eshell/--setup-keymap nil + (interactive) + (general-def + :states '(normal insert visual) + :keymaps 'eshell-mode-map + "M-j" #'eshell-next-prompt + "M-k" #'eshell-previous-prompt + "C-j" #'eshell-next-matching-input-from-input + "C-k" #'eshell-previous-matching-input-from-input) + + (local-leader + :keymaps 'eshell-mode-map + "g" (proc (interactive) + (let ((buffer (current-buffer))) + (eshell/goto) + (with-current-buffer buffer + (eshell-send-input)))) + "l" (proc (interactive) + (eshell-return-to-prompt) + (insert "ls") + (eshell-send-input)) + "c" #'+eshell/good-clear + "k" #'eshell-kill-process)))) +#+end_src +*** EShell prompt +Here I use my external library +[[file:elisp/eshell-prompt.el][eshell-prompt]], which provides a +dynamic prompt for EShell. Current features include: +- Git repository details (with difference from remote and number of + modified files) +- Current date and time +- A coloured prompt character which changes colour based on the exit + code of the previous command + +NOTE: I don't defer this package because it doesn't use any EShell +internals without autoloading. + +#+begin_src emacs-lisp +(use-package eshell-prompt + :load-path "elisp/" + :config + (setq eshell-prompt-function #'+eshell-prompt/make-prompt)) +#+end_src +*** EShell additions +Using my external library +[[file:elisp/eshell-additions.el][eshell-additions]], I get a few new +internal EShell commands and a command to open EShell at the current +working directory. + +NOTE: I don't defer this package because it must be loaded *before* +EShell is. This is because any ~eshell/*~ functions need to be loaded +before launching it. +#+begin_src emacs-lisp +(use-package eshell-additions + :demand t + :load-path "elisp/" + :general + (shell-leader + "t" #'+eshell/open) + (leader + "T" #'+eshell/at-cwd + "E" #'eshell-command)) +#+end_src +*** EShell syntax highlighting +This package external package adds syntax highlighting to EShell +(disabling it for remote work). Doesn't require a lot of config +thankfully. + +#+begin_src emacs-lisp +(use-package eshell-syntax-highlighting + :straight t + :after eshell + :hook (eshell-mode-hook . eshell-syntax-highlighting-mode)) +#+end_src +** VTerm +There are a few times when EShell doesn't cut it, particularly in the +domain of TUI applications like ~cfdisk~. Emacs comes by default with +some terminal emulators that can run a system wide shell like SH or +ZSH (~shell~ and ~term~ for example), but they're pretty terrible. +~vterm~ is an external package using a shared library for terminal +emulation, and is much better than the default Emacs stuff. + +Since my ZSH configuration enables vim emulation, using ~evil~ on top +of it would lead to some weird states. Instead, use the Emacs state +so vim emulation is completely controlled by the shell. +#+begin_src emacs-lisp +(use-package vterm + :straight t + :general + (shell-leader + "v" #'vterm) + :init + (with-eval-after-load "evil" + (evil-set-initial-state 'vterm-mode 'emacs))) +#+end_src +** (Rip)grep +Grep is a great piece of software, a necessary tool in any Linux +user's inventory. Out of the box Emacs has a family of functions +utilising grep which present results in a +[[*Compilation][compilation]] buffer: ~grep~ searches files, ~rgrep~ +searches files in a directory using the ~find~ program and ~zgrep~ +searches archives. + +Ripgrep is a program that attempts to perform better than grep, and it +does. This is because of many optimisations, such as reading +=.gitignore= to exclude certain files from being searched. The +ripgrep package provides utilities to search projects and files. Of +course, this requires installing the rg binary which is available in +most distribution nowadays. +*** Grep +#+begin_src emacs-lisp +(use-package grep + :defer t + :display + ("^\\*grep.*" + (display-buffer-reuse-window display-buffer-at-bottom) + (window-height . 0.35) + (reusable-frames . t)) + :general + (search-leader + "g" #'grep-this-file + "c" #'grep-config-file + "d" #'rgrep) + (nmmap + :keymaps 'grep-mode-map + "0" #'evil-beginning-of-line + "q" #'quit-window + "i" #'wgrep-change-to-wgrep-mode + "c" #'recompile) + (nmmap + :keymaps 'wgrep-mode-map + "q" #'evil-record-macro + "ZZ" #'wgrep-finish-edit + "ZQ" #'wgrep-abort-changes) + :config + ;; Without this wgrep doesn't work properly + (evil-set-initial-state 'grep-mode 'normal) + + (defun grep-file (query filename) + (grep (format "grep --color=auto -nIiHZEe \"%s\" -- %s" + query filename))) + + (defun grep-this-file () + (interactive) + (let ((query (read-string "Search for: "))) + (if (buffer-file-name (current-buffer)) + (grep-file query (buffer-file-name (current-buffer))) + (let ((temp-file (make-temp-file "temp-grep"))) + (write-region (point-min) (point-max) temp-file) + (grep-file query temp-file))))) + + (defun grep-config-file () + (interactive) + (let ((query (read-string "Search for: " "^[*]+ .*"))) + (grep-file query (concat user-emacs-directory "config.org"))))) +#+end_src +*** rg +#+begin_src emacs-lisp +(use-package rg + :straight t + :defer t + :commands (+rg/project-todo) + :display + ("^\\*\\*ripgrep\\*\\*" + (display-buffer-reuse-window display-buffer-at-bottom) + (window-height . 0.35)) + :general + (search-leader + "r" #'rg) + (:keymaps 'project-prefix-map + "t" `(+rg/project-todo :which-key "Project TODOs")) + (nmmap + :keymaps 'rg-mode-map + "c" #'rg-recompile + "C" #'rg-rerun-toggle-case + "]]" #'rg-next-file + "[[" #'rg-prev-file + "q" #'quit-window + "i" #'wgrep-change-to-wgrep-mode) + :init + (setq rg-group-result t + rg-hide-command t + rg-show-columns nil + rg-show-header t + rg-custom-type-aliases nil + rg-default-alias-fallback "all" + rg-buffer-name "*ripgrep*") + :config + (defun +rg/project-todo () + (interactive) + (rg "TODO" "*" + (if (project-current) + (project-root (project-current)) + default-directory))) + (evil-set-initial-state 'rg-mode 'normal)) +#+end_src +** WAIT Elfeed +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +Elfeed is the perfect RSS feed reader, integrated into Emacs +perfectly. I've got a set of feeds that I use for a large variety of +stuff, mostly media and entertainment. I've also bound "<leader> ar" +to elfeed for loading the system. + +#+begin_src emacs-lisp +(use-package elfeed + :straight t + :general + (app-leader "r" #'elfeed) + (nmmap + :keymaps 'elfeed-search-mode-map + "gr" #'elfeed-update + "s" #'elfeed-search-live-filter + "<return>" #'elfeed-search-show-entry) + :init + (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) + + (setq +rss/feed-urls + '(("Arch Linux" + "https://www.archlinux.org/feeds/news/" + News Technology) + ("The Onion" + "https://www.theonion.com/rss" + Social) + ("Protesilaos Stavrou" + "https://www.youtube.com/@protesilaos" + YouTube Technology) + ("Tsoding Daily" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" + YouTube Technology) + ("Tsoding" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" + YouTube Technology) + ("Nexpo" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw" + YouTube Stories) + ("3B1B" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw" + YouTube) + ("Fredrik Knusden" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w" + YouTube Stories) + ("Barely Sociable" + "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw" + YouTube Stories) + ("Atrocity Guide" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw" + YouTube Stories) + ("Hacker News" + "https://news.ycombinator.com/rss" + Social News Technology) + ("Hacker Factor" + "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2" + Social))) + :config + (with-eval-after-load "evil-collection" + (evil-collection-elfeed-setup)) + + (setq elfeed-feeds (cl-map 'list #'(lambda (item) + (append (list (nth 1 item)) (cdr (cdr item)))) + +rss/feed-urls)) + + (advice-add 'elfeed-search-show-entry :after #'+elfeed/dispatch-entry) + + (defun +elfeed/dispatch-entry (entry) + "Process each type of entry differently. + e.g., you may want to open HN entries in eww." + (let ((url (elfeed-entry-link entry))) + (pcase url + ((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch")) + (mpv-play-url url)) + (_ (eww url)))))) +#+end_src +** IBuffer +IBuffer is the dired of buffers. Nothing much else to be said. + +#+begin_src emacs-lisp +(use-package ibuffer + :defer t + :general + (buffer-leader + "i" #'ibuffer) + :config + (with-eval-after-load "evil-collection" + (evil-collection-ibuffer-setup))) +#+end_src +** Proced +Emacs has two systems for process management: ++ proced: a general 'top' like interface which allows general + management of linux processes ++ list-processes: a specific Emacs based system that lists processes + spawned by Emacs (similar to a top for Emacs specifically) + +Core Proced config, just a few bindings and evil collection setup. + +#+begin_src emacs-lisp +(use-package proced + :defer t + :general + (app-leader + "p" #'proced) + (nmap + :keymaps 'proced-mode-map + "za" #'proced-toggle-auto-update) + :display + ("\\*Proced\\*" + (display-buffer-at-bottom) + (window-height . 0.25)) + :init + (setq proced-auto-update-interval 5) + :config + (with-eval-after-load "evil-collection" + (evil-collection-proced-setup))) +#+end_src +** Calculator +~calc-mode~ is a calculator system within Emacs that provides a +diverse array of mathematical operations. It uses reverse polish +notation, but there is a standard infix algebraic notation mode so +don't be too shocked. It can do a surprising amount of stuff, such +as: ++ finding derivatives/integrals of generic equations ++ matrix operations ++ finding solutions for equations, such as for finite degree multi + variable polynomials + +Perhaps most powerful is ~embedded-mode~. This allows one to perform +computation within a non ~calc-mode~ buffer. Surround any equation +with dollar signs and call ~(calc-embedded)~ with your cursor on it to +compute it. It'll replace the equation with the result it computed. +This is obviously incredibly useful; I don't even need to leave the +current buffer to perform some quick mathematics in it. + +#+begin_src emacs-lisp +(use-package calc + :defer t + :display + ("*Calculator*" + (display-buffer-at-bottom) + (window-height . 0.2)) + :general + (app-leader + "c" #'calc-dispatch) + :init + (setq calc-algebraic-mode t) + :config + (with-eval-after-load "evil-collection" + (evil-collection-calc-setup))) +#+end_src +** Zone +Emacs' out of the box screensaver software. + +#+begin_src emacs-lisp +(use-package zone + :defer t + :commands (zone) + :general + (leader + "z" #'zone) + :init + + (setq zone-programs + [zone-pgm-drip + zone-pgm-drip-fretfully])) +#+end_src +** (Wo)man +Man pages are the user manuals for most software on Linux. Of course, +Emacs comes out of the box with a renderer for man pages and some +searching capabilities. + +2023-08-17: `Man-notify-method' is the reason the `:display' record +doesn't work here. I think it's to do with how Man pages are rendered +or something, but very annoying as it's a break from standards! + +2024-10-08: Man pages are rendered via a separate process, which is +why this is necessary. + +#+begin_src emacs-lisp +(use-package man + :defer t + :init + (setq Man-notify-method 'thrifty) + :display + ("\\*Man.*" + (display-buffer-reuse-mode-window display-buffer-same-window) + (mode . Man-mode)) + :general + (file-leader + "m" #'man) ;; kinda like "find man page" + (nmmap + :keymaps 'Man-mode-map + "RET" #'man-follow)) +#+end_src +** Info +Info is GNU's attempt at better man pages. Most Emacs packages have +info pages so I'd like nice navigation options. + +#+begin_src emacs-lisp +(use-package info + :defer t + :general + (nmmap + :keymaps 'Info-mode-map + "h" #'evil-backward-char + "k" #'evil-previous-line + "l" #'evil-forward-char + "H" #'Info-history-back + "L" #'Info-history-forward + "C-j" #'Info-forward-node + "C-k" #'Info-backward-node + "RET" #'Info-follow-nearest-node + "m" #'Info-menu + "C-o" #'Info-history-back + "s" #'Info-search + "S" #'Info-search-case-sensitively + "i" #'Info-index + "a" #'info-apropos + "gj" #'Info-next + "gk" #'Info-prev + "g?" #'Info-summary + "q" #'quit-window) + :init + (with-eval-after-load "evil" + (evil-set-initial-state 'Info-mode 'normal))) +#+end_src +** WAIT gif-screencast +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +Little application that uses =gifsicle= to make essentially videos of +Emacs. Useful for demonstrating features. + +#+begin_src emacs-lisp +(use-package gif-screencast + :straight t + :general + (app-leader + "x" #'gif-screencast-start-or-stop) + :init + (setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/"))) +#+end_src +** Image-mode +Image mode, for viewing images. Supports tons of formats, easy to use +and integrates slickly into image-dired. Of course, + +#+begin_src emacs-lisp +(use-package image-mode + :defer t + :general + (nmmap + :keymaps 'image-mode-map + "+" #'image-increase-size + "-" #'image-decrease-size + "a" #'image-toggle-animation + "p+" #'image-increase-speed + "p-" #'image-increase-speed + "h" #'image-backward-hscroll + "j" #'image-next-line + "k" #'image-previous-line + "l" #'image-forward-hscroll)) +#+end_src +** empv +Emacs MPV bindings, with very cool controls for queuing files for +playing. +#+begin_src emacs-lisp +(use-package empv + :straight t + :defer t + :init + (setq empv-audio-dir (expand-file-name "~/Media/audio") + empv-video-dir (expand-file-name "~/Media/videos") + empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" + "flac" "aac" "opus") + empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" + "webm")) + :hydra + (empv-hydra + nil "Hydra for EMPV" + ("(" #'empv-chapter-prev + "chapter-prev" :column "playback") + (")" #'empv-chapter-next + "chapter-next" :column "playback") + ("0" #'empv-volume-up + "volume-up" :column "playback") + ("9" #'empv-volume-down + "volume-down" :column "playback") + ("[" #'empv-playback-speed-down + "playback-speed-down" :column "playback") + ("]" #'empv-playback-speed-up + "playback-speed-up" :column "playback") + ("_" #'empv-toggle-video + "toggle-video" :column "playback") + ("q" #'empv-exit + "exit" :column "playback") + ("s" #'empv-seek + "seek" :column "playback") + ("t" #'empv-toggle + "toggle" :column "playback") + ("x" #'empv-chapter-select + "chapter-select" :column "playback") + ("N" #'empv-playlist-prev + "playlist-prev" :column "playlist") + ("C" #'empv-playlist-clear + "playlist-clear" :column "playlist") + ("n" #'empv-playlist-next + "playlist-next" :column "playlist") + ("p" #'empv-playlist-select + "playlist-select" :column "playlist") + ("S" #'empv-playlist-shuffle + "playlist-shuffle" :column "playlist") + ("a" #'empv-play-audio + "play-audio" :column "play") + ("R" #'empv-play-random-channel + "play-random-channel" :column "play") + ("d" #'empv-play-directory + "play-directory" :column "play") + ("f" #'empv-play-file + "play-file" :column "play") + ("o" #'empv-play-or-enqueue + "play-or-enqueue" :column "play") + ("r" #'empv-play-radio + "play-radio" :column "play") + ("v" #'empv-play-video + "play-video" :column "play") + ("i" #'empv-display-current + "display-current" :column "misc") + ("l" #'empv-log-current-radio-song-name + "log-current-radio-song-name" :column "misc") + ("c" #'empv-copy-path + "copy-path" :column "misc") + ("Y" #'empv-youtube-last-results + "youtube-last-results" :column "misc") + ("y" #'empv-youtube + "youtube" :column "misc")) + :general + (app-leader + "e" #'empv-hydra/body)) +#+end_src +** Grand Unified Debugger (GUD) +GUD is a system for debugging, hooking into processes and +providing an interface to the user all in Emacs. Here I define a +hydra which provides a ton of the useful =gud= keybindings that exist +in an Emacs-only map. +#+begin_src emacs-lisp +(use-package gud + :general + :after hydra + :hydra + (gud-hydra + (:hint nil) "Hydra for GUD" + ("<" #'gud-up "Up" + :column "Control Flow") + (">" #'gud-down "Down" + :column "Control Flow") + ("b" #'gud-break "Break" + :column "Breakpoints") + ("d" #'gud-remove "Remove" + :column "Breakpoints") + ("f" #'gud-finish "Finish" + :column "Control Flow") + ("J" #'gud-jump "Jump" + :column "Control Flow") + ("L" #'gud-refresh "Refresh" + :column "Misc") + ("n" #'gud-next "Next" + :column "Control Flow") + ("p" #'gud-print "Print" + :column "Misc") + ("c" #'gud-cont "Cont" + :column "Control Flow") + ("s" #'gud-step "Step" + :column "Control Flow") + ("t" #'gud-tbreak "Tbreak" + :column "Control Flow") + ("u" #'gud-until "Until" + :column "Control Flow") + ("v" #'gud-go "Go" + :column "Control Flow") + ("w" #'gud-watch "Watch" + :column "Breakpoints") + ("TAB" #'gud-stepi "Stepi" + :column "Control Flow")) + :general + (code-leader "d" #'gud-hydra/body + "D" #'gud-gdb)) +#+end_src +** WAIT esup +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +I used to be able to just use +[[file:elisp/profiler-dotemacs.el][profile-dotemacs.el]], when my +Emacs config was smaller, but now it tells me very little information +about where my setup is inefficient due to the literate config. Just +found this ~esup~ thing and it works perfectly, exactly how I would +prefer getting this kind of information. It runs an external Emacs +instance and collects information from it, so it doesn't require +restarting Emacs to profile, and I can compile my configuration in my +current instance to test it immediately. + +2023-10-16: Unless I'm doing some optimisations or tests, I don't +really need this in my config at all times. Enable when needed. + +#+begin_src emacs-lisp +(use-package esup + :straight t + :defer t + :general + (leader + "qe" #'esup)) +#+end_src +* Miscellaneous +** Evil additions +Additional packages that add the functionality of plugins in Vim I +really liked, as well as some new stuff. +*** Evil surround +A port for vim-surround, providing the ability to mutate delimiters +around some text. + +#+begin_src emacs-lisp +(use-package evil-surround + :after evil + :straight t + :config + (global-evil-surround-mode)) +#+end_src +*** Evil commentary +A port of vim-commentary, providing generalised commenting of objects. + +#+begin_src emacs-lisp +(use-package evil-commentary + :after evil + :straight t + :config + (evil-commentary-mode)) +#+end_src +*** Evil multi cursor +Setup for multi cursors in Evil mode, which is a bit of very nice +functionality. Don't let evil-mc setup it's own keymap because it +uses 'gr' as its prefix, which I don't like. + +#+begin_src emacs-lisp +(use-package evil-mc + :after evil + :straight t + :init + (defvar evil-mc-key-map (make-sparse-keymap)) + :general + (nmap + :infix "gz" + "m" 'evil-mc-make-all-cursors + "u" 'evil-mc-undo-last-added-cursor + "q" 'evil-mc-undo-all-cursors + "s" 'evil-mc-pause-cursors + "r" 'evil-mc-resume-cursors + "f" 'evil-mc-make-and-goto-first-cursor + "l" 'evil-mc-make-and-goto-last-cursor + "h" 'evil-mc-make-cursor-here + "j" 'evil-mc-make-cursor-move-next-line + "k" 'evil-mc-make-cursor-move-prev-line + "N" 'evil-mc-skip-and-goto-next-cursor + "P" 'evil-mc-skip-and-goto-prev-cursor + "n" 'evil-mc-skip-and-goto-next-match + "p" 'evil-mc-skip-and-goto-prev-match + "I" 'evil-mc-make-cursor-in-visual-selection-beg + "A" 'evil-mc-make-cursor-in-visual-selection-end + "d" #'evil-mc-make-and-goto-next-match) + :config + (global-evil-mc-mode)) +#+end_src +*** Evil multi edit +Evil-ME provides a simpler parallel editing experience within the same +buffer. I use it in-tandem with Evil-MC, where I use Evil-ME for +textual changes and Evil-MC for more complex motions. + +#+begin_src emacs-lisp +(use-package evil-multiedit + :straight t + :defer t + :init + (setq evil-multiedit-scope 'visible) + :general + (:states '(normal visual) + :keymaps 'override + "M-e" #'evil-multiedit-match-and-next + "M-E" #'evil-multiedit-match-and-prev)) +#+end_src +*** Evil collection +Provides a community based set of keybindings for most modes in +Emacs. I don't necessarily like all my modes having these bindings +though, as I may disagree with some. So I use it in a mode to mode basis. + +#+begin_src emacs-lisp +(use-package evil-collection + :straight t + :after evil) +#+end_src +*** Evil number +Increment/decrement a number at point like Vim does, but use bindings +that don't conflict with Emacs default. + +#+begin_src emacs-lisp +(use-package evil-numbers + :straight t + :defer t + :general + (nmmap + "+" #'evil-numbers/inc-at-pt + "-" #'evil-numbers/dec-at-pt)) +#+end_src +*** Evil goggles +Make it easier to notice edits and changes using Vim motions! +#+begin_src emacs-lisp +(use-package evil-goggles + :straight t + :after evil + :init + (setq evil-goggles-duration 0.1 + evil-goggles-blocking-duration 0.1 + evil-goggles-async-duration 0.9 + evil-goggles-default-face 'pulsar-cyan) + :config + (evil-goggles-mode) + (evil-goggles-use-diff-faces)) +#+end_src +** Save place +Saves current place in a buffer permanently, so on revisiting the file +(even in a different Emacs instance) you go back to the place you were +at last. + +#+begin_src emacs-lisp +(use-package saveplace + :demand t + :config + (save-place-mode)) +#+end_src +** Tabs +Tabs in vscode are just like buffers in Emacs but way slower and +harder to use. Tabs in Emacs are essentially window layouts, similar +to instances in Tmux. With this setup I can use tabs quite +effectively. + +#+begin_src emacs-lisp +(use-package tab-bar + :defer t + :hook (after-init-hook . tab-bar-mode) + :init + (setq tab-bar-close-button-show nil + tab-bar-format '(tab-bar-format-history + tab-bar-format-tabs tab-bar-separator) + tab-bar-show 1 + tab-bar-auto-width t + tab-bar-auto-width-max '((100) 20) + tab-bar-auto-width-min '((20) 2)) + :general + (tab-leader + "R" #'tab-rename + "c" #'tab-close + "d" #'tab-close + "f" #'tab-detach + "h" #'tab-move-to + "j" #'tab-next + "k" #'tab-previous + "l" #'tab-move + "n" #'tab-new + "r" #'tab-switch + "w" #'tab-window-detach)) +#+end_src +** Registers +Emacs comes by default with the notion of "registers". Registers are +essentially an alist of symbols mapped to some Lisp object, which +can be easily accessed and manipulated. Some common use cases of +registers are: ++ Marking locations in a file to quickly go to (using Emacs' in-built + notion of marks) ++ Copying and pasting text without the clipboard (essentially even + more clipboards) ++ Creating number counters (usually for macros) + +Of course, Vim has its own notion of registers which are frankly much +less capable than Emacs. Evil emulates this limited notion of +registers, but I prefer Emacs' hence the configuration here. + +#+begin_src emacs-lisp +(use-package register + :config + (defmacro +register/jump-to (reg) + `(proc (interactive) + (jump-to-register ,reg))) + :general + (nmmap + "m" #'point-to-register + "'" #'jump-to-register + "g1" (+register/jump-to ?1) + "g2" (+register/jump-to ?2) + "g3" (+register/jump-to ?3) + "g4" (+register/jump-to ?4) + "g5" (+register/jump-to ?5) + "g6" (+register/jump-to ?6) + "g7" (+register/jump-to ?7) + "g8" (+register/jump-to ?8) + "g9" (+register/jump-to ?9))) +#+end_src +** Recentf +Recentf provides a method of keeping track of recently opened files. + +#+begin_src emacs-lisp +(use-package recentf + :defer t + :hook (after-init-hook . recentf-mode) + :general + (file-leader + "r" #'recentf)) +#+end_src +** Memory-report +New feature of Emacs-29, gives a rough report of memory usage with +some details. Useful to know on a long Emacs instance what could be +eating up memory. + +#+begin_src emacs-lisp +(use-package memory-report + :defer t + :general + (leader + "qm" #'memory-report)) +#+end_src +** Helpful +Helpful provides a modern interface for some common help commands. I +replace ~describe-function~, ~describe-variable~ and ~describe-key~ by +their helpful counterparts. + +#+begin_src emacs-lisp +(use-package helpful + :straight t + :defer t + :commands (helpful-callable helpful-variable) + :general + ([remap describe-function] #'helpful-callable + [remap describe-variable] #'helpful-variable + [remap describe-key] #'helpful-key) + :display + ("\\*helpful.*" + (display-buffer-at-bottom) + (inhibit-duplicate-buffer . t) + (window-height . 0.25)) + :config + (evil-define-key 'normal helpful-mode-map "q" #'quit-window)) +#+end_src +** Avy and Ace +Avy is a package that provides "jump" functions. Given some input, +the current buffer is scanned and any matching candidates are given a +tag which the user can input to perform some action (usually moving +the cursor to that point). +*** Avy core +Setup avy with leader. As I use ~avy-goto-char-timer~ a lot, use the +~C-s~ bind which replaces isearch. Switch isearch to M-s in case I +need to use it. + +#+begin_src emacs-lisp +(use-package avy + :straight t + :defer t + :general + (nmmap + :keymaps 'override + "C-s" #'avy-goto-char-timer + "M-s" #'isearch-forward + "gp" #'avy-copy-region + "gP" #'avy-move-region + "gl" #'avy-goto-line + "gw" #'avy-goto-word-1)) +#+end_src +*** Ace window +Though evil provides a great many features in terms of window +management, ace window can provide some nicer chords for higher +management of windows (closing, switching, etc). + +#+begin_src emacs-lisp +(use-package ace-window + :straight t + :defer t + :custom + (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) + :general + (nmmap + [remap evil-window-next] #'ace-window)) +#+end_src +*** Ace link +Avy-style link following! + +#+begin_src emacs-lisp +(use-package ace-link + :straight t + :defer t + :general + (nmmap + :keymaps 'override + "gL" #'ace-link)) +#+end_src +** Drag Stuff +Drag stuff around, like my favourite russian programmer (Tsoding). +Useful mechanism which works better than any vim motion. + +#+begin_src emacs-lisp +(use-package drag-stuff + :straight t + :defer t + :general + (nmmap + "C-M-h" #'drag-stuff-left + "C-M-j" #'drag-stuff-down + "C-M-k" #'drag-stuff-up + "C-M-l" #'drag-stuff-right)) +#+end_src +** Separedit +Edit anything anywhere all at once! + +#+begin_src emacs-lisp +(use-package separedit + :defer t + :straight t + :general + (leader "e" #'separedit) + :init + (setq separedit-default-mode 'org-mode + separedit-remove-trailing-spaces-in-comment t)) +#+end_src +** Undo tree +Undo tree sits on top of Emacs' undo capabilities. It provides a nice +visual for the history of a buffer and is a great way to produce +branches of edits. This history may be saved to and loaded from the +disk, which makes Emacs a quasi version control system in and of +itself. The only feature left is describing changes... + +#+begin_src emacs-lisp +(use-package undo-tree + :demand t + :straight t + :general + (leader + "u" #'undo-tree-visualize) + :init + (setq undo-tree-auto-save-history t + undo-tree-history-directory-alist backup-directory-alist) + :config + (global-undo-tree-mode)) +#+end_src +** Searching common directories +Using [[file:elisp/search.el][search.el]] I can search a set of +directories particularly efficiently. + +#+begin_src emacs-lisp +(use-package search + :defer t + :load-path "elisp/" + :general + (search-leader + "a" #'+search/search-all) + (file-leader + "p" #'+search/find-file)) +#+end_src +** Abbrevs +Just define a few abbrevs for various date-time operations. Also +define a macro that will assume a function for the expansion, helping +with abstracting a few things away. + +#+begin_src emacs-lisp +(use-package abbrev + :defer t + :hook + (prog-mode-hook . abbrev-mode) + (text-mode-hook . abbrev-mode) + :init + (defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs) + `(progn + ,@(mapcar #'(lambda (abbrev) + `(define-abbrev + ,abbrev-table + ,(car abbrev) + "" + (proc (insert ,(cadr abbrev))))) + abbrevs))) + (setq save-abbrevs nil) + :config + (+abbrev/define-abbrevs + global-abbrev-table + ("sdate" + (format-time-string "%Y-%m-%d" (current-time))) + ("stime" + (format-time-string "%H:%M:%S" (current-time))) + ("sday" + (format-time-string "%A" (current-time))) + ("smon" + (format-time-string "%B" (current-time))))) +#+end_src +** Yasnippet +Look at the snippets [[file:../.config/yasnippet/snippets/][folder]] +for all snippets I've got. + +#+begin_src emacs-lisp +(use-package yasnippet + :straight t + :defer t + :hook + (prog-mode-hook . yas-minor-mode) + (text-mode-hook . yas-minor-mode) + :general + (insert-leader + "i" #'yas-insert-snippet) + :config + (yas-load-directory (no-littering-expand-etc-file-name + "yasnippet/snippets"))) +#+end_src +** Amx +Amx is a fork of Smex that works to enhance the +~execute-extended-command~ interface. It provides a lot of niceties +such as presenting the key bind when looking for a command. + +#+begin_src emacs-lisp +(use-package amx + :straight t + :demand t + :init + (setq amx-backend 'auto) + :config + (amx-mode)) +#+end_src |