From 46237d64785552ee19f6c8a4df36935c0f76d1ae Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Wed, 28 May 2025 00:10:50 +0100 Subject: Update config --- Emacs/.config/emacs/config.org | 3407 ++++++++++++++++++++-------------------- 1 file changed, 1715 insertions(+), 1692 deletions(-) diff --git a/Emacs/.config/emacs/config.org b/Emacs/.config/emacs/config.org index b1c2763..04c6ac7 100644 --- a/Emacs/.config/emacs/config.org +++ b/Emacs/.config/emacs/config.org @@ -263,15 +263,17 @@ also added to `enable-theme-functions` such that loading a theme will forcefully adjust the font size. #+begin_src emacs-lisp +(defun +oreo/--font-multiplier () + (pcase (display-pixel-width) + ((pred (>= 1920)) 0.9) + ((pred (>= 2560)) 1.24))) + (defun +oreo/font-reset (&optional theme) (let ((font-size (thread-first (pcase (system-name) ("rhmaiden" 140) (_ 120)) - (* - (pcase (display-pixel-width) - ((pred (>= 1920)) 0.90) - ((pred (>= 2560)) 1.24))) + (* (+oreo/--font-multiplier)) floor))) (set-face-attribute 'default nil :height font-size) (set-face-attribute 'mode-line nil :height font-size))) @@ -884,6 +886,49 @@ the keyword ~:hydra~ in use-package declarations. :general (leader "wr" #'evil-window-resize-hydra/body)) #+end_src +** Project.el +An out of the box system for managing projects. Where possible we +should try to use Emacs defaults, so when setting up on a new computer +it takes a bit less time. + +Here I: ++ Bind ~project-prefix-map~ to "p" ++ Bind a tags generation command to "pr" + + mimics projectile's one, so I can quickly generate them. + + mimicking + +#+begin_src emacs-lisp +(use-package project + :straight t + :defer t + :general + (:keymaps 'project-prefix-map + "r" #'+project/generate-tags) + (leader + "p" project-prefix-map) + :config + (setq project-vc-extra-root-markers '(".project")) + (defun +project/command (folder) + (format "ctags -Re -f %sTAGS %s*" + folder folder)) + + (defun +project/root () + (if (project-current) + (project-root (project-current)) + default-directory)) + + (defun +project/generate-tags () + (interactive) + (set-process-sentinel + (start-process-shell-command + "PROJECT-GENERATE-TAGS" + "*tags*" + (+project/command (+project/root))) + (lambda (p event) + (when (string= event "finished\n") + (message "Finished generating tags!") + (visit-tags-table (format "%sTAGS" (+project/root)))))))) +#+end_src * Aesthetics General look and feel of Emacs, perhaps the most important of all the sections here. @@ -957,10 +1002,10 @@ fundamental mode and call it a day. (proc (with-current-buffer "*scratch*" (goto-char (point-max)) - (--> - (emacs-init-time) - (format "Emacs v%s - %s\n" emacs-version it) - (insert it)))))) + (thread-last + (emacs-init-time) + (format "Emacs v%s - %s\n" emacs-version) + (insert)))))) #+end_src ** Blinking cursor Configure the blinking cursor. @@ -1267,1955 +1312,1930 @@ any prog language of choice. Mostly for reference and copying. ("<=" . "≤") (">=" . "≥") #+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 -also need it in commit messages and so on, thus it should really hook -into text-mode. - -#+begin_src emacs-lisp -(use-package flyspell - :defer t - :hook ((org-mode-hook text-mode-hook) . flyspell-mode) - :general - (nmmap - :keymaps 'text-mode-map - "M-C" #'flyspell-correct-word-before-point - "M-c" #'flyspell-auto-correct-word) - (mode-leader - "s" #'flyspell-mode)) -#+end_src -** Whitespace -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. +* Applications +Emacs is an operating system, now with a good text editor through +[[*Evil - Vim emulation][Evil]]. Let's configure some apps for it. +** Magit +Magit is *the* git porcelain for Emacs, which perfectly encapsulates +the git CLI. It's so good that some people use Emacs just for it. +It's another one of those "so indescribably good you have to try it" +things. I've hardly touched the Git CLI since getting Magit, and it +has actively taught me _new_ things about Git. -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. +In this case I just need to setup the bindings for it. #+begin_src emacs-lisp -(use-package whitespace +(use-package transient + :straight t) + +(use-package magit + :straight t :defer t + :display + ("magit:.*" + (display-buffer-same-window) + (inhibit-duplicate-buffer . t)) + ("magit-diff:.*" + (display-buffer-below-selected)) + ("magit-log:.*" + (display-buffer-same-window)) + ("magit-revision:.*" + (display-buffer-below-selected) + (inhibit-duplicate-buffer . t)) :general - (nmmap - "M--" #'whitespace-cleanup) - (mode-leader - "w" #'whitespace-mode) - :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 - nasm-mode-hook) - . whitespace-mode) + (leader + "g" #'magit-dispatch) + (code-leader + "b" #'magit-blame) + (nmap + :keymaps 'magit-status-mode-map + "M-j" #'magit-section-forward-sibling + "M-k" #'magit-section-backward-sibling) :init - (setq whitespace-line-column nil - whitespace-style '(face empty spaces tabs newline trailing - lines-char tab-mark))) + (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 -** Filling and displaying fills -The fill-column is the number of characters that should be in a single -line of text before doing a hard wrap. The default case is 80 -characters for that l33t Unix hard terminal character limit. I like -different fill-columns for different modes: text modes should really -use 70 fill columns while code should stick to 80. - +*** Magit Forge +Imagine being able to do all the bureaucratic nonsense involved on +GitHub i.e. pull requests, issue handling, etc. all through Emacs! No +need to imagine any more, with Magit Forge. #+begin_src emacs-lisp -(use-package emacs - :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) +(use-package forge + :after magit + :straight t :init - (setq-default fill-column 80) - (add-hook 'text-mode-hook (proc (setq-local fill-column 70)))) -#+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 + (setq forge-add-default-bindings nil) :config - (global-visual-line-mode t)) -#+end_src -** Show-paren-mode -When the cursor is over a parenthesis, highlight the other member of -the pair. - -#+begin_src emacs-lisp -(use-package paren - :hook (prog-mode-hook . show-paren-mode)) + (with-eval-after-load "evil-collection" + (evil-collection-forge-setup))) #+end_src -** Smartparens -Smartparens is a smarter electric-parens, it's much more aware of -context and easier to use. +** 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 smartparens - :straight t +(use-package eww :defer t - :hook - (prog-mode-hook . smartparens-mode) - (text-mode-hook . smartparens-mode) + :general + (app-leader + "w" #'eww) + (nmmap + :keymaps 'eww-mode-map + "w" #'evil-forward-word-begin + "Y" #'eww-copy-page-url) :config - (setq sp-highlight-pair-overlay nil - sp-highlight-wrap-overlay t - sp-highlight-wrap-tag-overlay t) - - (let ((unless-list '(sp-point-before-word-p - sp-point-after-word-p - sp-point-before-same-p))) - (sp-pair "'" nil :unless unless-list) - (sp-pair "\"" nil :unless unless-list)) - (sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p)) - (require 'smartparens-config)) + (with-eval-after-load "evil-collection" + (evil-collection-eww-setup))) #+end_src -** Powerthesaurus -Modern package for thesaurus in Emacs with a transient + hydra. +** 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 powerthesaurus +(use-package calendar :defer t - :straight t + :commands (+calendar/copy-date +calendar/toggle-calendar) + :display + ("\\*Calendar\\*" + (display-buffer-at-bottom) + (inhibit-duplicate-buffer . t) + (window-height . 0.17)) :general - (search-leader - "w" #'powerthesaurus-transient)) + (nmmap + :keymaps 'calendar-mode-map + "Y" #'+calendar/copy-date) + (app-leader + "d" #'calendar) + :config + (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)))))) + (with-eval-after-load "evil-collection" + (evil-collection-calendar-setup))) #+end_src -** lorem ipsum -Sometimes you need placeholder text for some UI or document. Pretty -easy to guess what text I'd use. +** 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 lorem-ipsum - :straight t - :general - (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. -** Eldoc -Eldoc presents documentation to the user upon placing ones cursor upon -any symbol. This is very useful when programming as it: -- presents the arguments of functions while writing calls for them -- presents typing and documentation of variables - -Eldoc box makes the help buffer a hovering box instead of printing it -in the minibuffer. A lot cleaner. - -2024-05-31: Eldoc box is a bit useless now that I'm not using frames. -I prefer the use of the minibuffer for printing documentation now. - -#+begin_src emacs-lisp -(use-package eldoc - :defer t - :hook (prog-mode-hook . eldoc-mode) - :init - (global-eldoc-mode 1) - :general - (leader - "h>" #'eldoc-doc-buffer)) -#+end_src -** Flycheck -Flycheck is the checking system for Emacs. I don't necessarily like -having all my code checked all the time, so I haven't added a hook to -prog-mode as it would be better for me to decide when I want checking -and when I don't. Many times Flycheck is annoying when checking a -program, particularly one which isn't finished yet. - -#+begin_src emacs-lisp -(use-package flycheck +(use-package notmuch :straight t :defer t - :commands (flycheck-mode flycheck-list-errors) + :commands (notmuch +mail/flag-thread) :general - (mode-leader - "f" #'flycheck-mode) - (code-leader - "x" #'flycheck-list-errors - "j" #'flycheck-next-error - "k" #'flycheck-previous-error) - :display - ("\\*Flycheck.*" - (display-buffer-at-bottom) - (window-height . 0.25)) + (app-leader "m" #'notmuch) + (nmap + :keymaps 'notmuch-search-mode-map + "f" #'+mail/flag-thread) :init - (setq-default flycheck-check-syntax-automatically - '(save idle-change mode-enabled) - flycheck-idle-change-delay 1.0 - flycheck-buffer-switch-check-intermediate-buffers t - flycheck-display-errors-delay 0.25) + (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) :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))) + (with-eval-after-load "evil-collection" - (evil-collection-flycheck-setup))) + (evil-collection-notmuch-setup))) #+end_src -** Eglot -Eglot is package to communicate with LSP servers for better -programming capabilities. Interactions with a server provide results -to the client, done through JSON. - -NOTE: Emacs 28.1 comes with better JSON parsing, which makes Eglot -much faster. - -2023-03-26: I've found Eglot to be useful sometimes, but many of the -projects I work on don't require a heavy server setup to efficiently -edit and check for errors; Emacs provides a lot of functionality. So -by default I've disabled it, using =M-x eglot= to startup the LSP -server when I need it. - -2024-06-27: In projects where I do use eglot and I know I will need it -regardless of file choice, I prefer setting it at the dir-local level -via an eval form. So I add to the safe values for the eval variable -to be set. +*** 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 eglot +(use-package smtpmail :defer t - :general - (code-leader - :keymaps 'eglot-mode-map - "f" #'eglot-format - "a" #'eglot-code-actions - "R" #'eglot-reconnect) + :commands mail-send :init - (setq eglot-auto-shutdown t - eglot-stay-out-of '(flymake) - eglot-ignored-server-capabilities '(:documentHighlightProvider - :documentOnTypeFormattingProvider - :inlayHintProvider)) - (add-to-list 'safe-local-variable-values '(eval eglot-ensure))) -#+end_src -** Indentation -By default, turn off tabs and set the tab width to two. - -#+begin_src emacs-lisp -(setq-default indent-tabs-mode nil - tab-width 2) -#+end_src - -However, if necessary later, define a function that may activate tabs locally. -#+begin_src emacs-lisp -(defun +oreo/use-tabs () - (interactive) - (setq-local indent-tabs-mode t)) + (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 -** Highlight todo items -TODO items are highlighted in org-mode, but not necessarily in every -mode. This minor mode highlights all TODO like items via a list of -strings to match. It also configures faces to use when highlighting. -I hook it to prog-mode. +*** Mail signature using fortune +Generate a mail signature using the ~fortune~ executable. Pretty +cool! #+begin_src emacs-lisp -(use-package hl-todo - :straight t - :after prog-mode - :hook (prog-mode-hook . hl-todo-mode) +(use-package fortune + :after message :init - (setq hl-todo-keyword-faces - '(("TODO" . "#E50000") - ("WIP" . "#ffa500") - ("NOTE" . "#00CC00") - ("FIXME" . "#d02090")))) -#+end_src -** Hide-show mode -Turn on ~hs-minor-mode~ for all prog-mode. This provides folds for -free. - -#+begin_src emacs-lisp -(use-package hideshow - :defer t - :hook (prog-mode-hook . hs-minor-mode)) -#+end_src -** Aggressive indenting -Essentially my dream editing experience: when I type stuff in, try and -indent it for me on the fly. Just checkout the -[[https://github.com/Malabarba/aggressive-indent-mode][page]], any -description I give won't do it justice. - -#+begin_src emacs-lisp -(use-package aggressive-indent - :straight t - :hook (emacs-lisp-mode-hook . aggressive-indent-mode) - :hook (scheme-mode-hook . aggressive-indent-mode) - :hook (lisp-mode-hook . aggressive-indent-mode)) + (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 -** Compilation -Compilation mode is an incredibly useful subsystem of Emacs which -allows one to run arbitrary commands. If those commands produce -errors (particularly errors that have a filename, column and line) -compilation-mode can colourise these errors and help you navigate to -them. - -Here I add some bindings and a filter which colourises the output of -compilation mode for ANSI escape sequences; the eyecandy is certainly -nice but it's very useful when dealing with tools that use those codes -so you can actually read the text. +** 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. +*** Dired Core +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 compile +(use-package dired :defer t - :display - ("\\*compilation\\*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.3) - (reusable-frames . t)) - :hydra - (move-error-hydra - (:hint nil) "Hydra for moving between errors" - ("j" #'next-error) - ("k" #'previous-error)) - :general - (leader - "j" #'move-error-hydra/next-error - "k" #'move-error-hydra/previous-error) - (code-leader - "c" #'compile - "r" #'recompile) - (nmap - "M-r" #'recompile) - (:keymaps 'compilation-mode-map - "g" nil ;; by default this is recompile - "M-j" #'compilation-next-error - "M-k" #'compilation-previous-error) - (nmmap - :keymaps 'compilation-mode-map - "c" #'recompile) + :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 compilation-scroll-output 'first-error - compilation-context-lines nil - next-error-highlight 'fringe-arrow) + (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 + dired-auto-revert-buffer t) + :general + (nmmap + :keymaps 'dired-mode-map + "SPC" nil + "SPC ," nil + "M-k" #'dired-prev-subdir + "M-j" #'dired-next-subdir + "q" #'quit-window + "j" #'dired-next-line + "k" #'dired-previous-line + "(" #'dired-hide-details-mode + ")" #'dired-omit-mode + "T" #'dired-create-empty-file + "H" #'dired-up-directory + "L" #'dired-find-file + "#" #'dired-flag-auto-save-files + "." #'dired-clean-directory + "~" #'dired-flag-backup-files + "A" #'dired-do-find-regexp + "C" #'dired-do-copy + "B" #'dired-do-byte-compile + "D" #'dired-do-delete + "M" #'dired-do-chmod + "O" #'dired-do-chown + "P" #'dired-do-print + "Q" #'dired-do-find-regexp-and-replace + "R" #'dired-do-rename + "S" #'dired-do-symlink + "T" #'dired-do-touch + "X" #'dired-do-shell-command + "Z" #'dired-do-compress + "c" #'dired-do-compress-to + "!" #'dired-do-shell-command + "&" #'dired-do-async-shell-command + "{" #'dired-prev-marked-file + "}" #'dired-next-marked-file + "%" nil + "%u" #'dired-upcase + "%l" #'dired-downcase + "%d" #'dired-flag-files-regexp + "%g" #'dired-mark-files-containing-regexp + "%m" #'dired-mark-files-regexp + "%r" #'dired-do-rename-regexp + "%C" #'dired-do-copy-regexp + "%H" #'dired-do-hardlink-regexp + "%R" #'dired-do-rename-regexp + "%S" #'dired-do-symlink-regexp + "%&" #'dired-flag-garbage-files + "*" nil + "**" #'dired-mark-executables + "*/" #'dired-mark-directories + "*@" #'dired-mark-symlinks + "*%" #'dired-mark-files-regexp + "*c" #'dired-change-marks + "*s" #'dired-mark-subdir-files + "*m" #'dired-mark + "*t" #'dired-toggle-marks + "*?" #'dired-unmark-all-files + "*!" #'dired-unmark-all-marks + "U" #'dired-unmark-all-marks + "a" #'dired-find-alternate-file + "d" #'dired-flag-file-deletion + "gf" #'browse-url-of-dired-file + "gr" #'revert-buffer + "i" #'dired-toggle-read-only + "J" #'dired-goto-file + "K" #'dired-do-kill-lines + "r" #'revert-buffer + "m" #'dired-mark + "t" #'dired-toggle-marks + "u" #'dired-unmark + "x" #'dired-do-flagged-delete + "gt" #'dired-show-file-type + "Y" #'dired-copy-filename-as-kill + "+" #'dired-create-directory + "RET" #'dired-find-file + "C-" #'dired-find-file-other-window + "o" #'dired-sort-toggle-or-edit + "[[" #'dired-prev-dirline + "]]" #'dired-next-dirline + [remap next-line] #'dired-next-line + [remap previous-line] #'dired-previous-line + "zt" #'dired-hide-subdir + "zC" #'dired-hide-all + [remap read-only-mode] #'dired-toggle-read-only + [remap toggle-read-only] #'dired-toggle-read-only + [remap undo] #'dired-undo + [remap advertised-undo] #'dired-undo) + (leader + "D" #'dired-jump) + (dir-leader + "f" #'find-dired + "d" #'dired + "D" #'dired-other-window + "i" #'image-dired + "b" (proc-int (find-file "~/Text/Books/"))) + (local-leader + :keymaps 'dired-mode-map + "i" #'dired-maybe-insert-subdir + "d" #'dired-goto-subdir + "I" #'+dired/insert-all-subdirectories + "o" #'dired-omit-mode + "K" #'dired-kill-subdir + "m" #'dired-mark-files-regexp + "u" #'dired-undo) :config - (add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)) + (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/--subdirs-not-inserted () + (dired-unmark-all-marks) + (dired-mark-directories nil) + (let* ((subdirs-inserted (mapcar #'car dired-subdir-alist)) + (subdirs-available (mapcar #'(lambda (x) (concat x "/")) + (dired-get-marked-files)))) + (dired-unmark-all-marks) + (cl-remove-if #'(lambda (f) (member f subdirs-inserted)) subdirs-available))) + + (defun +dired/insert-all-subdirectories (&optional arg) + "Insert all subdirectories recursively." + (interactive "P") + (let ((subdirs-left (+dired/--subdirs-not-inserted))) + (if (null arg) + (mapc #'dired-insert-subdir subdirs-left) + (while subdirs-left + (mapc #'dired-insert-subdir subdirs-left) + (setq subdirs-left (+dired/--subdirs-not-inserted))))))) #+end_src -** xref -Find definitions, references and general objects using tags without -external packages. Provided out of the box with Emacs, but requires a -way of generating a =TAGS= file for your project (look at -[[*Project.el][Project.el]] for my way of doing so). A critical -component in a minimal setup for programming without heavier systems -like [[*Eglot][Eglot]]. +*** 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 xref +(use-package dired :defer t - :display - ("\\*xref\\*" - (display-buffer-at-bottom) - (inhibit-duplicate-buffer . t) - (window-height . 0.3)) + :init + (setq image-dired-external-viewer "nsxiv") :general - (code-leader - "t" #'nil) - (code-leader - :infix "t" - "t" #'xref-find-apropos - "d" #'xref-find-definitions - "r" #'xref-find-references) (nmmap - :keymaps 'xref--xref-buffer-mode-map - "RET" #'xref-goto-xref - "J" #'xref-next-line - "K" #'xref-prev-line - "r" #'xref-query-replace-in-results - "gr" #'xref-revert-buffer - "q" #'quit-window)) + :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 -** Project.el -An out of the box system for managing projects. Where possible we -should try to use Emacs defaults, so when setting up on a new computer -it takes a bit less time. +*** fd-dired +Uses fd for finding file results in a directory: ~find-dired~ -> +~fd-dired~. -Here I: -+ Bind ~project-prefix-map~ to "p" -+ Bind a tags generation command to "pr" - + mimics projectile's one, so I can quickly generate them. - + mimicking +#+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 project - :defer t +(use-package wdired + :after dired + :hook (wdired-mode-hook . undo-tree-mode) :general - (:keymaps 'project-prefix-map - "r" #'+project/generate-tags) - (leader - "p" project-prefix-map) + (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 - (setq project-vc-extra-root-markers '(".project")) - (defun +project/command (folder) - (format "ctags -Re -f %sTAGS %s*" - folder folder)) - - (defun +project/root () - (if (project-current) - (project-root (project-current)) - default-directory)) - - (defun +project/generate-tags () - (interactive) - (set-process-sentinel - (start-process-shell-command - "PROJECT-GENERATE-TAGS" - "*tags*" - (+project/command (+project/root))) - (lambda (p event) - (when (string= event "finished\n") - (message "Finished generating tags!") - (visit-tags-table (format "%sTAGS" (+project/root)))))))) + (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 -** devdocs -When man pages aren't enough, you need some documentation lookup -system (basically whenever your using anything but C/C++/Bash). -[[https://devdocs.io][Devdocs]] is a great little website that -provides a ton of documentation sets. There's an Emacs package for it -which works well and downloads documentation sets to my machine, which -is nice. +*** 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 devdocs +(use-package dired-rsync :straight t - :defer t + :after dired :general - (file-leader - "d" #'devdocs-lookup)) + (nmmap + :keymaps 'dired-mode-map + "M-r" #'dired-rsync)) #+end_src -** rainbow-delimiters -Makes colours delimiters (parentheses) based on their depth in an -expression. Rainbow flag in your Lisp source code. +** 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 rainbow-delimiters +(use-package eshell :defer t - :straight t - :general - (mode-leader "r" #'rainbow-delimiters-mode) - :hook - ((lisp-mode-hook emacs-lisp-mode-hook racket-mode-hook) . rainbow-delimiters-mode)) + :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-int + (let ((buffer (current-buffer))) + (eshell/goto) + (with-current-buffer buffer + (eshell-send-input)))) + "l" (proc-int + (eshell-return-to-prompt) + (insert "ls") + (eshell-send-input)) + "c" #'+eshell/good-clear + "k" #'eshell-kill-process)))) #+end_src -** Licensing -Loads [[file:elisp/license.el][license.el]] for inserting licenses. -Licenses are important for distribution and attribution to be defined -clearly. +*** 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 license +(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/" + :config + ;; FIXME: Why do I need to double load this? Otherwise +eshell/open doesn't + ;; work as intended when using universal argument. + (load-file (concat user-emacs-directory "elisp/eshell-additions.el")) :general - (insert-leader - "l" #'+license/insert-copyright-notice - "L" #'+license/insert-complete-license)) + (shell-leader + "t" #'+eshell/open) + (leader + "T" #'+eshell/at-cwd + "E" #'eshell-command)) #+end_src -** diff mode -Good diff management is essentially mandatory in development. Emacs -comes with functionality out of the box to generate, manipulate, and -apply diffs - here I configure a small subset. +*** 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 diff-mode - :general - (nmmap - :keymaps 'diff-mode-map - "J" #'diff-hunk-next - "K" #'diff-hunk-prev - "M-RET" #'diff-apply-hunk - "RET" #'diff-goto-source)) +(use-package eshell-syntax-highlighting + :straight t + :after eshell + :hook (eshell-mode-hook . eshell-syntax-highlighting-mode)) #+end_src -* Org mode -Org is, at its most basic, a markup language. =org-mode= is a major -mode for Emacs to interpret org buffers. org-mode provides a lot of -capabilities, some are: -+ A complete table based spreadsheet system, with formulas (including - [[*Calculator][calc-mode]] integration) -+ Code blocks with proper syntax highlighting and editing experience - + Evaluation - + Export of code blocks to a variety of formats - + Export of code blocks to a code file (so called "tangling", which - is what occurs in this document) -+ Feature complete scheduling system with [[*Calendar][calendar]] - integration - + A clock-in system to time tasks -+ TODO system -+ Export to a variety of formats or make your own export engine using - the org AST. -+ Inline $\LaTeX$, with the ability to render the fragments on - demand within the buffer -+ Links to a variety of formats: - + Websites (via http or https) - + FTP - + SSH - + Files (even to a specific line) - + Info pages +** WAIT VTerm +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +2025-02-17: I haven't used this in at least 1.5 years. Why would I +use this when I can: ++ Use [[*EShell][EShell]] ++ Use ~async-shell-command~ ++ Just spawn a terminal like a normie -I'd argue this is a bit more than a markup language. Like -[[*Magit][Magit]], some use Emacs just for this system. -** Org Essentials -Org has a ton of settings to tweak, which change your experience quite -a bit. Here are mine, but this took a lot of just reading other -people's configurations and testing. I don't do a good job of -explaining how this works in all honesty, but it works well for me so -I'm not very bothered. - -+ By default =~/Text= is my directory for text files. I actually have - a repository that manages this directory for agenda files and other - documents -+ Indentation in file should not be allowed, i.e. text indentation, as - that forces other editors to read it a certain way as well. It's - obtrusive hence it's off. -+ Org startup indented is on by default as most documents do benefit - from the indentation, but I do turn it off for some files via - ~#+startup:noindent~ -+ When opening an org document there can be a lot of headings, so I - set folding to just content -+ Org documents can also have a lot of latex previews, which make - opening some after a while a massive hassle. If I want to see the - preview, I'll do it myself, so turn it off. -+ Org manages windowing itself, to some extent, so I set those options - to be as unobtrusive as possible -+ Load languages I use in =src= blocks in org-mode (Emacs-lisp for - this configuration, C and Python) +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 org - :defer t +(use-package vterm + :straight t + :general + (shell-leader + "v" #'vterm) :init - (setq org-directory "~/Text/" - org-adapt-indentation nil - org-indent-mode nil - org-startup-indented nil - org-startup-folded 'showeverything - org-startup-with-latex-preview nil - org-imenu-depth 10 - org-src-window-setup 'current-window - org-indirect-buffer-display 'current-window - org-link-frame-setup '((vm . vm-visit-folder-other-frame) - (vm-imap . vm-visit-imap-folder-other-frame) - (file . find-file)) - org-babel-load-languages '((emacs-lisp . t) - (lisp . t) - (shell . t)))) + (with-eval-after-load "evil" + (evil-set-initial-state 'vterm-mode 'emacs))) #+end_src -** Org Latex -Org mode has deep integration with latex, can export to PDF and even -display latex fragments in the document directly. I setup the -pdf-process, code listing options via minted and the format options -for latex fragments. +** (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 org +(use-package grep :defer t - :init - (setq org-format-latex-options - '(:foreground default :background "Transparent" :scale 2 - :html-foreground "Black" :html-background "Transparent" - :html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\[")) - org-latex-src-block-backend 'minted - org-latex-minted-langs '((emacs-lisp "common-lisp") - (ledger "text") - (cc "c++") - (cperl "perl") - (shell-script "bash") - (caml "ocaml")) - org-latex-packages-alist '(("" "minted")) - org-latex-pdf-process - (list (concat "latexmk -f -bibtex -pdf " - "-shell-escape -%latex -interaction=nonstopmode " - "-output-directory=%o %f")) - org-latex-minted-options - '(("style" "colorful") - ("linenos") - ("frame" "single") - ("mathescape") - ("fontfamily" "courier") - ("samepage" "false") - ("breaklines" "true") - ("breakanywhere" "true")))) -#+end_src -** Org Core Variables -Tons of variables for org-mode, including a ton of latex ones. Can't -really explain because it sets up quite a lot of local stuff. Also I -copy pasted the majority of this, tweaking it till it felt good. Doom -Emacs was very helpful here. + :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) -#+begin_src emacs-lisp -(use-package org - :defer t - :init - (setq org-edit-src-content-indentation 0 - org-bookmark-names-plist nil - org-eldoc-breadcrumb-separator " → " - org-enforce-todo-dependencies t - org-export-backends '(ascii html latex odt icalendar) - org-fontify-quote-and-verse-blocks t - org-fontify-whole-heading-line t - org-footnote-auto-label t - org-hide-emphasis-markers nil - org-hide-leading-stars t - org-image-actual-width nil - org-imenu-depth 10 - org-link-descriptive nil - org-priority-faces '((?A . error) (?B . warning) (?C . success)) - org-refile-targets '((nil . (:maxlevel . 2))) - org-tags-column 0 - org-todo-keywords '((sequence "TODO" "WIP" "DONE") - (sequence "PROJ" "WAIT" "COMPLETE")) - org-use-sub-superscripts '{})) -#+end_src -** Org Core Functionality -Hooks, prettify-symbols and records for auto insertion. + (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 org +(use-package rg + :straight t :defer t - :hook - (org-mode-hook . prettify-symbols-mode) + :commands (+rg/project-todo) :display - ("\\*Org Src.*" - (display-buffer-same-window)) - :auto-insert - (("\\.org\\'" . "Org skeleton") - "Enter title: " - "#+title: " str | (buffer-file-name) "\n" - "#+author: " (read-string "Enter author: ") | user-full-name "\n" - "#+description: " (read-string "Enter description: ") | "Description" "\n" - "#+date: " (format-time-string "%Y-%m-%d" (current-time)) "\n" - "* " _)) + ("^\\*\\*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) + (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|WIP|FIXME" "*" + (if (project-current) + (project-root (project-current)) + default-directory))) + (evil-set-initial-state 'rg-mode 'normal)) #+end_src -** Org Core Bindings -A load of bindings for org-mode which binds together a lot of -functionality. It's best to read it yourself; to describe it is to -write the code. +** Elfeed +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 " ar" +to elfeed for loading the system. #+begin_src emacs-lisp -(use-package org - :defer t - :init - (with-eval-after-load "consult" - (general-def - :keymaps 'org-mode-map - [remap imenu] #'consult-outline)) +(use-package elfeed + :straight t :general + (app-leader "r" #'elfeed) (nmmap - "M-F" #'org-open-at-point) + :keymaps 'elfeed-search-mode-map + "gr" #'elfeed-update + "s" #'elfeed-search-live-filter + "" #'elfeed-search-show-entry) (nmmap - :keymaps 'org-mode-map - "TAB" #'org-cycle) - (file-leader - "l" #'org-store-link) - (insert-leader - "o" #'org-insert-last-stored-link) - (code-leader - :keymaps 'emacs-lisp-mode-map - "D" #'org-babel-detangle) - (local-leader - :states '(normal motion) - :keymaps 'org-mode-map - "r" #'org-list-repair - "d" #'org-date-from-calendar - "t" #'org-todo - "," #'org-priority - "T" #'org-babel-tangle - "i" #'org-insert-structure-template - "p" #'org-latex-preview - "s" #'org-property-action - "e" #'org-export-dispatch - "o" #'org-edit-special - "R" #'org-refile - "O" #'org-open-at-point) - (local-leader - :keymaps 'org-mode-map - :infix "l" - "i" #'org-insert-link - "l" #'org-open-at-point - "f" #'org-footnote-action) - (local-leader - :keymaps 'org-mode-map - :infix "'" - "a" #'org-table-align - "c" #'org-table-create - "f" #'org-table-edit-formulas - "t" #'org-table-toggle-coordinate-overlays - "s" #'org-table-sum - "e" #'org-table-calc-current-TBLFM - "E" #'org-table-eval-formula) - (local-leader - :keymaps 'org-src-mode-map - "o" #'org-edit-src-exit)) -#+end_src -** Org Agenda -Org agenda provides a nice viewing for schedules. With org mode it's -a very tidy way to manage your time. + :keymaps '(elfeed-search-mode-map elfeed-show-mode-map) + "M-RET" #'elfeed-dispatch) + :init + (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) + :config + (with-eval-after-load "evil-collection" + (evil-collection-elfeed-setup)) + + (defvar +elfeed/dispatch-options + '(("Yank URL" . + (lambda (url) + (kill-new url) + (message "elfeed-dispatch: Yanked %s" url))) + ("Open via EWW" . eww) + ("Play via EMPV" . + (lambda (url) + (if (member 'empv features) + ;; FIXME: Using internal macro + (empv--with-video-enabled + (empv-play-or-enqueue url)) + (message "elfeed-dispatch: EMPV is not available"))))) + "Options available on entering an elfeed post.") + (defun elfeed-dispatch () + "Provide some extra options once you've clicked on an article." + (interactive) + (if (not (or elfeed-show-entry (eq major-mode 'elfeed-search-mode))) + (user-error "elfeed-dispatch: Not in an elfeed post.")) + (let ((choice (completing-read "Choose action: " (mapcar #'car +elfeed/dispatch-options))) + (url (elfeed-entry-link (if elfeed-show-entry + elfeed-show-entry + (elfeed-search-selected :ignore-region))))) + (if-let ((option (cdr (assoc choice +elfeed/dispatch-options #'string=)))) + (funcall option url))))) +#+end_src +*** Elfeed-org #+begin_src emacs-lisp -(use-package org-agenda - :defer t +(use-package elfeed-org + :load-path "elisp/" + :after elfeed :init - (defconst +org/agenda-root "~/Text/" - "Root directory for all agenda files") - (setq org-agenda-files (list (expand-file-name +org/agenda-root)) - org-agenda-window-setup 'current-window - org-agenda-skip-deadline-prewarning-if-scheduled t - org-agenda-skip-scheduled-if-done t - org-agenda-skip-deadline-if-done t - org-agenda-start-with-entry-text-mode nil) + (thread-last "elfeed/feeds.org" + no-littering-expand-etc-file-name + (setq elfeed-org/file)) :config - (evil-set-initial-state 'org-agenda-mode 'normal) - :general - (file-leader - "a" (proc-int - (--> (directory-files (car org-agenda-files)) - (mapcar #'(lambda (x) (concat (car org-agenda-files) x)) it) - (completing-read "Enter directory: " it nil t) - (find-file it)))) - (app-leader - "a" #'org-agenda) - (nmmap - :keymaps 'org-agenda-mode-map - "zd" #'org-agenda-day-view - "zw" #'org-agenda-week-view - "zm" #'org-agenda-month-view - "gd" #'org-agenda-goto-date - "RET" #'org-agenda-switch-to - "J" #'org-agenda-later - "K" #'org-agenda-earlier - "t" #'org-agenda-todo - "." #'org-agenda-goto-today - "," #'org-agenda-goto-date - "q" #'org-agenda-quit - "r" #'org-agenda-redo)) + (elfeed-org)) #+end_src -** Org capture -Org capture provides a system for quickly "capturing" some information -into an org file. A classic example is creating a new TODO in a -todo file, where the bare minimum to record one is: -+ where was it recorded? -+ when was it recorded? -+ what is it? -Org capture provides a way to do that seamlessly without opening the -todo file directly. +** IBuffer +IBuffer is the dired of buffers. Nothing much else to be said. #+begin_src emacs-lisp -(use-package org-capture +(use-package ibuffer :defer t - :init - (setq - org-default-notes-file (concat org-directory "todo.org") - org-capture-templates - '(("t" "Todo" entry - (file "") - "* TODO %? -%T -%a") - ("q" "Quote" entry - (file "quotes.org") - "* %^{Title} -,#+caption: %^{Origin} %t -,#+begin_quote -%? -,#+end_quote"))) :general - (leader - "C" #'org-capture) - (nmmap - :keymaps 'org-capture-mode-map - "ZZ" #'org-capture-finalize - "ZR" #'org-capture-refile - "ZQ" #'org-capture-kill)) + (buffer-leader + "i" #'ibuffer) + :config + (with-eval-after-load "evil-collection" + (evil-collection-ibuffer-setup))) #+end_src -** WAIT Org clock-in -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -2025-02-15: I haven't found much use for this yet but the system is -quite expressive. If I needed time-keeping somewhere, I know where to -go. +** 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) -Org provides a nice timekeeping system that allows for managing how -much time is taken per task. It even has an extensive reporting -system to see how much time you spend on specific tasks or overall. +Core Proced config, just a few bindings and evil collection setup. #+begin_src emacs-lisp -(use-package org-clock - :after org +(use-package proced + :defer t :general - (local-leader - :keymaps 'org-mode-map - :infix "c" - "d" #'org-clock-display - "c" #'org-clock-in - "o" #'org-clock-out - "r" #'org-clock-report)) + (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 -** WAIT Org ref -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -For bibliographic stuff in $\LaTeX$ export. +** 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 org-ref - :straight t +(use-package calc :defer t + :display + ("*Calculator*" + (display-buffer-at-bottom) + (window-height . 0.2)) + :general + (app-leader + "c" #'calc-dispatch) :init - (setq bibtex-files '("~/Text/bibliography.bib") - bibtex-completion-bibliography '("~/Text/bibliography.bib") - bibtex-completion-additional-search-fields '(keywords))) -#+end_src -*** Org ref ivy integration -Org ref requires ivy-bibtex to work properly with ivy, so we need to -set that up as well - -#+begin_src emacs-lisp -(use-package ivy-bibtex - :straight t - :after org-ref + (setq calc-algebraic-mode t) :config - (require 'org-ref-ivy)) + (with-eval-after-load "evil-collection" + (evil-collection-calc-setup))) #+end_src -** Org message -Org message allows for the use of org mode when composing mails, -generating HTML multipart emails. This integrates the WYSIWYG -experience with mail in Emacs while also providing powerful text -features with basically no learning curve (as long as you've already -learnt the basics of org). +** Zone +Emacs' out of the box screensaver software. #+begin_src emacs-lisp -(use-package org-msg - :straight t - :hook - (message-mode-hook . org-msg-mode) - (notmuch-message-mode-hook . org-msg-mode) - :config - (setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t tex:dvipng" - org-msg-greeting-name-limit 3) +(use-package zone + :defer t + :commands (zone) + :general + (leader + "z" #'zone) + :init - (add-to-list - 'org-msg-enforce-css - '(img latex-fragment-inline - ((transform . ,(format "translateY(-1px) scale(%.3f)" - (/ 1.0 (if (boundp 'preview-scale) - preview-scale 1.4)))) - (margin . "0 -0.35em"))))) + (setq zone-programs + [zone-pgm-drip + zone-pgm-drip-fretfully])) #+end_src -** Org for evil -Evil org for some nice bindings. +** (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. -#+begin_src emacs-lisp -(use-package evil-org - :straight t - :defer t - :hook (org-mode-hook . evil-org-mode)) -#+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. -** Magit -Magit is *the* git porcelain for Emacs, which perfectly encapsulates -the git CLI. It's so good that some people use Emacs just for it. -It's another one of those "so indescribably good you have to try it" -things. I've hardly touched the Git CLI since getting Magit, and it -has actively taught me _new_ things about Git. +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! -In this case I just need to setup the bindings for it. +2024-10-08: Man pages are rendered via a separate process, which is +why this is necessary. #+begin_src emacs-lisp -(use-package transient - :straight t) - -(use-package magit - :after transient - :straight t +(use-package man :defer t + :init + (setq Man-notify-method 'thrifty) :display - ("magit:.*" - (display-buffer-same-window) - (inhibit-duplicate-buffer . t)) - ("magit-diff:.*" - (display-buffer-below-selected)) - ("magit-log:.*" - (display-buffer-same-window)) - ("magit-revision:.*" - (display-buffer-below-selected) - (inhibit-duplicate-buffer . t)) + ("\\*Man.*" + (display-buffer-reuse-mode-window display-buffer-same-window) + (mode . Man-mode)) :general - (leader - "g" #'magit-dispatch) - (code-leader - "b" #'magit-blame) - (nmap :keymaps 'magit-status-mode-map - "}" #'magit-section-forward-sibling - "{" #'magit-section-backward-sibling) - :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 -*** Magit Forge -Imagine being able to do all the bureaucratic nonsense involved on -GitHub i.e. pull requests, issue handling, etc. all through Emacs! No -need to imagine any more, with Magit Forge. -#+begin_src emacs-lisp -(use-package forge - :after magit - :straight t - :config - (with-eval-after-load "evil-collection" - (evil-collection-forge-setup))) + (file-leader + "m" #'man) ;; kinda like "find man page" + (nmmap + :keymaps 'Man-mode-map + "RET" #'man-follow)) #+end_src -** 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. +** 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 eww +(use-package info :defer t :general - (app-leader - "w" #'eww) (nmmap - :keymaps 'eww-mode-map - "w" #'evil-forward-word-begin - "Y" #'eww-copy-page-url)) + :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 -** 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". +** 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 calendar +(use-package image-mode :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 - (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. + :keymaps 'image-mode-map + "q" #'quit-window + ;; motion + "gg" 'image-bob + "G" 'image-eob + [remap evil-forward-char] 'image-forward-hscroll + [remap evil-backward-char] 'image-backward-hscroll + [remap evil-next-line] 'image-next-line + [remap evil-previous-line] 'image-previous-line + "0" 'image-bol + "^" 'image-bol + "$" 'image-eol + (kbd "C-d") 'image-scroll-up + (kbd "SPC") 'image-scroll-up + (kbd "S-SPC") 'image-scroll-down + (kbd "") 'image-scroll-down + ;; animation + (kbd "RET") 'image-toggle-animation + "F" 'image-goto-frame + "," 'image-previous-frame ; mplayer/mpv style + "." 'image-next-frame ; mplayer/mpv style + ";" 'image-next-frame ; Evil style + "{" 'image-decrease-speed ; mplayer/mpv style + "}" 'image-increase-speed ; mplayer/mpv style -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. + "H" 'image-transform-fit-to-height + "W" 'image-transform-fit-to-width + "+" 'image-increase-size + "=" 'image-increase-size + "-" 'image-decrease-size + + "[[" 'image-previous-file + "]]" 'image-next-file + "gk" 'image-previous-file + "gj" 'image-next-file + (kbd "C-k") 'image-previous-file + (kbd "C-j") 'image-next-file + + (kbd "C-c C-c") 'image-toggle-display + + ;; quit + "q" 'quit-window + "ZQ" 'evil-quit + "ZZ" 'quit-window)) +#+end_src +** empv +Emacs MPV bindings, with very cool controls for queuing files for +playing. #+begin_src emacs-lisp -(use-package notmuch +(use-package empv :straight t :defer t - :commands (notmuch +mail/flag-thread) :general - (app-leader "m" #'notmuch) - (nmap - :keymaps 'notmuch-search-mode-map - "f" #'+mail/flag-thread) + (app-leader + "e" #'empv-hydra/body) :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) - :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)))) + (setq empv-audio-dir (list (expand-file-name "~/Media/audio") + ;; "/sshx:oldboy:/media/hdd/content/Audio" + ) + empv-video-dir (list (expand-file-name "~/Media/videos") + ;; "/sshx:oldboy:/media/hdd/content/Videos" + ) + empv-playlist-dir (expand-file-name "~/Media/playlists") + empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" "flac" "aac" "opus") + empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" "webm") + empv-radio-channels + '(("SomaFM - Groove Salad" . "http://www.somafm.com/groovesalad.pls") + ("SomaFM - Drone Zone" . "http://www.somafm.com/dronezone.pls") + ("SomaFM - Sonic Universe" . "http://www.somafm.com/sonicuniverse.pls") + ("SomaFM - Metal" . "http://www.somafm.com/metal.pls") + ("SomaFM - Vaporwaves" . "http://www.somafm.com/vaporwaves.pls") + ("SomaFM - DEFCON" . "http://www.somafm.com/defcon.pls") + ("SomaFM - The Trip" . "http://www.somafm.com/thetrip.pls")))) #+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. - +** 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 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)) +(use-package gud + :general + :after hydra + :hydra + (gud-hydra + (:hint nil) "Hydra for GUD" + ("<" #'gud-up "Up" + :column "Stack") + (">" #'gud-down "Down" + :column "Stack") + ("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 "Breakpoints") + ("s" #'gud-step "Step" + :column "Control Flow") + ("t" #'gud-tbreak "Tbreak" + :column "Breakpoints") + ("u" #'gud-until "Until" + :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 -*** Mail signature using fortune -Generate a mail signature using the ~fortune~ executable. Pretty -cool! - +** Jira #+begin_src emacs-lisp -(use-package fortune - :after message +(use-package jira + :straight (:host github :repo "unmonoqueteclea/jira.el") :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)))) - ) + (setq jira-base-url "https://reframe.atlassian.net") + :general + (app-leader + "j" #'jira-issues) + (nmmap + :keymaps 'jira-issues-mode-map + "M-RET" #'jira-issues-actions-menu)) #+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. -*** Dired Core -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~) +* 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 +also need it in commit messages and so on, thus it should really hook +into text-mode. #+begin_src emacs-lisp -(use-package dired +(use-package flyspell :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) + :hook ((org-mode-hook text-mode-hook) . flyspell-mode) :general (nmmap - :keymaps 'dired-mode-map - "SPC" nil - "SPC ," nil - "M-k" #'dired-prev-subdir - "M-j" #'dired-next-subdir - "q" #'quit-window - "j" #'dired-next-line - "k" #'dired-previous-line - "(" #'dired-hide-details-mode - ")" #'dired-omit-mode - "T" #'dired-create-empty-file - "H" #'dired-up-directory - "L" #'dired-find-file - "#" #'dired-flag-auto-save-files - "." #'dired-clean-directory - "~" #'dired-flag-backup-files - "A" #'dired-do-find-regexp - "C" #'dired-do-copy - "B" #'dired-do-byte-compile - "D" #'dired-do-delete - "M" #'dired-do-chmod - "O" #'dired-do-chown - "P" #'dired-do-print - "Q" #'dired-do-find-regexp-and-replace - "R" #'dired-do-rename - "S" #'dired-do-symlink - "T" #'dired-do-touch - "X" #'dired-do-shell-command - "Z" #'dired-do-compress - "c" #'dired-do-compress-to - "!" #'dired-do-shell-command - "&" #'dired-do-async-shell-command - "{" #'dired-prev-marked-file - "}" #'dired-next-marked-file - "%" nil - "%u" #'dired-upcase - "%l" #'dired-downcase - "%d" #'dired-flag-files-regexp - "%g" #'dired-mark-files-containing-regexp - "%m" #'dired-mark-files-regexp - "%r" #'dired-do-rename-regexp - "%C" #'dired-do-copy-regexp - "%H" #'dired-do-hardlink-regexp - "%R" #'dired-do-rename-regexp - "%S" #'dired-do-symlink-regexp - "%&" #'dired-flag-garbage-files - "*" nil - "**" #'dired-mark-executables - "*/" #'dired-mark-directories - "*@" #'dired-mark-symlinks - "*%" #'dired-mark-files-regexp - "*c" #'dired-change-marks - "*s" #'dired-mark-subdir-files - "*m" #'dired-mark - "*t" #'dired-toggle-marks - "*?" #'dired-unmark-all-files - "*!" #'dired-unmark-all-marks - "U" #'dired-unmark-all-marks - "a" #'dired-find-alternate-file - "d" #'dired-flag-file-deletion - "gf" #'browse-url-of-dired-file - "gr" #'revert-buffer - "i" #'dired-toggle-read-only - "J" #'dired-goto-file - "K" #'dired-do-kill-lines - "r" #'revert-buffer - "m" #'dired-mark - "t" #'dired-toggle-marks - "u" #'dired-unmark - "x" #'dired-do-flagged-delete - "gt" #'dired-show-file-type - "Y" #'dired-copy-filename-as-kill - "+" #'dired-create-directory - "RET" #'dired-find-file - "C-" #'dired-find-file-other-window - "o" #'dired-sort-toggle-or-edit - "[[" #'dired-prev-dirline - "]]" #'dired-next-dirline - [remap next-line] #'dired-next-line - [remap previous-line] #'dired-previous-line - "zt" #'dired-hide-subdir - "zC" #'dired-hide-all - [remap read-only-mode] #'dired-toggle-read-only - [remap toggle-read-only] #'dired-toggle-read-only - [remap undo] #'dired-undo - [remap advertised-undo] #'dired-undo) - (leader - "D" #'dired-jump) - (dir-leader - "f" #'find-dired - "d" #'dired - "D" #'dired-other-window - "i" #'image-dired - "b" (proc-int (find-file "~/Text/Books/"))) - (local-leader - :keymaps 'dired-mode-map - "i" #'dired-maybe-insert-subdir - "d" #'dired-goto-subdir - "I" #'+dired/insert-all-subdirectories - "o" #'dired-omit-mode - "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/--subdirs-not-inserted () - (dired-unmark-all-marks) - (dired-mark-directories nil) - (let* ((subdirs-inserted (mapcar #'car dired-subdir-alist)) - (subdirs-available (mapcar #'(lambda (x) (concat x "/")) - (dired-get-marked-files)))) - (dired-unmark-all-marks) - (cl-remove-if #'(lambda (f) (member f subdirs-inserted)) subdirs-available))) - - (defun +dired/insert-all-subdirectories (&optional arg) - "Insert all subdirectories recursively." - (interactive "P") - (let ((subdirs-left (+dired/--subdirs-not-inserted))) - (if (null arg) - (mapc #'dired-insert-subdir subdirs-left) - (while subdirs-left - (mapc #'dired-insert-subdir subdirs-left) - (setq subdirs-left (+dired/--subdirs-not-inserted))))))) + :keymaps 'text-mode-map + "M-C" #'flyspell-correct-word-before-point + "M-c" #'flyspell-auto-correct-word) + (mode-leader + "s" #'flyspell-mode)) #+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. +** Whitespace +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 dired +(use-package whitespace :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)) + "M--" #'whitespace-cleanup) + (mode-leader + "w" #'whitespace-mode) + :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 + nasm-mode-hook) + . whitespace-mode) + :init + (setq whitespace-line-column nil + whitespace-style '(face empty spaces tabs newline trailing + lines-char tab-mark))) #+end_src -*** fd-dired -Uses fd for finding file results in a directory: ~find-dired~ -> -~fd-dired~. +** Filling and displaying fills +The fill-column is the number of characters that should be in a single +line of text before doing a hard wrap. The default case is 80 +characters for that l33t Unix hard terminal character limit. I like +different fill-columns for different modes: text modes should really +use 70 fill columns while code should stick to 80. #+begin_src emacs-lisp -(use-package fd-dired - :straight t - :after dired - :general - (dir-leader - "g" #'fd-dired)) +(use-package emacs + :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) + :init + (setq-default fill-column 80) + (add-hook 'text-mode-hook (proc (setq-local fill-column 70)))) #+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. +** 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 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) +(use-package emacs + :demand t :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))) + (global-visual-line-mode t)) #+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. +** Show-paren-mode +When the cursor is over a parenthesis, highlight the other member of +the pair. #+begin_src emacs-lisp -(use-package dired-rsync +(use-package paren + :hook (prog-mode-hook . show-paren-mode)) +#+end_src +** Smartparens +Smartparens is a smarter electric-parens, it's much more aware of +context and easier to use. + +#+begin_src emacs-lisp +(use-package smartparens + :straight t + :defer t + :hook + (prog-mode-hook . smartparens-mode) + (text-mode-hook . smartparens-mode) + :config + (setq sp-highlight-pair-overlay nil + sp-highlight-wrap-overlay t + sp-highlight-wrap-tag-overlay t) + + (let ((unless-list '(sp-point-before-word-p + sp-point-after-word-p + sp-point-before-same-p))) + (sp-pair "'" nil :unless unless-list) + (sp-pair "\"" nil :unless unless-list)) + (sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p)) + (require 'smartparens-config)) +#+end_src +** Powerthesaurus +Modern package for thesaurus in Emacs with a transient + hydra. +#+begin_src emacs-lisp +(use-package powerthesaurus + :defer t :straight t - :after dired :general - (nmmap - :keymaps 'dired-mode-map - "M-r" #'dired-rsync)) + (search-leader + "w" #'powerthesaurus-transient)) #+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). +** lorem ipsum +Sometimes you need placeholder text for some UI or document. Pretty +easy to guess what text I'd use. -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). +#+begin_src emacs-lisp +(use-package lorem-ipsum + :straight t + :general + (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~. -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! +#+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. +** Eldoc +Eldoc presents documentation to the user upon placing ones cursor upon +any symbol. This is very useful when programming as it: +- presents the arguments of functions while writing calls for them +- presents typing and documentation of variables -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. +Eldoc box makes the help buffer a hovering box instead of printing it +in the minibuffer. A lot cleaner. -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 +2024-05-31: Eldoc box is a bit useless now that I'm not using frames. +I prefer the use of the minibuffer for printing documentation now. + +#+begin_src emacs-lisp +(use-package eldoc + :defer t + :hook (prog-mode-hook . eldoc-mode) + :init + (global-eldoc-mode 1) + :general + (leader + "h>" #'eldoc-doc-buffer)) +#+end_src +** Flycheck +Flycheck is the checking system for Emacs. I don't necessarily like +having all my code checked all the time, so I haven't added a hook to +prog-mode as it would be better for me to decide when I want checking +and when I don't. Many times Flycheck is annoying when checking a +program, particularly one which isn't finished yet. + +#+begin_src emacs-lisp +(use-package flycheck + :straight t + :defer t + :commands (flycheck-mode flycheck-list-errors) + :general + (mode-leader + "f" #'flycheck-mode) + (code-leader + "x" #'flycheck-list-errors + "j" #'flycheck-next-error + "k" #'flycheck-previous-error) + :display + ("\\*Flycheck.*" + (display-buffer-at-bottom) + (window-height . 0.25)) + :init + (setq-default flycheck-check-syntax-automatically + '(save idle-change mode-enabled) + flycheck-idle-change-delay 1.0 + flycheck-buffer-switch-check-intermediate-buffers t + flycheck-display-errors-delay 0.25) + :config + (with-eval-after-load "evil-collection" + (evil-collection-flycheck-setup))) +#+end_src +** Eglot +Eglot is package to communicate with LSP servers for better +programming capabilities. Interactions with a server provide results +to the client, done through JSON. -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: Emacs 28.1 comes with better JSON parsing, which makes Eglot +much faster. -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. +2023-03-26: I've found Eglot to be useful sometimes, but many of the +projects I work on don't require a heavy server setup to efficiently +edit and check for errors; Emacs provides a lot of functionality. So +by default I've disabled it, using =M-x eglot= to startup the LSP +server when I need it. + +2024-06-27: In projects where I do use eglot and I know I will need it +regardless of file choice, I prefer setting it at the dir-local level +via an eval form. So I add to the safe values for the eval variable +to be set. #+begin_src emacs-lisp -(use-package eshell +(use-package eglot :defer t - :display - ("\\*eshell\\*" - (display-buffer-same-window) - (reusable-frames . t)) + :general + (code-leader + :keymaps 'eglot-mode-map + "f" #'eglot-format + "a" #'eglot-code-actions + "R" #'eglot-reconnect) :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) + (setq eglot-auto-shutdown t + eglot-stay-out-of '(flymake) + eglot-ignored-server-capabilities '(:documentHighlightProvider + :documentOnTypeFormattingProvider + :inlayHintProvider)) + (add-to-list 'safe-local-variable-values '(eval eglot-ensure))) +#+end_src +** Indentation +By default, turn off tabs and set the tab width to two. - (local-leader - :keymaps 'eshell-mode-map - "g" (proc-int - (let ((buffer (current-buffer))) - (eshell/goto) - (with-current-buffer buffer - (eshell-send-input)))) - "l" (proc-int - (eshell-return-to-prompt) - (insert "ls") - (eshell-send-input)) - "c" #'+eshell/good-clear - "k" #'eshell-kill-process)))) +#+begin_src emacs-lisp +(setq-default indent-tabs-mode nil + tab-width 2) #+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. +However, if necessary later, define a function that may activate tabs locally. +#+begin_src emacs-lisp +(defun +oreo/use-tabs () + (interactive) + (setq-local indent-tabs-mode t)) +#+end_src +** Highlight todo items +TODO items are highlighted in org-mode, but not necessarily in every +mode. This minor mode highlights all TODO like items via a list of +strings to match. It also configures faces to use when highlighting. +I hook it to prog-mode. #+begin_src emacs-lisp -(use-package eshell-prompt - :load-path "elisp/" - :config - (setq eshell-prompt-function #'+eshell-prompt/make-prompt)) +(use-package hl-todo + :straight t + :after prog-mode + :hook (prog-mode-hook . hl-todo-mode) + :init + (setq hl-todo-keyword-faces + '(("TODO" . "#E50000") + ("WIP" . "#ffa500") + ("NOTE" . "#00CC00") + ("FIXME" . "#d02090")))) #+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. +** Hide-show mode +Turn on ~hs-minor-mode~ for all prog-mode. This provides folds for +free. -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/" - :config - ;; FIXME: Why do I need to double load this? Otherwise +eshell/open doesn't - ;; work as intended when using universal argument. - (load-file (concat user-emacs-directory "elisp/eshell-additions.el")) - :general - (shell-leader - "t" #'+eshell/open) - (leader - "T" #'+eshell/at-cwd - "E" #'eshell-command)) +(use-package hideshow + :defer t + :hook (prog-mode-hook . hs-minor-mode)) #+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. +** Aggressive indenting +Essentially my dream editing experience: when I type stuff in, try and +indent it for me on the fly. Just checkout the +[[https://github.com/Malabarba/aggressive-indent-mode][page]], any +description I give won't do it justice. #+begin_src emacs-lisp -(use-package eshell-syntax-highlighting +(use-package aggressive-indent :straight t - :after eshell - :hook (eshell-mode-hook . eshell-syntax-highlighting-mode)) + :hook (emacs-lisp-mode-hook . aggressive-indent-mode) + :hook (scheme-mode-hook . aggressive-indent-mode) + :hook (lisp-mode-hook . aggressive-indent-mode)) #+end_src -** WAIT VTerm -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -2025-02-17: I haven't used this in at least 1.5 years. Why would I -use this when I can: -+ Use [[*EShell][EShell]] -+ Use ~async-shell-command~ -+ Just spawn a terminal like a normie +** Compilation +Compilation mode is an incredibly useful subsystem of Emacs which +allows one to run arbitrary commands. If those commands produce +errors (particularly errors that have a filename, column and line) +compilation-mode can colourise these errors and help you navigate to +them. -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. +Here I add some bindings and a filter which colourises the output of +compilation mode for ANSI escape sequences; the eyecandy is certainly +nice but it's very useful when dealing with tools that use those codes +so you can actually read the text. -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 +(use-package compile + :defer t + :display + ("\\*compilation\\*" + (display-buffer-reuse-window display-buffer-at-bottom) + (window-height . 0.3) + (reusable-frames . t)) + :hydra + (move-error-hydra + (:hint nil) "Hydra for moving between errors" + ("j" #'next-error) + ("k" #'previous-error)) :general - (shell-leader - "v" #'vterm) + (leader + "j" #'move-error-hydra/next-error + "k" #'move-error-hydra/previous-error) + (code-leader + "c" #'compile + "r" #'recompile) + (nmap + "M-r" #'recompile) + (:keymaps 'compilation-mode-map + "g" nil ;; by default this is recompile + "M-j" #'compilation-next-error + "M-k" #'compilation-previous-error) + (nmmap + :keymaps 'compilation-mode-map + "c" #'recompile) :init - (with-eval-after-load "evil" - (evil-set-initial-state 'vterm-mode 'emacs))) + (setq compilation-scroll-output 'first-error + compilation-context-lines nil + next-error-highlight 'fringe-arrow) + :config + (add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)) #+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 +** xref +Find definitions, references and general objects using tags without +external packages. Provided out of the box with Emacs, but requires a +way of generating a =TAGS= file for your project (look at +[[*Project.el][Project.el]] for my way of doing so). A critical +component in a minimal setup for programming without heavier systems +like [[*Eglot][Eglot]]. + #+begin_src emacs-lisp -(use-package grep +(use-package xref :defer t :display - ("^\\*grep.*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.35) - (reusable-frames . t)) + ("\\*xref\\*" + (display-buffer-at-bottom) + (inhibit-duplicate-buffer . t) + (window-height . 0.3)) :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) + (code-leader + "t" #'nil) + (code-leader + :infix "t" + "t" #'xref-find-apropos + "d" #'xref-find-definitions + "r" #'xref-find-references) (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"))))) + :keymaps 'xref--xref-buffer-mode-map + "RET" #'xref-goto-xref + "J" #'xref-next-line + "K" #'xref-prev-line + "r" #'xref-query-replace-in-results + "gr" #'xref-revert-buffer + "q" #'quit-window)) #+end_src -*** rg +** devdocs +When man pages aren't enough, you need some documentation lookup +system (basically whenever your using anything but C/C++/Bash). +[[https://devdocs.io][Devdocs]] is a great little website that +provides a ton of documentation sets. There's an Emacs package for it +which works well and downloads documentation sets to my machine, which +is nice. + #+begin_src emacs-lisp -(use-package rg +(use-package devdocs :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) - (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|WIP|FIXME" "*" - (if (project-current) - (project-root (project-current)) - default-directory))) - (evil-set-initial-state 'rg-mode 'normal)) + (file-leader + "d" #'devdocs-lookup)) #+end_src -** Elfeed -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 " ar" -to elfeed for loading the system. +** rainbow-delimiters +Makes colours delimiters (parentheses) based on their depth in an +expression. Rainbow flag in your Lisp source code. #+begin_src emacs-lisp -(use-package elfeed +(use-package rainbow-delimiters + :defer t :straight t :general - (app-leader "r" #'elfeed) - (nmmap - :keymaps 'elfeed-search-mode-map - "gr" #'elfeed-update - "s" #'elfeed-search-live-filter - "" #'elfeed-search-show-entry) + (mode-leader "r" #'rainbow-delimiters-mode) + :hook + ((lisp-mode-hook emacs-lisp-mode-hook racket-mode-hook) . rainbow-delimiters-mode)) +#+end_src +** Licensing +Loads [[file:elisp/license.el][license.el]] for inserting licenses. +Licenses are important for distribution and attribution to be defined +clearly. + +#+begin_src emacs-lisp +(use-package license + :demand t + :load-path "elisp/" + :general + (insert-leader + "l" #'+license/insert-copyright-notice + "L" #'+license/insert-complete-license)) +#+end_src +** diff mode +Good diff management is essentially mandatory in development. Emacs +comes with functionality out of the box to generate, manipulate, and +apply diffs - here I configure a small subset. +#+begin_src emacs-lisp +(use-package diff-mode + :general (nmmap - :keymaps '(elfeed-search-mode-map elfeed-show-mode-map) - "M-RET" #'elfeed-dispatch) - :init - (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) - :config - (with-eval-after-load "evil-collection" - (evil-collection-elfeed-setup)) + :keymaps 'diff-mode-map + "J" #'diff-hunk-next + "K" #'diff-hunk-prev + "M-RET" #'diff-apply-hunk + "RET" #'diff-goto-source)) +#+end_src +* Languages +For a variety of (programming) languages Emacs comes with default +modes but this configures them as well as pulls any modes Emacs +doesn't come with. +** Org mode +Org is, at its most basic, a markup language. =org-mode= is a major +mode for Emacs to interpret org buffers. org-mode provides a lot of +capabilities, some are: ++ A complete table based spreadsheet system, with formulas (including + [[*Calculator][calc-mode]] integration) ++ Code blocks with proper syntax highlighting and editing experience + + Evaluation + + Export of code blocks to a variety of formats + + Export of code blocks to a code file (so called "tangling", which + is what occurs in this document) ++ Feature complete scheduling system with [[*Calendar][calendar]] + integration + + A clock-in system to time tasks ++ TODO system ++ Export to a variety of formats or make your own export engine using + the org AST. ++ Inline $\LaTeX$, with the ability to render the fragments on + demand within the buffer ++ Links to a variety of formats: + + Websites (via http or https) + + FTP + + SSH + + Files (even to a specific line) + + Info pages - (defvar +elfeed/dispatch-options - '(("Yank URL" . - (lambda (url) - (kill-new url) - (message "elfeed-dispatch: Yanked %s" url))) - ("Open via EWW" . eww) - ("Play via EMPV" . - (lambda (url) - (if (member 'empv features) - ;; FIXME: Using internal macro - (empv--with-video-enabled - (empv-play-or-enqueue url)) - (message "elfeed-dispatch: EMPV is not available"))))) - "Options available on entering an elfeed post.") +I'd argue this is a bit more than a markup language. Like +[[*Magit][Magit]], some use Emacs just for this system. +*** Org Essentials +Org has a ton of settings to tweak, which change your experience quite +a bit. Here are mine, but this took a lot of just reading other +people's configurations and testing. I don't do a good job of +explaining how this works in all honesty, but it works well for me so +I'm not very bothered. + ++ By default =~/Text= is my directory for text files. I actually have + a repository that manages this directory for agenda files and other + documents ++ Indentation in file should not be allowed, i.e. text indentation, as + that forces other editors to read it a certain way as well. It's + obtrusive hence it's off. ++ Org startup indented is on by default as most documents do benefit + from the indentation, but I do turn it off for some files via + ~#+startup:noindent~ ++ When opening an org document there can be a lot of headings, so I + set folding to just content ++ Org documents can also have a lot of latex previews, which make + opening some after a while a massive hassle. If I want to see the + preview, I'll do it myself, so turn it off. ++ Org manages windowing itself, to some extent, so I set those options + to be as unobtrusive as possible ++ Load languages I use in =src= blocks in org-mode (Emacs-lisp for + this configuration, C and Python) - (defun elfeed-dispatch () - "Provide some extra options once you've clicked on an article." - (interactive) - (if (not (or elfeed-show-entry (eq major-mode 'elfeed-search-mode))) - (user-error "elfeed-dispatch: Not in an elfeed post.")) - (let ((choice (completing-read "Choose action: " (mapcar #'car +elfeed/dispatch-options))) - (url (elfeed-entry-link (if elfeed-show-entry - elfeed-show-entry - (elfeed-search-selected :ignore-region))))) - (if-let ((option (cdr (assoc choice +elfeed/dispatch-options #'string=)))) - (funcall option url))))) -#+end_src -*** Elfeed-org #+begin_src emacs-lisp -(use-package elfeed-org - :load-path "elisp/" - :after elfeed - :init - (thread-last "elfeed/feeds.org" - no-littering-expand-etc-file-name - (setq elfeed-org/file)) - :config - (elfeed-org)) +(use-package org + :defer t + :init + (setq org-directory "~/Text/" + org-adapt-indentation nil + org-indent-mode nil + org-startup-indented nil + org-startup-folded 'showeverything + org-startup-with-latex-preview nil + org-imenu-depth 10 + org-src-window-setup 'current-window + org-indirect-buffer-display 'current-window + org-link-frame-setup '((vm . vm-visit-folder-other-frame) + (vm-imap . vm-visit-imap-folder-other-frame) + (file . find-file)) + org-babel-load-languages '((emacs-lisp . t) + (lisp . t) + (shell . t)))) #+end_src -** IBuffer -IBuffer is the dired of buffers. Nothing much else to be said. +*** Org Latex +Org mode has deep integration with latex, can export to PDF and even +display latex fragments in the document directly. I setup the +pdf-process, code listing options via minted and the format options +for latex fragments. #+begin_src emacs-lisp -(use-package ibuffer +(use-package org :defer t - :general - (buffer-leader - "i" #'ibuffer)) + :init + (setq org-format-latex-options + '(:foreground default :background "Transparent" :scale 2 + :html-foreground "Black" :html-background "Transparent" + :html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\[")) + org-latex-src-block-backend 'minted + org-latex-minted-langs '((emacs-lisp "common-lisp") + (ledger "text") + (cc "c++") + (cperl "perl") + (shell-script "bash") + (caml "ocaml")) + org-latex-packages-alist '(("" "minted")) + org-latex-pdf-process + (list (concat "latexmk -f -bibtex -pdf " + "-shell-escape -%latex -interaction=nonstopmode " + "-output-directory=%o %f")) + org-latex-minted-options + '(("style" "colorful") + ("linenos") + ("frame" "single") + ("mathescape") + ("fontfamily" "courier") + ("samepage" "false") + ("breaklines" "true") + ("breakanywhere" "true")))) #+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. +*** Org Core Variables +Tons of variables for org-mode, including a ton of latex ones. Can't +really explain because it sets up quite a lot of local stuff. Also I +copy pasted the majority of this, tweaking it till it felt good. Doom +Emacs was very helpful here. #+begin_src emacs-lisp -(use-package proced +(use-package org :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)) + (setq org-edit-src-content-indentation 0 + org-bookmark-names-plist nil + org-eldoc-breadcrumb-separator " → " + org-enforce-todo-dependencies t + org-export-backends '(ascii html latex odt icalendar) + org-fontify-quote-and-verse-blocks t + org-fontify-whole-heading-line t + org-footnote-auto-label t + org-hide-emphasis-markers nil + org-hide-leading-stars t + org-image-actual-width nil + org-imenu-depth 10 + org-link-descriptive nil + org-priority-faces '((?A . error) (?B . warning) (?C . success)) + org-refile-targets '((nil . (:maxlevel . 2))) + org-tags-column 0 + org-todo-keywords '((sequence "TODO" "WIP" "DONE") + (sequence "PROJ" "WAIT" "COMPLETE")) + org-use-sub-superscripts '{})) #+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. +*** Org Core Functionality +Hooks, prettify-symbols and records for auto insertion. #+begin_src emacs-lisp -(use-package calc +(use-package org :defer t + :hook + (org-mode-hook . prettify-symbols-mode) :display - ("*Calculator*" - (display-buffer-at-bottom) - (window-height . 0.2)) - :general - (app-leader - "c" #'calc-dispatch) - :init - (setq calc-algebraic-mode t)) + ("\\*Org Src.*" + (display-buffer-same-window)) + :auto-insert + (("\\.org\\'" . "Org skeleton") + "Enter title: " + "#+title: " str | (buffer-file-name) "\n" + "#+author: " (read-string "Enter author: ") | user-full-name "\n" + "#+description: " (read-string "Enter description: ") | "Description" "\n" + "#+date: " (format-time-string "%Y-%m-%d" (current-time)) "\n" + "* " _)) #+end_src -** Zone -Emacs' out of the box screensaver software. +*** Org Core Bindings +A load of bindings for org-mode which binds together a lot of +functionality. It's best to read it yourself; to describe it is to +write the code. #+begin_src emacs-lisp -(use-package zone +(use-package org :defer t - :commands (zone) - :general - (leader - "z" #'zone) :init - - (setq zone-programs - [zone-pgm-drip - zone-pgm-drip-fretfully])) + (with-eval-after-load "consult" + (general-def + :keymaps 'org-mode-map + [remap imenu] #'consult-outline)) + :general + (nmmap + "M-F" #'org-open-at-point) + (nmmap + :keymaps 'org-mode-map + "TAB" #'org-cycle) + (file-leader + "l" #'org-store-link) + (insert-leader + "o" #'org-insert-last-stored-link) + (code-leader + :keymaps 'emacs-lisp-mode-map + "D" #'org-babel-detangle) + (local-leader + :states '(normal motion) + :keymaps 'org-mode-map + "r" #'org-list-repair + "d" #'org-date-from-calendar + "t" #'org-todo + "," #'org-priority + "T" #'org-babel-tangle + "i" #'org-insert-structure-template + "p" #'org-latex-preview + "s" #'org-property-action + "e" #'org-export-dispatch + "o" #'org-edit-special + "R" #'org-refile + "O" #'org-open-at-point) + (local-leader + :keymaps 'org-mode-map + :infix "l" + "i" #'org-insert-link + "l" #'org-open-at-point + "f" #'org-footnote-action) + (local-leader + :keymaps 'org-mode-map + :infix "'" + "a" #'org-table-align + "c" #'org-table-create + "f" #'org-table-edit-formulas + "t" #'org-table-toggle-coordinate-overlays + "s" #'org-table-sum + "e" #'org-table-calc-current-TBLFM + "E" #'org-table-eval-formula) + (local-leader + :keymaps 'org-src-mode-map + "o" #'org-edit-src-exit)) #+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. +*** Org Agenda +Org agenda provides a nice viewing for schedules. With org mode it's +a very tidy way to manage your time. #+begin_src emacs-lisp -(use-package man +(use-package org-agenda :defer t :init - (setq Man-notify-method 'thrifty) - :display - ("\\*Man.*" - (display-buffer-reuse-mode-window display-buffer-same-window) - (mode . Man-mode)) + (defconst +org/agenda-root "~/Text/" + "Root directory for all agenda files") + (setq org-agenda-files (list (expand-file-name +org/agenda-root)) + org-agenda-window-setup 'current-window + org-agenda-skip-deadline-prewarning-if-scheduled t + org-agenda-skip-scheduled-if-done t + org-agenda-skip-deadline-if-done t + org-agenda-start-with-entry-text-mode nil) + :config + (evil-set-initial-state 'org-agenda-mode 'normal) :general (file-leader - "m" #'man) ;; kinda like "find man page" + "a" (proc-int + (--> (directory-files (car org-agenda-files)) + (mapcar #'(lambda (x) (concat (car org-agenda-files) x)) it) + (completing-read "Enter directory: " it nil t) + (find-file it)))) + (app-leader + "a" #'org-agenda) (nmmap - :keymaps 'Man-mode-map - "RET" #'man-follow)) + :keymaps 'org-agenda-mode-map + "zd" #'org-agenda-day-view + "zw" #'org-agenda-week-view + "zm" #'org-agenda-month-view + "gd" #'org-agenda-goto-date + "RET" #'org-agenda-switch-to + "J" #'org-agenda-later + "K" #'org-agenda-earlier + "t" #'org-agenda-todo + "." #'org-agenda-goto-today + "," #'org-agenda-goto-date + "q" #'org-agenda-quit + "r" #'org-agenda-redo)) #+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. +*** Org capture +Org capture provides a system for quickly "capturing" some information +into an org file. A classic example is creating a new TODO in a +todo file, where the bare minimum to record one is: ++ where was it recorded? ++ when was it recorded? ++ what is it? +Org capture provides a way to do that seamlessly without opening the +todo file directly. #+begin_src emacs-lisp -(use-package info +(use-package org-capture :defer t + :init + (setq + org-default-notes-file (concat org-directory "todo.org") + org-capture-templates + '(("t" "Todo" entry + (file "") + "* TODO %? +%T +%a") + ("q" "Quote" entry + (file "quotes.org") + "* %^{Title} +,#+caption: %^{Origin} %t +,#+begin_quote +%? +,#+end_quote"))) :general + (leader + "C" #'org-capture) (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))) + :keymaps 'org-capture-mode-map + "ZZ" #'org-capture-finalize + "ZR" #'org-capture-refile + "ZQ" #'org-capture-kill)) #+end_src -** Image-mode -Image mode, for viewing images. Supports tons of formats, easy to use -and integrates slickly into image-dired. Of course, +*** WAIT Org clock-in +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +2025-02-15: I haven't found much use for this yet but the system is +quite expressive. If I needed time-keeping somewhere, I know where to +go. + +Org provides a nice timekeeping system that allows for managing how +much time is taken per task. It even has an extensive reporting +system to see how much time you spend on specific tasks or overall. #+begin_src emacs-lisp -(use-package image-mode - :defer t +(use-package org-clock + :after org :general - (nmmap - :keymaps 'image-mode-map - "q" #'quit-window - ;; motion - "gg" 'image-bob - "G" 'image-eob - [remap evil-forward-char] 'image-forward-hscroll - [remap evil-backward-char] 'image-backward-hscroll - [remap evil-next-line] 'image-next-line - [remap evil-previous-line] 'image-previous-line - "0" 'image-bol - "^" 'image-bol - "$" 'image-eol - (kbd "C-d") 'image-scroll-up - (kbd "SPC") 'image-scroll-up - (kbd "S-SPC") 'image-scroll-down - (kbd "") 'image-scroll-down - ;; animation - (kbd "RET") 'image-toggle-animation - "F" 'image-goto-frame - "," 'image-previous-frame ; mplayer/mpv style - "." 'image-next-frame ; mplayer/mpv style - ";" 'image-next-frame ; Evil style - "{" 'image-decrease-speed ; mplayer/mpv style - "}" 'image-increase-speed ; mplayer/mpv style - - "H" 'image-transform-fit-to-height - "W" 'image-transform-fit-to-width - - "+" 'image-increase-size - "=" 'image-increase-size - "-" 'image-decrease-size - - "[[" 'image-previous-file - "]]" 'image-next-file - "gk" 'image-previous-file - "gj" 'image-next-file - (kbd "C-k") 'image-previous-file - (kbd "C-j") 'image-next-file - - (kbd "C-c C-c") 'image-toggle-display - - ;; quit - "q" 'quit-window - "ZQ" 'evil-quit - "ZZ" 'quit-window)) + (local-leader + :keymaps 'org-mode-map + :infix "c" + "d" #'org-clock-display + "c" #'org-clock-in + "o" #'org-clock-out + "r" #'org-clock-report)) #+end_src -** empv -Emacs MPV bindings, with very cool controls for queuing files for -playing. +*** WAIT Org ref +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +For bibliographic stuff in $\LaTeX$ export. + #+begin_src emacs-lisp -(use-package empv +(use-package org-ref :straight t :defer t :init - (setq empv-audio-dir (list (expand-file-name "~/Media/audio") - ;; "/sshx:oldboy:/media/hdd/content/Audio" - ) - empv-video-dir (list (expand-file-name "~/Media/videos") - ;; "/sshx:oldboy:/media/hdd/content/Videos" - ) - empv-playlist-dir (expand-file-name "~/Media/playlists") - empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" "flac" "aac" "opus") - empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" "webm") - empv-radio-channels - '(("SomaFM - Groove Salad" . "http://www.somafm.com/groovesalad.pls") - ("SomaFM - Drone Zone" . "http://www.somafm.com/dronezone.pls") - ("SomaFM - Sonic Universe" . "http://www.somafm.com/sonicuniverse.pls") - ("SomaFM - Metal" . "http://www.somafm.com/metal.pls") - ("SomaFM - Vaporwaves" . "http://www.somafm.com/vaporwaves.pls") - ("SomaFM - DEFCON" . "http://www.somafm.com/defcon.pls") - ("SomaFM - The Trip" . "http://www.somafm.com/thetrip.pls")))) + (setq bibtex-files '("~/Text/bibliography.bib") + bibtex-completion-bibliography '("~/Text/bibliography.bib") + bibtex-completion-additional-search-fields '(keywords))) +#+end_src +**** Org ref ivy integration +Org ref requires ivy-bibtex to work properly with ivy, so we need to +set that up as well -(use-package empv-hydra - :after hydra - :general - (app-leader - "e" #'empv-hydra/body)) +#+begin_src emacs-lisp +(use-package ivy-bibtex + :straight t + :after org-ref + :config + (require 'org-ref-ivy)) #+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. +*** Org message +Org message allows for the use of org mode when composing mails, +generating HTML multipart emails. This integrates the WYSIWYG +experience with mail in Emacs while also providing powerful text +features with basically no learning curve (as long as you've already +learnt the basics of org). + #+begin_src emacs-lisp -(use-package gud - :general - :after hydra - :hydra - (gud-hydra - (:hint nil) "Hydra for GUD" - ("<" #'gud-up "Up" - :column "Stack") - (">" #'gud-down "Down" - :column "Stack") - ("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 "Breakpoints") - ("s" #'gud-step "Step" - :column "Control Flow") - ("t" #'gud-tbreak "Tbreak" - :column "Breakpoints") - ("u" #'gud-until "Until" - :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)) +(use-package org-msg + :straight t + :hook + (message-mode-hook . org-msg-mode) + (notmuch-message-mode-hook . org-msg-mode) + :config + (setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t tex:dvipng" + org-msg-greeting-name-limit 3) + + (add-to-list + 'org-msg-enforce-css + '(img latex-fragment-inline + ((transform . ,(format "translateY(-1px) scale(%.3f)" + (/ 1.0 (if (boundp 'preview-scale) + preview-scale 1.4)))) + (margin . "0 -0.35em"))))) #+end_src -** Jira +*** Org for evil +Evil org for some nice bindings. + #+begin_src emacs-lisp -(use-package jira - :straight (:host github :repo "unmonoqueteclea/jira.el") - :init - (setq jira-base-url "https://reframe.atlassian.net") - :general - (app-leader - "j" #'jira-issues) - (nmmap - :keymaps 'jira-issues-mode-map - "M-RET" #'jira-issues-actions-menu)) +(use-package evil-org + :straight t + :defer t + :hook (org-mode-hook . evil-org-mode)) #+end_src -* Languages -For a variety of (programming) languages Emacs comes with default -modes but this configures them as well as pulls any modes Emacs -doesn't come with. ** Makefile Defines an auto-insert for Makefiles. Assumes C but it's very easy to change it for C++. @@ -4145,12 +4165,12 @@ I may disagree with some. So I use it in a mode to mode basis. #+begin_src emacs-lisp (use-package evil-collection - :defer t - :hook (after-init-hook . evil-collection-init) + :after evil :straight t :init - (setq evil-collection-mode-list '(eww flycheck magit calendar notmuch - ibuffer proced calc image))) + ;; (setq evil-collection-mode-list '(eww flycheck magit calendar notmuch + ;; ibuffer proced calc image)) + ) #+end_src *** Evil number Increment/decrement a number at point like Vim does, but use bindings @@ -4436,6 +4456,9 @@ itself. The only feature left is describing changes... :general (leader "u" #'undo-tree-visualize) + (mmap + :keymaps 'undo-tree-visualizer-mode-map + "t" #'undo-tree-visualizer-toggle-timestamps) :init (setq undo-tree-auto-save-history t undo-tree-history-directory-alist backup-directory-alist) -- cgit v1.2.3-13-gbd6f