From 4c64e8de0a4aeb3b2520280e356174eafe066c7a Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Sun, 20 Oct 2024 20:31:11 +0100 Subject: Move around some sections to make flow clearer --- Emacs/.config/emacs/config.org | 6335 ++++++++++++++++++++-------------------- 1 file changed, 3183 insertions(+), 3152 deletions(-) diff --git a/Emacs/.config/emacs/config.org b/Emacs/.config/emacs/config.org index d7e919a..068c753 100644 --- a/Emacs/.config/emacs/config.org +++ b/Emacs/.config/emacs/config.org @@ -261,10 +261,9 @@ never used before, 3 seems to be a reasonable default. ("ravenmaiden" 6) (_ 3)))) #+end_src -* Core packages -Here I configure packages, internal and external, which define either -critical infrastructure for the rest of the configuration or provide -key functionality disassociated from any specific environment. +* Essential packages +External and internal packages absolutely necessary for the rest of +this configuration. ** General - Bindings package Vanilla Emacs has the ~bind-key~ function (and the ~bind-key*~ macro) for this, but [[*Evil - Vim Emulation][Evil]] has it's own @@ -468,8 +467,9 @@ a package for porting Vim's modal editing style to Emacs, called evil However there are a lot of packages in Vim that provide greater functionality, for example tpope's "vim-surround". Emacs has these capabilities out of the box, but there are further packages which -integrate them into Evil. -*** Evil core +integrate them into Evil. These are setup later in [[*Evil +additions][Evil additions]]. + Setup the evil package, with some opinionated settings: + Switch ~evil-upcase~ and ~evil-downcase~ because I use ~evil-upcase~ more @@ -529,116 +529,6 @@ Setup the evil package, with some opinionated settings: "e" #'transpose-sexps "l" #'transpose-lines)) #+end_src -*** Evil surround -A port for vim-surround, providing the ability to mutate delimiters -around some text. - -#+begin_src emacs-lisp -(use-package evil-surround - :after evil - :straight t - :config - (global-evil-surround-mode)) -#+end_src -*** Evil commentary -A port of vim-commentary, providing generalised commenting of objects. - -#+begin_src emacs-lisp -(use-package evil-commentary - :after evil - :straight t - :config - (evil-commentary-mode)) -#+end_src -*** Evil multi cursor -Setup for multi cursors in Evil mode, which is a bit of very nice -functionality. Don't let evil-mc setup it's own keymap because it -uses 'gr' as its prefix, which I don't like. - -#+begin_src emacs-lisp -(use-package evil-mc - :after evil - :straight t - :init - (defvar evil-mc-key-map (make-sparse-keymap)) - :general - (nmap - :infix "gz" - "m" 'evil-mc-make-all-cursors - "u" 'evil-mc-undo-last-added-cursor - "q" 'evil-mc-undo-all-cursors - "s" 'evil-mc-pause-cursors - "r" 'evil-mc-resume-cursors - "f" 'evil-mc-make-and-goto-first-cursor - "l" 'evil-mc-make-and-goto-last-cursor - "h" 'evil-mc-make-cursor-here - "j" 'evil-mc-make-cursor-move-next-line - "k" 'evil-mc-make-cursor-move-prev-line - "N" 'evil-mc-skip-and-goto-next-cursor - "P" 'evil-mc-skip-and-goto-prev-cursor - "n" 'evil-mc-skip-and-goto-next-match - "p" 'evil-mc-skip-and-goto-prev-match - "I" 'evil-mc-make-cursor-in-visual-selection-beg - "A" 'evil-mc-make-cursor-in-visual-selection-end - "d" #'evil-mc-make-and-goto-next-match) - :config - (global-evil-mc-mode)) -#+end_src -*** Evil multi edit -Evil-ME provides a simpler parallel editing experience within the same -buffer. I use it in-tandem with Evil-MC, where I use Evil-ME for -textual changes and Evil-MC for more complex motions. - -#+begin_src emacs-lisp -(use-package evil-multiedit - :straight t - :defer t - :init - (setq evil-multiedit-scope 'visible) - :general - (:states '(normal visual) - :keymaps 'override - "M-e" #'evil-multiedit-match-and-next - "M-E" #'evil-multiedit-match-and-prev)) -#+end_src -*** Evil collection -Provides a community based set of keybindings for most modes in -Emacs. I don't necessarily like all my modes having these bindings -though, as I may disagree with some. So I use it in a mode to mode basis. - -#+begin_src emacs-lisp -(use-package evil-collection - :straight t - :after evil) -#+end_src -*** Evil number -Increment/decrement a number at point like Vim does, but use bindings -that don't conflict with Emacs default. - -#+begin_src emacs-lisp -(use-package evil-numbers - :straight t - :defer t - :general - (nmmap - "+" #'evil-numbers/inc-at-pt - "-" #'evil-numbers/dec-at-pt)) -#+end_src -*** Evil goggles -Make it easier to notice edits and changes using Vim motions! -#+begin_src emacs-lisp -(use-package evil-goggles - :straight t - :after evil - :init - (setq evil-goggles-duration 0.1 - evil-goggles-blocking-duration 0.1 - evil-goggles-async-duration 0.9 - evil-goggles-default-face 'pulsar-cyan) - :config - (evil-goggles-mode) - (evil-goggles-use-diff-faces)) -#+end_src ** Text Completion Emacs is a text based interface. All commands use textual input, operate on text and produce text as output. A classic command is @@ -804,20 +694,6 @@ From https://jmthornton.net/blog/p/consult-line-isearch-history, taken (advice-add #'consult-line :after #'consult-line-isearch-history)) #+end_src -*** Amx -Amx is a fork of Smex that works to enhance the -~execute-extended-command~ interface. It provides a lot of niceties -such as presenting the key bind when looking for a command. - -#+begin_src emacs-lisp -(use-package amx - :straight t - :defer 2 - :init - (setq amx-backend 'auto) - :config - (amx-mode)) -#+end_src *** Orderless Orderless sorting method for completion, probably one of the best things ever. @@ -842,8 +718,7 @@ just setup some evil binds for company. :defer t :straight t :hook - (prog-mode-hook . company-mode) - (eshell-mode-hook . company-mode) + ((prog-mode-hook eshell-mode-hook) . company-mode) :general (imap :keymaps 'company-mode-map @@ -852,3589 +727,3745 @@ just setup some evil binds for company. "M-j" #'company-select-next "M-k" #'company-select-previous)) #+end_src -** Pretty symbols -Prettify symbols mode allows users to declare "symbols" that replace -text within certain modes. It's eye candy in most cases, but can aid -comprehension for symbol heavy languages. +** Hydra +Hydra is a great package by =abo-abo= (yes the same guy who made ivy +and swiper). Though not absolutely essential it provides an easy +interface option for keybindings which enhances their discoverability +and ease of use. There are two use-package declarations here: one for +~hydra~ itself, and the other for ~use-package-hydra~ which provides +the keyword ~:hydra~ in use-package declarations. -This configures a ~use-package~ keyword which makes declaring pretty -symbols for language modes incredibly easy. Checkout my [[*Emacs -lisp][Emacs lisp]] configuration for an example. +#+begin_src emacs-lisp +(use-package hydra + :straight t) + +(use-package use-package-hydra + :straight t) +#+end_src +* Aesthetics +General look and feel of Emacs, perhaps the most important of all the +sections here. +** Themes +I have both a dark and light theme for differing situations. Here I +configure a timer which ensures I have a light theme during the day +and dark theme at night. I wrote my own themes by copying stuff I +like from other themes then modifying them. The dark theme is in +[[file:elisp/personal-solarized-theme.el][this file]] and the light +theme is in [[file:elisp/personal-light-theme.el][this file]]. #+begin_src emacs-lisp -(use-package prog-mode - :demand t +(use-package custom + :defer t + :commands (+oreo/load-theme) + :hook (after-init-hook . +oreo/load-theme) :init - (setq prettify-symbols-unprettify-at-point t) + (setq custom-theme-directory (concat user-emacs-directory "elisp/")) + (defvar +oreo/theme-list `(personal-light personal-solarized)) + (defvar +oreo/theme 1) :config - (with-eval-after-load "use-package-core" - (add-to-list 'use-package-keywords ':pretty) - (defun use-package-normalize/:pretty (_name-symbol _keyword args) - args) + (defun +oreo/disable-other-themes () + "Disable all other themes in +OREO/THEME-LIST excluding ++OREO/THEME." + (cl-loop + for theme in +oreo/theme-list + for i from 0 + if (not (= i +oreo/theme)) + do (disable-theme theme))) - (defun use-package-handler/:pretty (name _keyword args rest state) - (use-package-concat - (use-package-process-keywords name rest state) - (mapcar - #'(lambda (arg) - (let ((mode (car arg)) - (rest (cdr arg))) - `(add-hook - ',mode - #'(lambda nil - (setq prettify-symbols-alist ',rest) - (prettify-symbols-mode))))) - args))))) + (defun +oreo/load-theme () + "Load +OREO/THEME, disabling all other themes to reduce conflict." + (mapc #'disable-theme custom-enabled-themes) + (+oreo/disable-other-themes) + (load-theme (nth +oreo/theme +oreo/theme-list) t)) + + (defun +oreo/switch-theme () + "Flip between different themes set in +OREO/THEME-ALIST." + (setq +oreo/theme (mod (+ 1 +oreo/theme) (length +oreo/theme-list))) + (+oreo/load-theme)) + + (+oreo/load-theme)) #+end_src +** Startup screen +The default startup screen is quite bad in all honesty. While for a +first time user it can be very helpful in running the tutorial and +finding more about Emacs, for someone who's already configured it +there isn't much point. -Here's a collection of keywords and possible associated symbols for -any prog language of choice. Mostly for reference and copying. +The scratch buffer is created at boot. When the splash screen isn't +enabled, it is the first buffer a user sees. By default, it is in +~lisp-interaction-mode~, which allows one to prototype Emacs Lisp +code. -#+begin_example -("null" . "Ø") -("list" . "ℓ") -("string" . "𝕊") -("char" . "ℂ") -("int" . "ℤ") -("float" . "ℝ") -("!" . "¬") -("for" . "Σ") -("return" . "≡") -("reduce" . "↓") -("map" . "→") -("some" . "∃") -("every" . "∃") -("lambda" . "λ") -("function" . "ƒ") -("<=" . "≤") -(">=" . "≥") -#+end_example -** Tabs -Tabs in vscode are just like buffers in Emacs but way slower and -harder to use. Tabs in Emacs are essentially window layouts, similar -to instances in Tmux. With this setup I can use tabs quite -effectively. +I mostly use the scratch buffer to hold snippets of code and to write +text (usually then copy-pasted into other applications). So +~text-mode~ is a good fit for that. + +2024-06-04: I use to load [[*Org mode][org-mode]] in the scratch +buffer and it added 2 seconds of load time, so let's just use +fundamental mode and call it a day. #+begin_src emacs-lisp -(use-package tab-bar +(use-package emacs :defer t - :hook (after-init-hook . tab-bar-mode) :init - (setq tab-bar-close-button-show nil - tab-bar-format '(tab-bar-format-history - tab-bar-format-tabs tab-bar-separator) - tab-bar-show 1 - tab-bar-auto-width t - tab-bar-auto-width-max '((100) 20) - tab-bar-auto-width-min '((20) 2)) - :general - (tab-leader - "R" #'tab-rename - "c" #'tab-close - "d" #'tab-close - "f" #'tab-detach - "h" #'tab-move-to - "j" #'tab-next - "k" #'tab-previous - "l" #'tab-move - "n" #'tab-new - "r" #'tab-switch - "w" #'tab-window-detach)) -#+end_src -** Registers -#+begin_src emacs-lisp -(use-package register + (setq inhibit-startup-screen t + inhibit-startup-echo-area-message user-login-name + initial-major-mode 'text-mode + initial-scratch-message "" + ring-bell-function 'ignore) :config - (defmacro +register/jump-to (reg) - `(proc (interactive) - (jump-to-register ,reg))) - :general - (nmmap - "m" #'point-to-register - "'" #'jump-to-register - "g1" (+register/jump-to "1") - "g2" (+register/jump-to "2") - "g3" (+register/jump-to "3") - "g4" (+register/jump-to "4") - "g5" (+register/jump-to "5") - "g6" (+register/jump-to "6") - "g7" (+register/jump-to "7") - "g8" (+register/jump-to "8") - "g9" (+register/jump-to "9"))) -#+end_src -** Auto typing -Snippets are a pretty nice way of automatically inserting code. Emacs -provides a few packages by default to do this, but there are great -packages to install as well. - -Abbrevs and skeletons make up a popular solution within Emacs default. -Abbrevs are for simple expressions wherein the only input is the key, -and the output is some Elisp function. They provide a lot of inbuilt -functionality and are quite useful. Skeletons, on the other hand, are -for higher level insertions with user influence. - -The popular external solution is Yasnippet. Yasnippet is a great -package for snippets, which I use heavily in programming and org-mode. -Here I setup the global mode for yasnippet and a collection of -snippets for ease of use. -*** Abbrevs -Just define a few abbrevs for various date-time operations. Also -define a macro that will assume a function for the expansion, helping -with abstracting a few things away. + (add-hook 'after-init-hook + (proc + (with-current-buffer "*scratch*" + (goto-char (point-max)) + (--> + (emacs-init-time) + (format "Emacs v%s - %s\n" emacs-version it) + (insert it)))))) +#+end_src +** Blinking cursor +Configure the blinking cursor. #+begin_src emacs-lisp -(use-package abbrev +(use-package frame :defer t - :hook - (prog-mode-hook . abbrev-mode) - (text-mode-hook . abbrev-mode) :init - (defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs) - `(progn - ,@(mapcar #'(lambda (abbrev) - `(define-abbrev - ,abbrev-table - ,(car abbrev) - "" - (proc (insert ,(cadr abbrev))))) - abbrevs))) - (setq save-abbrevs nil) + (setq blink-cursor-delay 0.2) :config - (+abbrev/define-abbrevs - global-abbrev-table - ("sdate" - (format-time-string "%Y-%m-%d" (current-time))) - ("stime" - (format-time-string "%H:%M:%S" (current-time))) - ("sday" - (format-time-string "%A" (current-time))) - ("smon" - (format-time-string "%B" (current-time))))) + (blink-cursor-mode)) #+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~. +** Mode line +The mode line is the little bar at the bottom of the buffer, just +above the minibuffer. It can store essentially any text, but +generally details about the current buffer (such as name, major mode, +etc) is placed there. + +The default mode-line is... disgusting. It displays information in an +unintelligible format and seems to smash together a bunch of +information without much care for ordering. Most heartbreaking is +that *anything* can seemingly append new information to it without any +purview, which is *REALLY* annoying. It can be very overstimulating +to look at, without even being that immediately informative. + +I've got a custom Emacs lisp package +([[file:elisp/better-mode-line.el][here]]) which sets up the default +mode line as a set of 3 segments: left, centre and right. It pads out +the mode line with space strings to achieve this. #+begin_src emacs-lisp -(use-package autoinsert +(use-package better-mode-line + :load-path "elisp/" :demand t - :hook (after-init-hook . auto-insert-mode) - :config - (with-eval-after-load "use-package-core" - (add-to-list 'use-package-keywords ':auto-insert) - (defun use-package-normalize/:auto-insert (_name-symbol _keyword args) - args) - (defun use-package-handler/:auto-insert (name _keyword args rest state) - (use-package-concat - (use-package-process-keywords name rest state) - (mapcar - #'(lambda (arg) - `(add-to-list - 'auto-insert-alist - ',arg)) - args))))) -#+end_src -*** Yasnippet -Look at the snippets [[file:../.config/yasnippet/snippets/][folder]] -for all snippets I've got. + :init + (defun +mode-line/evil-state () + "Returns either \"E\" if no evil-state is defined or the first character +of the evil state capitalised" + (with-eval-after-load "evil" + (if (bound-and-true-p evil-state) + (--> + (format "%s" evil-state) + (substring it 0 1) + (upcase it)) + "E"))) -#+begin_src emacs-lisp -(use-package yasnippet - :straight t - :defer t - :hook - (prog-mode-hook . yas-minor-mode) - (text-mode-hook . yas-minor-mode) - :general - (insert-leader - "i" #'yas-insert-snippet) + (setq better-mode-line/left-segment + '(" " ;; Left padding + (:eval + (when (mode-line-window-selected-p) + '("%l:%c" ;; Line and column count + " " + "%p" ;; Percentage into buffer + "[" ;; Evil state + (:eval + (+mode-line/evil-state)) + "]")))) + better-mode-line/centre-segment + '("%+" ;; Buffer state (changed or not) + "%b" ;; Buffer name + "(" ;; Major mode + (:eval (format "%s" major-mode)) + ")") + better-mode-line/right-segment + '((:eval + (when (mode-line-window-selected-p) + (format "%s %s" + (if (project-current) ;; Name of current project (if any) + (project-name + (project-current)) + "") + (if vc-mode ;; Project and Git branch + vc-mode + "")) + )) + mode-line-misc-info ;; Any other information + (:eval ;; Compilation mode errors + (if (eq major-mode 'compilation-mode) + compilation-mode-line-errors)) + " " ;; Right padding + )) :config - (yas-load-directory (no-littering-expand-etc-file-name - "yasnippet/snippets"))) + (better-mode-line/setup-mode-line)) #+end_src -*** Hydra -Hydra is a great package by =abo-abo= (yes the same guy who made ivy -and swiper). There are two use-package declarations here: one for -~hydra~ itself, and the other for ~use-package-hydra~ which provides -the keyword ~:hydra~ in use-package declarations. +** Fringes +Turning off borders in my window manager was a good idea, so I should +adjust the borders for Emacs, so called fringes. However, some things +like [[info:emacs#Compilation Mode][Compilation Mode]] do require +fringes to provide arrows on the left side of the window. Hence I +provide a minimal fringe style with only 10 pixels on the left +provided. #+begin_src emacs-lisp -(use-package hydra - :straight t) - -(use-package use-package-hydra - :straight t) +(fringe-mode (cons 10 0)) #+end_src -** Helpful -Helpful provides a modern interface for some common help commands. I -replace ~describe-function~, ~describe-variable~ and ~describe-key~ by -their helpful counterparts. +** Scrolling +When scrolling, editors generally try to keep the cursor on screen. +Emacs has some variables which ensure the cursor is a certain number +of lines above the bottom of the screen and below the top of the +screen when scrolling. Here I set the margin to 8 (so it'll start +correcting at 8) and scroll-conservatively to the same value so it'll +keep the cursor centred. + +I also setup the ~pixel-scroll-mode~ to make scrolling nicer looking. #+begin_src emacs-lisp -(use-package helpful - :straight t - :defer t - :commands (helpful-callable helpful-variable) - :general - ([remap describe-function] #'helpful-callable - [remap describe-variable] #'helpful-variable - [remap describe-key] #'helpful-key) - :display - ("\\*helpful.*" - (display-buffer-at-bottom) - (inhibit-duplicate-buffer . t) - (window-height . 0.25)) +(use-package emacs + :init + (setq scroll-conservatively 8 + scroll-margin 8 + pixel-dead-time nil + pixel-scroll-precision-use-momentum nil + pixel-resolution-fine-flag t + fast-but-imprecise-scrolling t) :config - (evil-define-key 'normal helpful-mode-map "q" #'quit-window)) + (pixel-scroll-mode t) + (pixel-scroll-precision-mode t)) #+end_src -** Avy and Ace -Avy is a package that provides "jump" functions. Given some input, -the current buffer is scanned and any matching candidates are given a -tag which the user can input to perform some action (usually moving -the cursor to that point). -*** Avy core -Setup avy with leader. As I use ~avy-goto-char-timer~ a lot, use the -~C-s~ bind which replaces isearch. Switch isearch to M-s in case I -need to use it. +** Display line numbers +I don't really like line numbers, I find them similar to +[[*Fringes][fringes]] (useless space), but at least it provides some +information. Sometimes it can help with doing repeated commands so a +toggle option is necessary. #+begin_src emacs-lisp -(use-package avy - :straight t +(use-package display-line-numbers :defer t + :hook ((prog-mode-hook text-mode-hook) . display-line-numbers-mode) + :commands display-line-numbers-mode :general - (nmmap - :keymaps 'override - "C-s" #'avy-goto-char-timer - "M-s" #'isearch-forward - "gp" #'avy-copy-region - "gP" #'avy-move-region - "gl" #'avy-goto-line - "gw" #'avy-goto-word-1)) + (mode-leader + "l" #'display-line-numbers-mode) + :init + (setq-default display-line-numbers-type 'relative)) #+end_src -*** Ace window -Though evil provides a great many features in terms of window -management, ace window can provide some nicer chords for higher -management of windows (closing, switching, etc). - +** Pulsar +Similar to how [[*Evil goggles][Evil goggles]] highlights Evil +actions, pulsar provides more highlighting capabilities. Made by my +favourite Greek philosopher, Prot. #+begin_src emacs-lisp -(use-package ace-window +(use-package pulsar :straight t - :defer t - :custom - (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) - :general - (nmmap - [remap evil-window-next] #'ace-window)) + :init + (setq pulsar-face 'pulsar-cyan + pulsar-pulse-functions + '(next-buffer + previous-buffer + fill-paragraph + drag-stuff-right + drag-stuff-left + drag-stuff-up + drag-stuff-down + evil-goto-first-line + evil-goto-line + evil-scroll-down + evil-scroll-up + evil-scroll-page-down + evil-scroll-page-up + evil-window-left + evil-window-right + evil-window-up + evil-window-down + evil-forward-paragraph + evil-backward-paragraph + evil-fill-and-move + evil-join + org-forward-paragraph + org-backward-paragraph + org-fill-paragraph)) + :config + (pulsar-global-mode 1)) #+end_src -*** Ace link -Avy-style link following! +** Zoom +Zoom provides a very useful capability: dynamic resizing of windows +based on which one is active. I prefer larger font sizes but make it +too large and it's difficult to have multiple buffers side by side. +This package allows larger font sizes and still have multiple buffers +side by side. #+begin_src emacs-lisp -(use-package ace-link +(use-package zoom :straight t :defer t - :general - (nmmap - :keymaps 'override - "gL" #'ace-link)) -#+end_src -** Save place -Saves current place in a buffer permanently, so on revisiting the file -(even in a different Emacs instance) you go back to the place you were -at last. - -#+begin_src emacs-lisp -(use-package saveplace - :demand t - :config - (save-place-mode)) + :hook (after-init-hook . zoom-mode) + :init + (setq zoom-size '(90 . 25))) #+end_src -** Recentf -Recentf provides a method of keeping track of recently opened files. +** Hide mode line +Custom minor mode to toggle the mode line. Check it out at +[[file:elisp/hide-mode-line.el][elisp/hide-mode-line.el]]. #+begin_src emacs-lisp -(use-package recentf +(use-package hide-mode-line + :load-path "elisp/" :defer t - :hook (after-init-hook . recentf-mode) :general - (file-leader - "r" #'recentf)) + (mode-leader + "m" #'global-hide-mode-line-mode)) #+end_src -** Memory-report -New feature of Emacs-29, gives a rough report of memory usage with -some details. Useful to know on a long Emacs instance what could be -eating up memory. +** Olivetti +Olivetti provides a focus mode for Emacs, which makes it look a bit +nicer. It uses margins by default and centres using fill-column. I +actually really like olivetti mode particularly with my [[*Mode +line][centred mode-line]], so I also define a global minor mode which +enables it in all but the minibuffer. #+begin_src emacs-lisp -(use-package memory-report +(use-package olivetti + :straight t :defer t :general - (leader - "qm" #'memory-report)) + (mode-leader + "o" #'olivetti-global-mode) + :init + (setq-default olivetti-body-width nil + olivetti-minimum-body-width 100 + olivetti-style nil) + :config + (define-globalized-minor-mode olivetti-global-mode olivetti-mode + (lambda nil (unless (or (minibufferp) + (string= (buffer-name) "*which-key*")) + (olivetti-mode 1))))) #+end_src -** Drag Stuff -Drag stuff around, like my favourite russian programmer (Tsoding). -Useful mechanism which works better than any vim motion. +** All the Icons +Nice set of icons, for even more emojis. #+begin_src emacs-lisp -(use-package drag-stuff +(use-package all-the-icons :straight t :defer t :general - (nmmap - "C-M-h" #'drag-stuff-left - "C-M-j" #'drag-stuff-down - "C-M-k" #'drag-stuff-up - "C-M-l" #'drag-stuff-right)) + (insert-leader + "e" #'all-the-icons-insert)) #+end_src -** Searching common directories -Using [[file:elisp/search.el][search.el]] I can search a set of -directories particularly efficiently. +** Pretty symbols +Prettify symbols mode allows users to declare "symbols" that replace +text within certain modes. It's eye candy in most cases, but can aid +comprehension for symbol heavy languages. + +This configures a ~use-package~ keyword which makes declaring pretty +symbols for language modes incredibly easy. Checkout my [[*Emacs +lisp][Emacs lisp]] configuration for an example. #+begin_src emacs-lisp -(use-package search +(use-package prog-mode + :demand t + :init + (setq prettify-symbols-unprettify-at-point t) + :config + (with-eval-after-load "use-package-core" + (add-to-list 'use-package-keywords ':pretty) + (defun use-package-normalize/:pretty (_name-symbol _keyword args) + args) + + (defun use-package-handler/:pretty (name _keyword args rest state) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (arg) + (let ((mode (car arg)) + (rest (cdr arg))) + `(add-hook + ',mode + #'(lambda nil + (setq prettify-symbols-alist ',rest) + (prettify-symbols-mode))))) + args))))) +#+end_src + +Here's a collection of keywords and possible associated symbols for +any prog language of choice. Mostly for reference and copying. + +#+begin_example +("null" . "Ø") +("list" . "ℓ") +("string" . "𝕊") +("char" . "ℂ") +("int" . "ℤ") +("float" . "ℝ") +("!" . "¬") +("for" . "Σ") +("return" . "≡") +("reduce" . "↓") +("map" . "→") +("some" . "∃") +("every" . "∃") +("lambda" . "λ") +("function" . "ƒ") +("<=" . "≤") +(">=" . "≥") +#+end_example +* 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 - :load-path "elisp/" + :hook ((org-mode-hook text-mode-hook) . flyspell-mode) :general - (search-leader - "a" #'+search/search-all) - (file-leader - "p" #'+search/find-file)) + (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 -** Separedit -Edit anything anywhere all at once! +** 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 separedit +(use-package whitespace :defer t - :straight t :general - (leader "e" #'separedit) + (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) :init - (setq separedit-default-mode 'org-mode - separedit-remove-trailing-spaces-in-comment t)) + (setq whitespace-line-column nil + whitespace-style '(face empty spaces tabs newline trailing + lines-char tab-mark))) #+end_src -* Aesthetics -General look and feel of Emacs (mostly disabling stuff I don't like). -** Themes -I have both a dark and light theme for differing situations. Here I -configure a timer which ensures I have a light theme during the day -and dark theme at night. I wrote my own themes by copying stuff I -like from other themes then modifying them. The dark theme is in -[[file:elisp/personal-solarized-theme.el][this file]] and the light -theme is in [[file:elisp/personal-light-theme.el][this file]]. +** 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 custom - :defer t - :commands (+oreo/load-theme) - :hook (after-init-hook . +oreo/load-theme) +(use-package emacs + :hook + (text-mode-hook . auto-fill-mode) :init - (setq custom-theme-directory (concat user-emacs-directory "elisp/")) - (defvar +oreo/theme-list `(personal-light personal-solarized)) - (defvar +oreo/theme 1) - :config - (defun +oreo/disable-other-themes () - "Disable all other themes in +OREO/THEME-LIST excluding -+OREO/THEME." - (cl-loop - for theme in +oreo/theme-list - for i from 0 - if (not (= i +oreo/theme)) - do (disable-theme theme))) + (setq-default fill-column 80) + (add-hook 'text-mode-hook (proc (setq-local fill-column 70))) + ((c-mode-hook c++-mode-hook haskell-mode-hook python-mode-hook + org-mode-hook text-mode-hook js-mode-hook) + . display-fill-column-indicator-mode)) +#+end_src +** Visual line mode +When dealing with really long lines I have a specific taste. I don't +want text to just go off the screen, such that I have to move the +cursor forward in the line to see later content - I want line +wrapping. Emacs provides ~truncate-lines~ for line wrapping but it +cuts words, which isn't very nice as that cut word spans two lines. +Instead I want Emacs to cut by word, which is where visual-line-mode +comes in. Since I may experience really long lines anywhere, it +should be enabled globally. - (defun +oreo/load-theme () - "Load +OREO/THEME, disabling all other themes to reduce conflict." - (mapc #'disable-theme custom-enabled-themes) - (+oreo/disable-other-themes) - (load-theme (nth +oreo/theme +oreo/theme-list) t)) - - (defun +oreo/switch-theme () - "Flip between different themes set in +OREO/THEME-ALIST." - (setq +oreo/theme (mod (+ 1 +oreo/theme) (length +oreo/theme-list))) - (+oreo/load-theme)) - - (+oreo/load-theme)) +#+begin_src emacs-lisp +(use-package emacs + :demand t + :config + (global-visual-line-mode t)) #+end_src -** Startup screen -The default startup screen is quite bad in all honesty. While for a -first time user it can be very helpful in running the tutorial and -finding more about Emacs, for someone who's already configured it -there isn't much point. - -The scratch buffer is created at boot. When the splash screen isn't -enabled, it is the first buffer a user sees. By default, it is in -~lisp-interaction-mode~, which allows one to prototype Emacs Lisp -code. - -I mostly use the scratch buffer to hold snippets of code and to write -text (usually then copy-pasted into other applications). So -~text-mode~ is a good fit for that. +** Show-paren-mode +When the cursor is over a parenthesis, highlight the other member of +the pair. -2024-06-04: I use to load [[*Org mode][org-mode]] in the scratch -buffer and it added 2 seconds of load time, so let's just use -fundamental mode and call it a day. +#+begin_src emacs-lisp +(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 emacs +(use-package smartparens + :straight t :defer t - :init - (setq inhibit-startup-screen t - inhibit-startup-echo-area-message user-login-name - initial-major-mode 'text-mode - initial-scratch-message "" - ring-bell-function 'ignore) + :hook + (prog-mode-hook . smartparens-mode) + (text-mode-hook . smartparens-mode) :config - (add-hook 'after-init-hook - (proc - (with-current-buffer "*scratch*" - (goto-char (point-max)) - (--> - (emacs-init-time) - (format "Emacs v%s - %s\n" emacs-version it) - (insert it)))))) -#+end_src -** Blinking cursor -Configure the blinking cursor. + (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 frame +(use-package powerthesaurus :defer t - :init - (setq blink-cursor-delay 0.2) - :config - (blink-cursor-mode)) + :straight t + :general + (search-leader + "w" #'powerthesaurus-transient)) #+end_src -** Mode line -The mode line is the little bar at the bottom of the buffer, just -above the minibuffer. It can store essentially any text, but -generally details about the current buffer (such as name, major mode, -etc) is placed there. - -The default mode-line is... disgusting. It displays information in an -unintelligible format and seems to smash together a bunch of -information without much care for ordering. Most heartbreaking is -that *anything* can seemingly append new information to it without any -purview, which is *REALLY* annoying. It can be very overstimulating -to look at, without even being that immediately informative. +** lorem ipsum +Sometimes you need placeholder text for some UI or document. Pretty +easy to guess what text I'd use. -I've got a custom Emacs lisp package -([[file:elisp/better-mode-line.el][here]]) which sets up the default -mode line as a set of 3 segments: left, centre and right. It pads out -the mode line with space strings to achieve this. +#+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 better-mode-line - :load-path "elisp/" +(use-package autoinsert :demand t - :init - (defun +mode-line/evil-state () - "Returns either \"E\" if no evil-state is defined or the first character -of the evil state capitalised" - (with-eval-after-load "evil" - (if (bound-and-true-p evil-state) - (--> - (format "%s" evil-state) - (substring it 0 1) - (upcase it)) - "E"))) - - (setq better-mode-line/left-segment - '(" " ;; Left padding - (:eval - (when (mode-line-window-selected-p) - '("%l:%c" ;; Line and column count - " " - "%p" ;; Percentage into buffer - "[" ;; Evil state - (:eval - (+mode-line/evil-state)) - "]")))) - better-mode-line/centre-segment - '("%+" ;; Buffer state (changed or not) - "%b" ;; Buffer name - "(" ;; Major mode - (:eval (format "%s" major-mode)) - ")") - better-mode-line/right-segment - '((:eval - (when (mode-line-window-selected-p) - (format "%s %s" - (if (project-current) ;; Name of current project (if any) - (project-name - (project-current)) - "") - (if vc-mode ;; Project and Git branch - vc-mode - "")) - )) - mode-line-misc-info ;; Any other information - (:eval ;; Compilation mode errors - (if (eq major-mode 'compilation-mode) - compilation-mode-line-errors)) - " " ;; Right padding - )) + :hook (after-init-hook . auto-insert-mode) :config - (better-mode-line/setup-mode-line)) + (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 -** Fringes -Turning off borders in my window manager was a good idea, so I should -adjust the borders for Emacs, so called fringes. However, some things -like [[info:emacs#Compilation Mode][Compilation Mode]] do require -fringes to provide arrows on the left side of the window. Hence I -provide a minimal fringe style with only 10 pixels on the left -provided. +* 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 -#+begin_src emacs-lisp -(fringe-mode (cons 10 0)) -#+end_src -** Scrolling -When scrolling, editors generally try to keep the cursor on screen. -Emacs has some variables which ensure the cursor is a certain number -of lines above the bottom of the screen and below the top of the -screen when scrolling. Here I set the margin to 8 (so it'll start -correcting at 8) and scroll-conservatively to the same value so it'll -keep the cursor centred. +Eldoc box makes the help buffer a hovering box instead of printing it +in the minibuffer. A lot cleaner. -I also setup the ~pixel-scroll-mode~ to make scrolling nicer looking. +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 emacs +(use-package eldoc + :defer t + :hook (prog-mode-hook . eldoc-mode) :init - (setq scroll-conservatively 8 - scroll-margin 8 - pixel-dead-time nil - pixel-scroll-precision-use-momentum nil - pixel-resolution-fine-flag t - fast-but-imprecise-scrolling t) - :config - (pixel-scroll-mode t) - (pixel-scroll-precision-mode t)) + (global-eldoc-mode 1) + :general + (leader + "h>" #'eldoc-doc-buffer)) #+end_src -** Display line numbers -I don't really like line numbers, I find them similar to -[[*Fringes][fringes]] (useless space), but at least it provides some -information. Sometimes it can help with doing repeated commands so a -toggle option is necessary. +** 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 display-line-numbers +(use-package flycheck + :straight t :defer t - :hook ((prog-mode-hook text-mode-hook) . display-line-numbers-mode) - :commands display-line-numbers-mode + :commands (flycheck-mode flycheck-list-errors) :general (mode-leader - "l" #'display-line-numbers-mode) + "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 display-line-numbers-type 'relative)) -#+end_src -** Pulsar -Similar to how [[*Evil goggles][Evil goggles]] highlights Evil -actions, pulsar provides more highlighting capabilities. Made by my -favourite Greek philosopher, Prot. -#+begin_src emacs-lisp -(use-package pulsar - :straight t - :init - (setq pulsar-face 'pulsar-cyan - pulsar-pulse-functions - '(next-buffer - previous-buffer - fill-paragraph - drag-stuff-right - drag-stuff-left - drag-stuff-up - drag-stuff-down - evil-goto-first-line - evil-goto-line - evil-scroll-down - evil-scroll-up - evil-scroll-page-down - evil-scroll-page-up - evil-window-left - evil-window-right - evil-window-up - evil-window-down - evil-forward-paragraph - evil-backward-paragraph - evil-fill-and-move - evil-join - org-forward-paragraph - org-backward-paragraph - org-fill-paragraph)) + (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 - (pulsar-global-mode 1)) + (with-eval-after-load "evil-collection" + (evil-collection-flycheck-setup))) #+end_src -** Zoom -Zoom provides a very useful capability: dynamic resizing of windows -based on which one is active. I prefer larger font sizes but make it -too large and it's difficult to have multiple buffers side by side. -This package allows larger font sizes and still have multiple buffers -side by side. +** 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. #+begin_src emacs-lisp -(use-package zoom - :straight t +(use-package eglot :defer t - :hook (after-init-hook . zoom-mode) + :general + (code-leader + :keymaps 'eglot-mode-map + "f" #'eglot-format + "a" #'eglot-code-actions + "R" #'eglot-reconnect) :init - (setq zoom-size '(90 . 25))) + (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 -** Hide mode line -Custom minor mode to toggle the mode line. Check it out at -[[file:elisp/hide-mode-line.el][elisp/hide-mode-line.el]]. +** Indentation +By default, turn off tabs and set the tab width to two. #+begin_src emacs-lisp -(use-package hide-mode-line - :load-path "elisp/" - :defer t - :general - (mode-leader - "m" #'global-hide-mode-line-mode)) +(setq-default indent-tabs-mode nil + tab-width 2) #+end_src -** Olivetti -Olivetti provides a focus mode for Emacs, which makes it look a bit -nicer. It uses margins by default and centres using fill-column. I -actually really like olivetti mode particularly with my [[*Mode -line][centred mode-line]], so I also define a global minor mode which -enables it in all but the minibuffer. +However, if necessary later, define a function that may activate tabs locally. #+begin_src emacs-lisp -(use-package olivetti +(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 hl-todo :straight t - :defer t - :general - (mode-leader - "o" #'olivetti-global-mode) + :after prog-mode + :hook (prog-mode-hook . hl-todo-mode) :init - (setq-default olivetti-body-width nil - olivetti-minimum-body-width 100 - olivetti-style nil) - :config - (define-globalized-minor-mode olivetti-global-mode olivetti-mode - (lambda nil (unless (or (minibufferp) - (string= (buffer-name) "*which-key*")) - (olivetti-mode 1))))) + (setq hl-todo-keyword-faces + '(("TODO" . "#E50000") + ("WIP" . "#ffa500") + ("NOTE" . "#00CC00") + ("FIXME" . "#d02090")))) #+end_src -** All the Icons -Nice set of icons, for even more emojis. +** Hide-show mode +Turn on ~hs-minor-mode~ for all prog-mode. This provides folds for +free. #+begin_src emacs-lisp -(use-package all-the-icons - :straight t +(use-package hideshow :defer t - :general - (insert-leader - "e" #'all-the-icons-insert)) + :hook (prog-mode-hook . hs-minor-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. -** 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. +** 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 eww - :defer t - :general - (app-leader - "w" #'eww) - (nmmap - :keymaps 'eww-mode-map - "w" #'evil-forward-word-begin - "Y" #'eww-copy-page-url) - :config - (with-eval-after-load "evil-collection" - (evil-collection-eww-setup))) +(use-package aggressive-indent + :straight t + :hook (python-mode-hook . aggressive-indent-mode) + :hook (emacs-lisp-mode-hook . aggressive-indent-mode) + :hook (lisp-mode-hook . aggressive-indent-mode)) #+end_src -** Magit -Magit is *the* git porcelain for Emacs, which perfectly encapsulates -the git CLI. It's so good that some people are use Emacs just for it. -It's difficult to describe it well without using it and it integrates -so well with Emacs that there is very little need to use the git CLI -ever. +** 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. -In this case I just need to setup the bindings for it. Also, define -an auto insert for commit messages so that I don't need to write -everything myself. +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. #+begin_src emacs-lisp -(use-package transient - :defer t - :straight (:host github :repo "magit/transient" :tag "v0.7.5")) - -(use-package magit - :straight (:host github :repo "magit/magit" :tag "v4.1.0") +(use-package compile :defer t :display - ("magit:.*" - (display-buffer-same-window) - (inhibit-duplicate-buffer . t)) - ("magit-diff:.*" - (display-buffer-below-selected)) - ("magit-log:.*" - (display-buffer-same-window)) + ("\\*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 - "g" '(magit-dispatch :which-key "Magit")) + "j" #'move-error-hydra/next-error + "k" #'move-error-hydra/previous-error) (code-leader - "b" #'magit-blame) + "c" #'compile + "r" #'recompile) + (nmap + "M-r" #'recompile) + (:keymaps 'compilation-mode-map + "g" nil) ;; by default this is recompile + (nmmap + :keymaps 'compilation-mode-map + "c" #'recompile) :init - (setq vc-follow-symlinks t - magit-blame-echo-style 'lines - magit-copy-revision-abbreviated t) + (setq compilation-scroll-output 'first-error + compilation-context-lines nil + next-error-highlight 'fringe-arrow) :config - (with-eval-after-load "evil" - (evil-set-initial-state 'magit-status-mode 'motion)) - (with-eval-after-load "evil-collection" - (evil-collection-magit-setup))) + (add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)) #+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". +** 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 calendar +(use-package xref :defer t - :commands (+calendar/copy-date +calendar/toggle-calendar) :display - ("\\*Calendar\\*" + ("\\*xref\\*" (display-buffer-at-bottom) (inhibit-duplicate-buffer . t) - (window-height . 0.17)) + (window-height . 0.3)) :general - (nmmap - :keymaps 'calendar-mode-map - "Y" #'+calendar/copy-date) - (app-leader - "d" #'calendar) - :config - (with-eval-after-load "evil-collection" - (evil-collection-calendar-setup)) - (defun +calendar/copy-date () - "Copy date under cursor into kill ring." - (interactive) - (if (use-region-p) - (call-interactively #'kill-ring-save) - (let ((date (calendar-cursor-to-date))) - (when date - (setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))) - (kill-new (format-time-string "%Y-%m-%d" date))))))) + (code-leader + "t" '(nil :which-key "Tags")) + (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)) #+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. +** 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. -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. +Here I: ++ Bind ~project-prefix-map~ to "p" ++ write a TAGS command, mimicking projectile's one, so I can quickly + generate them. + + Bind that to "pr" #+begin_src emacs-lisp -(use-package notmuch - :straight t +(use-package project :defer t - :commands (notmuch +mail/flag-thread) :general - (app-leader "m" #'notmuch) - (nmap - :keymaps 'notmuch-search-mode-map - "f" #'+mail/flag-thread) - :init - (defconst +mail/local-dir (no-littering-expand-var-file-name "mail/")) - (setq notmuch-show-logo nil - notmuch-search-oldest-first nil - notmuch-hello-sections '(notmuch-hello-insert-saved-searches - notmuch-hello-insert-alltags - notmuch-hello-insert-recent-searches) - notmuch-archive-tags '("-inbox" "-unread" "+archive") - message-auto-save-directory +mail/local-dir - message-directory +mail/local-dir) - (with-eval-after-load "evil-collection" - (evil-collection-notmuch-setup)) + (:keymaps 'project-prefix-map + "r" #'+project/generate-tags) + (leader + "p" `(,project-prefix-map :which-key "Project")) :config - (defun +mail/flag-thread (&optional unflag beg end) - (interactive (cons current-prefix-arg (notmuch-interactive-region))) - (notmuch-search-tag - (notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end) - (when (eq beg end) - (notmuch-search-next-thread)))) -#+end_src -*** Smtpmail -Setup the smtpmail package, which is used when sending mail. Mostly -custom configuration for integration with other parts of Emacs' mail -system. + (defun +project/command (folder) + (format "ctags -Re -f %sTAGS %s*" + folder folder)) -#+begin_src emacs-lisp -(use-package smtpmail - :defer t - :commands mail-send - :init - (setq-default - smtpmail-smtp-server "mail.aryadevchavali.com" - smtpmail-smtp-user "aryadev" - smtpmail-smtp-service 587 - smtpmail-stream-type 'starttls - send-mail-function #'smtpmail-send-it - message-send-mail-function #'smtpmail-send-it)) -#+end_src -*** Mail signature using fortune -Generate a mail signature using the ~fortune~ executable. Pretty -cool! + (defun +project/root () + (if (project-current) + (project-root (project-current)) + default-directory)) -#+begin_src emacs-lisp -(use-package fortune - :init - (setq fortune-dir "/usr/share/fortune" - fortune-file "/usr/share/fortune/cookie") - :config - (defvar +mail/signature "---------------\nAryadev Chavali\n---------------\n%s") - (defun +mail/make-signature () + (defun +project/generate-tags () (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))))) + (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 -** Dired -Dired: Directory editor for Emacs. An incredibly nifty piece of -software which deeply integrates with Emacs as a whole. I can't think -of a better file management tool than this. - -Here I setup dired with a few niceties -+ Hide details by default (no extra stuff from ~ls~) -+ Omit dot files by default (using ~dired-omit-mode~) -+ If I have two dired windows open, moving or copying files in one - dired instance will automatically target the other dired window - (~dired-dwim~) -+ If opening an application on a PDF file, suggest ~zathura~ -+ Examine all the subdirectories within the same buffer - (~+dired/insert-all-subdirectories~) +** 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 dired +(use-package devdocs + :straight t :defer t - :commands (dired find-dired) - :hook - (dired-mode-hook . auto-revert-mode) - (dired-mode-hook . dired-hide-details-mode) - (dired-mode-hook . dired-omit-mode) - :init - (setq-default dired-listing-switches "-AFBlu --group-directories-first" - dired-omit-files "^\\." ; dotfiles - dired-omit-verbose nil - dired-dwim-target t - dired-kill-when-opening-new-dired-buffer t) - (with-eval-after-load "evil-collection" - (evil-collection-dired-setup)) :general - (nmmap - :keymaps 'dired-mode-map - "SPC" nil - "SPC ," nil - "(" #'dired-hide-details-mode - ")" #'dired-omit-mode - "T" #'dired-create-empty-file - "H" #'dired-up-directory - "L" #'dired-find-file) - (leader - "D" #'dired-jump) - (dir-leader - "f" #'find-dired - "d" #'dired - "D" #'dired-other-window - "i" #'image-dired - "b" `(,(proc (interactive) - (dired "~/Text/Books/")) - :which-key "Open Books")) - (local-leader - :keymaps 'dired-mode-map - "i" #'dired-maybe-insert-subdir - "I" #'+dired/insert-all-subdirectories - "o" #'dired-omit-mode - "k" #'dired-prev-subdir - "j" #'dired-next-subdir - "K" #'dired-kill-subdir - "m" #'dired-mark-files-regexp - "u" #'dired-undo) - :config - (add-multiple-to-list dired-guess-shell-alist-user - '("\\.pdf\\'" "zathura") - '("\\.epub\\'" "zathura") - '("\\.jpg\\'" "feh") - '("\\.png\\'" "feh") - '("\\.webm\\'" "mpv") - '("\\.mp[34]\\'" "mpv") - '("\\.mkv\\'" "mpv")) - (defun +dired/insert-all-subdirectories () - "Insert all subdirectories currently viewable." - (interactive) - (dired-mark-directories nil) - (mapc #'dired-insert-subdir (dired-get-marked-files)) - (dired-unmark-all-marks))) + (file-leader + "d" #'devdocs-lookup)) #+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. +** 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 dired +(use-package rainbow-delimiters :defer t - :init - (setq image-dired-external-viewer "nsxiv") + :straight t :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)) + (mode-leader "r" #'rainbow-delimiters-mode) + :hook + ((lisp-mode-hook emacs-lisp-mode-hook racket-mode-hook) . rainbow-delimiters-mode)) #+end_src -*** fd-dired -Uses fd for finding file results in a directory: ~find-dired~ -> -~fd-dired~. +** 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 fd-dired - :straight t - :after dired +(use-package license + :demand t + :load-path "elisp/" :general - (dir-leader - "g" #'fd-dired)) + (insert-leader + "l" #'+license/insert-copyright-notice + "L" #'+license/insert-complete-license)) #+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. +** diff mode +Oh diffs; the way of the ancient ones. Nowadays we use our newfangled +"pull requests" and "cool web interfaces" to manage changes in our +code repositories, but old school projects use patches to make code +changes. They're a pain to distribute and can be very annoying to use +when applying them to code. Even then I somewhat like patches, if +only for their simplicity. -#+begin_src emacs-lisp -(use-package wdired - :after dired - :hook (wdired-mode-hook . undo-tree-mode) - :general - (nmmap - :keymaps 'dired-mode-map - "W" #'wdired-change-to-wdired-mode) - (nmmap - :keymaps 'wdired-mode-map - "ZZ" #'wdired-finish-edit - "ZQ" #'wdired-abort-changes) - :config - (eval-after-load "evil" - ;; 2024-09-07: Why does evil-set-initial-state returning a list of modes for - ;; normal state make eval-after-load evaluate as if it were an actual - ;; expression? - (progn (evil-set-initial-state 'wdired-mode 'normal) - nil))) -#+end_src -*** dired-rsync -Rsync is a great way of transferring files around *nix machines, and I -use dired for all my file management concerns. So I should be able to -rsync stuff around if I want. +[[https://git.aryadevchavali.com/dwm][dwm]] uses patches for adding +new features and Emacs has great functionality to work with patches +effectively. Here I configure ~diff-mode~, which provides most of +this cool stuff, to be a bit more ergonomic with ~evil~. #+begin_src emacs-lisp -(use-package dired-rsync - :straight t - :after dired +(use-package diff-mode :general (nmmap - :keymaps 'dired-mode-map - "M-r" #'dired-rsync)) + :keymaps 'diff-mode-map + "}" #'diff-hunk-next + "{" #'diff-hunk-prev + "RET" #'diff-goto-source)) #+end_src -** EShell -*** Why EShell? -EShell is an integrated shell environment for Emacs, written in Emacs -Lisp. Henceforth I will argue that it is the best shell/command -interpreter to use in Emacs, so good that you should eschew any second -class terminal emulators (~term~, ~shell~, etc). - -EShell is unlike the other alternatives in Emacs as it's a /shell/ -first, not a terminal emulator (granted, with the ability to spoof -some aspects of a terminal emulator). - -The killer benefits of EShell (which would appeal particularly to an -Emacs user) are a direct consequence of EShell being written in Emacs -Lisp: -- strong integration with Emacs utilities (such as ~dired~, - ~find-file~, any read functions, etc) -- very extensible, easy to write new commands which leverage Emacs - commands as well as external utilities -- agnostic of platform: "eshell/cd" will call the underlying change - directory function for you, so commands will (usually) mean the same - thing regardless of platform - - this means as long as Emacs can run on an operating system, one - may run EShell -- mixing of Lisp and shell commands, with piping! - -However, my favourite feature of EShell is the set of evaluators that -run on command input. Some of the benefits listed above come as a -consequence of this powerful feature. - -The main evaluator for any expression for EShell evaluates an -expression by testing the first symbol against different namespaces. -The namespaces are ordered such that if a symbol is not found in one, -the next namespace is tested. These namespaces are: -- alias (defined in the [[file:.config/eshell/aliases][aliases - file]]) -- "built-in" command i.e. in the ~eshell/~ namespace of functions -- external command -- Lisp function +* 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 -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. +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. -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. ++ 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) #+begin_src emacs-lisp -(use-package eshell +(use-package org :defer t - :display - ("\\*eshell\\*" - (display-buffer-same-window) - (reusable-frames . t)) :init - (defun +eshell/banner-message () - (concat (shell-command-to-string "fortune") "\n")) - - (setq eshell-cmpl-ignore-case t - eshell-cd-on-directory t - eshell-cd-shows-directory nil - eshell-highlight-prompt nil - eshell-banner-message '(+eshell/banner-message)) - - (defun +eshell/good-clear () - (interactive) - (eshell/clear-scrollback) - (eshell-send-input)) - - (add-hook - 'eshell-mode-hook - (defun +eshell/--setup-keymap nil - (interactive) - (general-def - :states '(normal insert visual) - :keymaps 'eshell-mode-map - "M-j" #'eshell-next-prompt - "M-k" #'eshell-previous-prompt - "C-j" #'eshell-next-matching-input-from-input - "C-k" #'eshell-previous-matching-input-from-input) - - (local-leader - :keymaps 'eshell-mode-map - "g" (proc (interactive) - (let ((buffer (current-buffer))) - (eshell/goto) - (with-current-buffer buffer - (eshell-send-input)))) - "l" (proc (interactive) - (eshell-return-to-prompt) - (insert "ls") - (eshell-send-input)) - "c" #'+eshell/good-clear - "k" #'eshell-kill-process)))) -#+end_src -*** EShell prompt -Here I use my external library -[[file:elisp/eshell-prompt.el][eshell-prompt]], which provides a -dynamic prompt for EShell. Current features include: -- Git repository details (with difference from remote and number of - modified files) -- Current date and time -- A coloured prompt character which changes colour based on the exit - code of the previous command - -NOTE: I don't defer this package because it doesn't use any EShell -internals without autoloading. - -#+begin_src emacs-lisp -(use-package eshell-prompt - :load-path "elisp/" - :config - (setq eshell-prompt-function #'+eshell-prompt/make-prompt)) + (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 -*** 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. +** 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. -NOTE: I don't defer this package because it must be loaded *before* -EShell is. This is because any ~eshell/*~ functions need to be loaded -before launching it. #+begin_src emacs-lisp -(use-package eshell-additions - :demand t - :load-path "elisp/" - :general - (shell-leader - "t" #'+eshell/open) - (leader - "T" #'+eshell/at-cwd)) +(use-package org + :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 -*** 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. +** 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 eshell-syntax-highlighting - :straight t - :after eshell - :hook (eshell-mode-hook . eshell-syntax-highlighting-mode)) +(use-package org + :defer t + :init + (setq org-edit-src-content-indentation 0 + 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 -** VTerm -There are a few times when EShell doesn't cut it, particularly in the -domain of TUI applications like ~cfdisk~. Emacs comes by default with -some terminal emulators that can run a system wide shell like SH or -ZSH (~shell~ and ~term~ for example), but they're pretty terrible. -~vterm~ is an external package using a shared library for terminal -emulation, and is much better than the default Emacs stuff. +** Org Core Functionality +Hooks, prettify-symbols and records for auto insertion. -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 org + :defer t + :hook + (org-mode-hook . prettify-symbols-mode) + :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" + "* " _)) +#+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. + +#+begin_src emacs-lisp +(use-package org + :defer t + :config + (with-eval-after-load "consult" + (general-def + :keymaps 'org-mode-map + [remap consult-imenu] #'consult-outline)) :general - (shell-leader - "v" #'vterm) - :init - (with-eval-after-load "evil" - (evil-set-initial-state 'vterm-mode 'emacs))) + (file-leader + "l" #'org-store-link + "i" #'org-insert-last-stored-link) + (code-leader + :keymaps 'emacs-lisp-mode-map + "D" #'org-babel-detangle) + (local-leader + :state '(normal motion) + :keymaps 'org-src-mode-map + "o" #'org-edit-src-exit) + (local-leader + :keymaps 'org-mode-map + "l" '(nil :which-key "Links") + "'" '(nil :which-key "Tables") + "c" '(nil :which-key "Clocks") + "r" #'org-refile + "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 + "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)) #+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. +** Org Agenda +Org agenda provides a nice viewing for schedules. With org mode it's +a very tidy way to manage your time. -Ripgrep is a program that attempts to perform better than grep, and it -does. This is because of many optimisations, such as reading -=.gitignore= to exclude certain files from being searched. The -ripgrep package provides utilities to search projects and files. Of -course, this requires installing the rg binary which is available in -most distribution nowadays. -*** Grep #+begin_src emacs-lisp -(use-package grep +(use-package org-agenda :defer t - :display - ("^\\*grep.*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.35) - (reusable-frames . t)) + :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) + :config + (evil-set-initial-state 'org-agenda-mode 'normal) :general - (search-leader - "g" #'grep-this-file - "c" #'grep-config-file - "d" #'rgrep) + (file-leader + "a" `(,(proc (interactive) + (find-file (completing-read "Enter directory: " org-agenda-files nil t))) + :which-key "Open agenda directory")) + (app-leader + "a" #'org-agenda) (nmmap - :keymaps 'grep-mode-map - "0" #'evil-beginning-of-line - "q" #'quit-window - "i" #'wgrep-change-to-wgrep-mode - "c" #'recompile) + :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 +** 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 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 '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) + :keymaps 'org-capture-mode-map + "ZZ" #'org-capture-finalize + "ZR" #'org-capture-refile + "ZQ" #'org-capture-kill)) +#+end_src +** WIP Org clock-in +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +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. - (defun grep-file (query filename) - (grep (format "grep --color=auto -nIiHZEe \"%s\" -- %s" - query filename))) +#+begin_src emacs-lisp +(use-package org-clock + :after org + :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)) +#+end_src +** 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 org-ref + :straight t + :defer t + :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 + :config + (require 'org-ref-ivy)) +#+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). - (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))))) +#+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) - (defun grep-config-file () - (interactive) - (let ((query (read-string "Search for: " "^[*]+ .*"))) - (grep-file query (concat user-emacs-directory "config.org"))))) + (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 -*** rg +** Org for evil +Evil org for some nice bindings. + #+begin_src emacs-lisp -(use-package rg +(use-package evil-org :straight t :defer t - :commands (+rg/project-todo) - :display - ("^\\*\\*ripgrep\\*\\*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.35)) + :hook (org-mode-hook . evil-org-mode) :general - (search-leader - "r" #'rg) - (:keymaps 'project-prefix-map - "t" `(+rg/project-todo :which-key "Project TODOs")) (nmmap - :keymaps 'rg-mode-map - "c" #'rg-recompile - "C" #'rg-rerun-toggle-case - "]]" #'rg-next-file - "[[" #'rg-prev-file - "q" #'quit-window - "i" #'wgrep-change-to-wgrep-mode) - :init - (setq rg-group-result t - rg-hide-command t - rg-show-columns nil - rg-show-header t - rg-custom-type-aliases nil - rg-default-alias-fallback "all" - rg-buffer-name "*ripgrep*") - :config - (defun +rg/project-todo () - (interactive) - (rg "TODO" "*" - (if (project-current) - (project-root (project-current)) - default-directory))) - (evil-set-initial-state 'rg-mode 'normal)) + :keymaps 'org-mode-map + "TAB" #'org-cycle)) #+end_src -** WAIT Elfeed +** WAIT Org reveal :PROPERTIES: :header-args:emacs-lisp: :tangle no :results none :END: -Elfeed is the perfect RSS feed reader, integrated into Emacs -perfectly. I've got a set of feeds that I use for a large variety of -stuff, mostly media and entertainment. I've also bound " ar" -to elfeed for loading the system. +Org reveal allows one to export org files as HTML presentations via +reveal.js. Pretty nifty and it's easy to use. #+begin_src emacs-lisp -(use-package elfeed +(use-package ox-reveal :straight t + :defer t + :init + (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js" + org-reveal-theme "sky")) +#+end_src +** Org bookmark +I maintain a bookmarks file at =~/Text/bookmarks.org=. I would like +the ability to construct new bookmarks and open bookmarks. They may +be either articles I want to read, useful information documents or +just straight up youtube videos. So I wrote a +[[file:elisp/org-bookmark.el][library]] myself which does the +appropriate dispatching and work for me. Pretty sweet! + +Also I define a template for org-capture here for bookmarks and add it +to the list ~org-capture-templates~. + +#+begin_src emacs-lisp +(use-package org-bookmark + :defer t + :load-path "elisp/" :general - (app-leader "r" #'elfeed) - (nmmap - :keymaps 'elfeed-search-mode-map - "gr" #'elfeed-update - "s" #'elfeed-search-live-filter - "" #'elfeed-search-show-entry) + (file-leader + "b" #'org-bookmark/open-bookmark) :init - (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) + (with-eval-after-load "org-capture" + (add-to-list + 'org-capture-templates + '("b" "Bookmark" entry + (file "bookmarks.org") + "* %? :bookmark: +%T +%^{url|%x}p +" + )))) +#+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++. - (setq +rss/feed-urls - '(("Arch Linux" - "https://www.archlinux.org/feeds/news/" - News Technology) - ("The Onion" - "https://www.theonion.com/rss" - Social) - ("Protesilaos Stavrou" - "https://www.youtube.com/@protesilaos" - YouTube Technology) - ("Tsoding Daily" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" - YouTube Technology) - ("Tsoding" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" - YouTube Technology) - ("Nexpo" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw" - YouTube Stories) - ("3B1B" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw" - YouTube) - ("Fredrik Knusden" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w" - YouTube Stories) - ("Barely Sociable" - "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw" - YouTube Stories) - ("Atrocity Guide" - "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw" - YouTube Stories) - ("Hacker News" - "https://news.ycombinator.com/rss" - Social News Technology) - ("Hacker Factor" - "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2" - Social))) - :config - (with-eval-after-load "evil-collection" - (evil-collection-elfeed-setup)) +#+begin_src emacs-lisp +(use-package make-mode + :defer t + :auto-insert + (("[mM]akefile\\'" . "Makefile skeleton") + "" + "CC=gcc +OUT=main.out +LIBS= +ARGS= - (setq elfeed-feeds (cl-map 'list #'(lambda (item) - (append (list (nth 1 item)) (cdr (cdr item)))) - +rss/feed-urls)) +RELEASE=0 +GFLAGS=-Wall -Wextra -Werror -Wswitch-enum -std=c11 +DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined +RFLAGS=-O3 +DEPFLAGS=-MT $@ -MMD -MP -MF +ifeq ($(RELEASE), 1) +CFLAGS=$(GFLAGS) $(RFLAGS) +else +CFLAGS=$(GFLAGS) $(DFLAGS) +endif - (advice-add 'elfeed-search-show-entry :after #'+elfeed/dispatch-entry) +SRC=src +DIST=build +CODE=$(addprefix $(SRC)/, ) # add source files here +OBJECTS=$(CODE:$(SRC)/%.c=$(DIST)/%.o) +DEPDIR:=$(DIST)/dependencies +DEPS:=$(CODE:$(SRC)/%.c=$(DEPDIR):%.d) $(DEPDIR)/main.d - (defun +elfeed/dispatch-entry (entry) - "Process each type of entry differently. - e.g., you may want to open HN entries in eww." - (let ((url (elfeed-entry-link entry))) - (pcase url - ((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch")) - (mpv-play-url url)) - (_ (eww url)))))) +.PHONY: all +all: $(OUT) + +$(OUT): $(DIST)/$(OUT) + +$(DIST)/$(OUT): $(OBJECTS) $(DIST)/main.o | $(DIST) + $(CC) $(CFLAGS) $^ -o $@ $(LIBS) + +$(DIST)/%.o: $(SRC)/%.c | $(DIST) $(DEPDIR) + $(CC) $(CFLAGS) $(DEPFLAGS) $(DEPDIR)/$*.d -c $< -o $@ $(LIBS) + +.PHONY: run +run: $(DIST)/$(OUT) + ./$^ $(ARGS) + +.PHONY: +clean: + rm -rfv $(DIST)/* + +$(DIST): + mkdir -p $(DIST) + +$(DEPDIR): + mkdir -p $(DEPDIR) + +-include $(DEPS) +" + _)) +#+end_src +** WAIT SQL +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +The default SQL package provides support for connecting to common +database types (sqlite, mysql, etc) for auto completion and query +execution. I don't use SQL currently but whenever I need it it's +there. + +#+begin_src emacs-lisp +(use-package sql + :defer t + :init + (setq sql-display-sqli-buffer-function nil)) #+end_src -** IBuffer -IBuffer is the dired of buffers. Nothing much else to be said. +** WAIT Ada +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +Check out [[file:elisp/ada-mode.el][ada-mode]], my custom ~ada-mode~ +that replaces the default one. This mode just colourises stuff, and +uses eglot and a language server to do the hard work. #+begin_src emacs-lisp -(use-package ibuffer +(use-package ada-mode + :load-path "elisp/" :defer t - :general - (buffer-leader - "i" #'ibuffer) :config - (with-eval-after-load "evil-collection" - (evil-collection-ibuffer-setup))) + (with-eval-after-load "eglot" + (add-hook 'ada-mode-hook #'eglot))) #+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) +** NHexl +Hexl-mode is the inbuilt package within Emacs to edit hex and binary +format buffers. There are a few problems with hexl-mode though, +including an annoying prompt on /revert-buffer/. -Core Proced config, just a few bindings and evil collection setup. +Thus, nhexl-mode! It comes with a few other improvements. Check out +the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself. #+begin_src emacs-lisp -(use-package proced +(use-package nhexl-mode + :straight t + :defer t + :mode ("\\.bin" "\\.out")) +#+end_src +** NASM +#+begin_src emacs-lisp +(use-package nasm-mode + :straight t + :defer t + :mode ("\\.asm" . nasm-mode)) +#+end_src +** C/C++ +Setup for C and C++ modes, using Emacs' default package: cc-mode. +*** cc-mode +Tons of stuff, namely: ++ ~auto-fill-mode~ for 80 char limit ++ Some keybindings to make evil statement movement easy ++ Lots of pretty symbols ++ Indenting options and a nice (for me) code style for C ++ Auto inserts to get a C file going + +#+begin_src emacs-lisp +(use-package cc-mode :defer t + :hook + (c-mode-hook . auto-fill-mode) + (c++-mode-hook . auto-fill-mode) :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)) + (:keymaps '(c-mode-map + c++-mode-map) + :states '(normal motion visual) + "(" #'c-beginning-of-statement + ")" #'c-end-of-statement + "{" #'c-beginning-of-defun + "}" #'c-end-of-defun) :init - (setq proced-auto-update-interval 5) + (setq-default c-basic-offset 2) + (setq-default c-auto-newline nil) + (setq-default c-default-style '((other . "user"))) + (defun +cc/copyright-notice () + (let* ((lines (split-string (+license/copyright-notice) "\n")) + (copyright-line (car lines)) + (rest (cdr lines))) + (concat + "* " + copyright-line + "\n" + (mapconcat + #'(lambda (x) + (if (string= x "") + "" + (concat " * " x))) + rest + "\n")))) + :auto-insert + (("\\.c\\'" . "C skeleton") + "" + "/" (+cc/copyright-notice) "\n\n" + " * Created: " (format-time-string "%Y-%m-%d") "\n" + " * Author: " user-full-name "\n" + " * Description: " _ "\n" + " */\n" + "\n") + (("\\.cpp\\'" "C++ skeleton") + "" + "/" (+cc/copyright-notice) "\n\n" + " * Created: " (format-time-string "%Y-%m-%d") "\n" + " * Author: " user-full-name "\n" + " * Description: " _ "\n" + " */\n" + "\n") + (("\\.\\([Hh]\\|hh\\|hpp\\|hxx\\|h\\+\\+\\)\\'" . "C / C++ header") + (replace-regexp-in-string "[^A-Z0-9]" "_" + (string-replace "+" "P" + (upcase + (file-name-nondirectory buffer-file-name)))) + "/" (+cc/copyright-notice) "\n\n" + " * Created: " (format-time-string "%Y-%m-%d") "\n" + " * Author: " user-full-name "\n" + " * Description: " _ "\n" + " */\n\n" + "#ifndef " str n "#define " str "\n\n" "\n\n#endif") :config - (with-eval-after-load "evil-collection" - (evil-collection-proced-setup))) + (c-add-style + "user" + '((c-basic-offset . 2) + (c-comment-only-line-offset . 0) + (c-hanging-braces-alist (brace-list-open) + (brace-entry-open) + (substatement-open after) + (block-close . c-snug-do-while) + (arglist-cont-nonempty)) + (c-cleanup-list brace-else-brace) + (c-offsets-alist + (statement-block-intro . +) + (substatement-open . 0) + (access-label . -) + (inline-open . 0) + (label . 0) + (statement-cont . +))))) #+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 +*** Clang format +clang-format is a program that formats C/C++ files. It's highly +configurable and quite fast. I have a root configuration in my +Dotfiles (check it out +[[file:~/Dotfiles/ClangFormat/).clang-format][here]]. -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. +Clang format comes inbuilt with clang/LLVM, so it's quite likely to be +on your machine. #+begin_src emacs-lisp -(use-package calc +(use-package clang-format + :load-path "/usr/share/clang/" :defer t - :display - ("*Calculator*" - (display-buffer-at-bottom) - (window-height . 0.2)) + :after cc-mode + :commands (+code/clang-format-region-or-buffer + clang-format-mode) :general - (app-leader - "c" #'calc-dispatch) - :init - (setq calc-algebraic-mode t) + (code-leader + :keymaps '(c-mode-map c++-mode-map) + "f" #'clang-format-buffer) :config - (with-eval-after-load "evil-collection" - (evil-collection-calc-setup))) + (define-minor-mode clang-format-mode + "On save formats the current buffer via clang-format." + :lighter nil + (let ((save-func (proc (interactive) + (clang-format-buffer)))) + (if clang-format-mode + (add-hook 'before-save-hook save-func nil t) + (remove-hook 'before-save-hook save-func t)))) + (defun +code/clang-format-region-or-buffer () + (interactive) + (if (mark) + (clang-format-region (region-beginning) (region-end)) + (clang-format-buffer)))) #+end_src -** Zone -Emacs' out of the box screensaver software. +*** cc org babel +To ensure org-babel executes language blocks of C/C++, I need to load +it as an option in ~org-babel-load-languages~. #+begin_src emacs-lisp -(use-package zone - :defer t - :commands (zone) - :general - (leader - "z" #'zone) +(use-package org + :after cc-mode :init - - (setq zone-programs - [zone-pgm-drip - zone-pgm-drip-fretfully])) + (org-babel-do-load-languages + 'org-babel-load-languages + '((C . t)))) #+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! +*** cc compile fsan +Sanitisers are a blessing for C/C++. An additional runtime on top of +the executable which catches stuff like undefined behaviour or memory +leaks make it super easy to see where and how code is failing. +However, by default, Emacs' compilation-mode doesn't understand the +logs =fsanitize= makes so you usually have to manually deal with it +yourself. -2024-10-08: Man pages are rendered via a separate process, which is -why this is necessary. +Compilation mode uses regular expressions to figure out whether +something is an error and how to navigate to the file where that error +is located. So adding support for =-fsanitize= is as simple as making +a regular expression which captures file names and digits #+begin_src emacs-lisp -(use-package man - :defer t - :init - (setq Man-notify-method 'thrifty) - :display - ("\\*Man.*" - (display-buffer-reuse-mode-window display-buffer-same-window) - (mode . Man-mode)) - :general - (file-leader - "m" #'man) ;; kinda like "find man page" - (nmmap - :keymaps 'Man-mode-map - "RET" #'man-follow)) -#+end_src -** Info -Info is GNU's attempt at better man pages. Most Emacs packages have -info pages so I'd like nice navigation options. +(use-package compile + :after cc-mode + :config + (add-to-list 'compilation-error-regexp-alist-alist + `(fsan ,(rx (and + line-start " #" digit " 0x" (1+ hex) " in " + (1+ (or word "_")) " " + (group (seq (* any) (or ".c" ".cpp" ".h" ".hpp"))) ":" + (group (+ digit)))) + 1 2)) + (add-to-list 'compilation-error-regexp-alist + 'fsan)) +#+end_src +** Markdown +Why use Markdown when you have org-mode? Because LSP servers +sometimes format their documentation as markdown, which +[[*Eglot][Eglot]] can use to provide nicer views on docs! #+begin_src emacs-lisp -(use-package info +(use-package markdown-mode :defer t - :general - (nmmap - :keymaps 'Info-mode-map - "h" #'evil-backward-char - "k" #'evil-previous-line - "l" #'evil-forward-char - "H" #'Info-history-back - "L" #'Info-history-forward - "C-j" #'Info-forward-node - "C-k" #'Info-backward-node - "RET" #'Info-follow-nearest-node - "m" #'Info-menu - "C-o" #'Info-history-back - "s" #'Info-search - "S" #'Info-search-case-sensitively - "i" #'Info-index - "a" #'info-apropos - "gj" #'Info-next - "gk" #'Info-prev - "g?" #'Info-summary - "q" #'quit-window) - :init - (with-eval-after-load "evil" - (evil-set-initial-state 'Info-mode 'normal))) + :straight t) #+end_src -** WAIT gif-screencast +** WAIT D :PROPERTIES: :header-args:emacs-lisp: :tangle no :results none :END: -Little application that uses =gifsicle= to make essentially videos of -Emacs. Useful for demonstrating features. +D is a systems level programming language with C-style syntax. I +think it has some interesting ideas such as a toggleable garbage +collector. Here I just install the D-mode package, enable ~org-babel~ +execution of d-mode blocks and alias ~D-mode~ with ~d-mode~. #+begin_src emacs-lisp -(use-package gif-screencast +(use-package d-mode + :defer t :straight t - :general - (app-leader - "x" #'gif-screencast-start-or-stop) - :init - (setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/"))) + :config + (fset 'D-mode 'd-mode) + (with-eval-after-load "org-mode" + (setf (alist-get 'd org-babel-load-languages) t))) #+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 Rust +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +Rust is the systems programming language that also does web stuff and +CLI programs and basically tries to be a jack of all trades. It's got +some interesting stuff but most importantly it's very new, so everyone +must learn it, right? #+begin_src emacs-lisp -(use-package image-mode +(use-package rust-mode + :straight t :defer t :general - (nmmap - :keymaps 'image-mode-map - "+" #'image-increase-size - "-" #'image-decrease-size - "a" #'image-toggle-animation - "p+" #'image-increase-speed - "p-" #'image-increase-speed - "h" #'image-backward-hscroll - "j" #'image-next-line - "k" #'image-previous-line - "l" #'image-forward-hscroll)) + (code-leader + :keymaps 'rust-mode-map + "f" #'rust-format-buffer) + (local-leader + :keymaps 'rust-mode-map + "c" #'rust-run-clippy) + :init + (setq rust-format-on-save t) + (with-eval-after-load "eglot" + (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer")))) #+end_src -** empv -Emacs MPV bindings, with very cool controls for queuing files for -playing. +** WAIT Racket +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +A scheme with lots of stuff inside it. Using it for a language design +book so it's useful to have some Emacs binds for it. + #+begin_src emacs-lisp -(use-package empv +(use-package racket-mode :straight t :defer t + :hook (racket-mode-hook . racket-xp-mode) + :display + ("\\*Racket REPL*" + (display-buffer-at-bottom) + (window-height . 0.3)) :init - (setq empv-audio-dir (expand-file-name "~/Media/audio") - empv-video-dir (expand-file-name "~/Media/videos") - empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" - "flac" "aac" "opus") - empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" - "webm")) - :hydra - (empv-hydra - nil "Hydra for EMPV" - ("(" #'empv-chapter-prev - "chapter-prev" :column "playback") - (")" #'empv-chapter-next - "chapter-next" :column "playback") - ("0" #'empv-volume-up - "volume-up" :column "playback") - ("9" #'empv-volume-down - "volume-down" :column "playback") - ("[" #'empv-playback-speed-down - "playback-speed-down" :column "playback") - ("]" #'empv-playback-speed-up - "playback-speed-up" :column "playback") - ("_" #'empv-toggle-video - "toggle-video" :column "playback") - ("q" #'empv-exit - "exit" :column "playback") - ("s" #'empv-seek - "seek" :column "playback") - ("t" #'empv-toggle - "toggle" :column "playback") - ("x" #'empv-chapter-select - "chapter-select" :column "playback") - ("N" #'empv-playlist-prev - "playlist-prev" :column "playlist") - ("C" #'empv-playlist-clear - "playlist-clear" :column "playlist") - ("n" #'empv-playlist-next - "playlist-next" :column "playlist") - ("p" #'empv-playlist-select - "playlist-select" :column "playlist") - ("S" #'empv-playlist-shuffle - "playlist-shuffle" :column "playlist") - ("a" #'empv-play-audio - "play-audio" :column "play") - ("R" #'empv-play-random-channel - "play-random-channel" :column "play") - ("d" #'empv-play-directory - "play-directory" :column "play") - ("f" #'empv-play-file - "play-file" :column "play") - ("o" #'empv-play-or-enqueue - "play-or-enqueue" :column "play") - ("r" #'empv-play-radio - "play-radio" :column "play") - ("v" #'empv-play-video - "play-video" :column "play") - ("i" #'empv-display-current - "display-current" :column "misc") - ("l" #'empv-log-current-radio-song-name - "log-current-radio-song-name" :column "misc") - ("c" #'empv-copy-path - "copy-path" :column "misc") - ("Y" #'empv-youtube-last-results - "youtube-last-results" :column "misc") - ("y" #'empv-youtube - "youtube" :column "misc")) + (setq racket-documentation-search-location 'local) :general - (app-leader - "e" #'empv-hydra/body)) + (nmap + :keymaps 'racket-describe-mode-map + "q" #'quit-window) + (nmap + :keymaps 'racket-mode-map + "gr" #'racket-eval-last-sexp) + (local-leader + :keymaps '(racket-mode-map racket-repl-mode-map) + "d" #'racket-repl-describe) + (local-leader + :keymaps 'racket-mode-map + "r" #'racket-run + "i" #'racket-repl + "e" #'racket-send-definition + "sr" #'racket-send-region + "sd" #'racket-send-definition)) #+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. +** WAIT CSharp +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +Haven't used C# in a while, but Emacs is alright for it with +omnisharp. + #+begin_src emacs-lisp -(use-package gud - :general - :after hydra - :hydra - (gud-hydra - (:hint nil) "Hydra for GUD" - ("<" #'gud-up "Up" - :column "Control Flow") - (">" #'gud-down "Down" - :column "Control Flow") - ("b" #'gud-break "Break" - :column "Breakpoints") - ("d" #'gud-remove "Remove" - :column "Breakpoints") - ("f" #'gud-finish "Finish" - :column "Control Flow") - ("J" #'gud-jump "Jump" - :column "Control Flow") - ("L" #'gud-refresh "Refresh" - :column "Misc") - ("n" #'gud-next "Next" - :column "Control Flow") - ("p" #'gud-print "Print" - :column "Misc") - ("c" #'gud-cont "Cont" - :column "Control Flow") - ("s" #'gud-step "Step" - :column "Control Flow") - ("t" #'gud-tbreak "Tbreak" - :column "Control Flow") - ("u" #'gud-until "Until" - :column "Control Flow") - ("v" #'gud-go "Go" - :column "Control Flow") - ("w" #'gud-watch "Watch" - :column "Breakpoints") - ("TAB" #'gud-stepi "Stepi" - :column "Control Flow")) - :general - (code-leader "d" #'gud-hydra/body - "D" #'gud-gdb)) +(use-package csharp-mode + :defer t) +#+end_src +** WAIT Java +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +I kinda dislike Java, but if necessary I will code in it. Just setup +a style and some pretty symbols. You can use LSP to get cooler +features to be fair. + +#+begin_src emacs-lisp +(use-package ob-java + :defer t + :config + (with-eval-after-load "cc-mode" + (c-add-style + "java" + '((c-basic-offset . 4) + (c-comment-only-line-offset 0 . 0) + (c-offsets-alist + (inline-open . 0) + (topmost-intro-cont . +) + (statement-block-intro . +) + (knr-argdecl-intro . 5) + (substatement-open . 0) + (substatement-label . +) + (label . +) + (statement-case-open . +) + (statement-cont . +) + (arglist-intro . c-lineup-arglist-intro-after-paren) + (arglist-close . c-lineup-arglist) + (brace-list-intro first c-lineup-2nd-brace-entry-in-arglist c-lineup-class-decl-init-+ +) + (access-label . 0) + (inher-cont . c-lineup-java-inher) + (func-decl-cont . c-lineup-java-throws)))) + (add-to-list 'c-default-style '(java-mode . "java"))) + + (with-eval-after-load "abbrev" + (define-abbrev-table 'java-mode-abbrev-table nil) + (add-hook 'java-mode-hook + (proc (setq-local local-abbrev-table java-mode-abbrev-table))))) #+end_src -** WAIT esup +** WAIT Haskell :PROPERTIES: :header-args:emacs-lisp: :tangle no :results none :END: -I used to be able to just use -[[file:elisp/profiler-dotemacs.el][profile-dotemacs.el]], when my -Emacs config was smaller, but now it tells me very little information -about where my setup is inefficient due to the literate config. Just -found this ~esup~ thing and it works perfectly, exactly how I would -prefer getting this kind of information. It runs an external Emacs -instance and collects information from it, so it doesn't require -restarting Emacs to profile, and I can compile my configuration in my -current instance to test it immediately. +Haskell is a static lazy functional programming language (what a +mouthful). It's quite a beautiful language and really learning it will +change the way you think about programming. However, my preferred +functional language is still unfortunately Lisp so no extra brownie +points there. -2023-10-16: Unless I'm doing some optimisations or tests, I don't -really need this in my config at all times. Enable when needed. +Here I configure the REPL for Haskell via the +~haskell-interactive-mode~. I also load my custom package +[[file:elisp/haskell-multiedit.el][haskell-multiedit]] which allows a +user to create temporary ~haskell-mode~ buffers that, upon completion, +will run in the REPL. Even easier than making your own buffer. #+begin_src emacs-lisp -(use-package esup +(use-package haskell-mode :straight t :defer t + :hook + (haskell-mode-hook . haskell-indentation-mode) + (haskell-mode-hook . interactive-haskell-mode) + :display + ("\\*haskell.**\\*" + (display-buffer-at-bottom) + (window-height . 0.3)) :general - (leader - "qe" #'esup)) + (shell-leader + "h" #'haskell-interactive-bring) + (local-leader + :keymaps 'haskell-mode-map + "c" #'haskell-compile + "t" #'haskell-process-do-type) + (nmmap + :keymaps 'haskell-mode-map + "C-c C-c" #'haskell-process-load-file) + (local-leader + :keymaps 'haskell-interactive-mode-map + "c" #'haskell-interactive-mode-clear) + (imap + :keymaps 'haskell-interactive-mode-map + "M-k" #'haskell-interactive-mode-history-previous + "M-j" #'haskell-interactive-mode-history-next) + :init + (setq haskell-interactive-prompt "[λ] " + haskell-interactive-prompt-cont "{λ} " + haskell-interactive-popup-errors nil + haskell-stylish-on-save t + haskell-process-type 'auto) + :config + (load (concat user-emacs-directory "elisp/haskell-multiedit.el"))) #+end_src -* Text modes -Standard packages and configurations for text-mode and its derived -modes. -** 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. +** Python +Works well for python. If you have ~pyls~ it should be on your path, so +just run eglot if you need. But an LSP server is not necessary for a +lot of my time in python. Here I also setup org-babel for python +source code blocks. #+begin_src emacs-lisp -(use-package flyspell +(use-package python :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)) + :keymaps 'python-mode-map + "C-M-x" #'python-shell-send-defun) + (local-leader + :keymaps 'python-mode-map + "c" #'python-check) + (local-leader + :keymaps 'python-mode-map + :infix "e" + "e" #'python-shell-send-statement + "r" #'python-shell-send-region + "f" #'python-shell-send-buffer) + :pretty + (python-mode-hook + ("None" . "Ø") + ("list" . "ℓ") + ("List" . "ℓ") + ("str" . "𝕊") + ("!" . "¬") + ("for" . "∀") + ("print" . "φ") + ("lambda" . "λ") + ("reduce" . "↓") + ("map" . "→") + ("return" . "≡") + ("yield" . "≈")) + :init + (setq python-indent-offset 4) + :config + (with-eval-after-load "org-mode" + (setf (alist-get 'python org-babel-load-languages) t))) #+end_src -** Undo tree -Undo tree sits on top of Emacs' undo capabilities. It provides a nice -visual for the history of a buffer and is a great way to produce -branches of edits. This history may be saved to and loaded from the -disk, which makes Emacs a quasi version control system in and of -itself. The only feature left is describing changes... +*** Python shell +Setup for python shell, including a toggle option #+begin_src emacs-lisp -(use-package undo-tree - :demand t - :straight t +(use-package python + :defer t + :commands +python/toggle-repl :general - (leader - "u" #'undo-tree-visualize) - :init - (setq undo-tree-auto-save-history t - undo-tree-history-directory-alist backup-directory-alist) - :config - (global-undo-tree-mode)) + (shell-leader + "p" #'run-python) + :hook + (inferior-python-mode-hook . company-mode) + :display + ("\\*Python\\*" + (display-buffer-at-bottom) + (window-height . 0.3))) #+end_src -** Whitespace -Deleting whitespace, highlighting when going beyond the 80th character -limit, all good stuff. I don't want to highlight whitespace for -general mode categories (Lisp shouldn't really have an 80 character -limit), so set it for specific modes need the help. +** YAML +YAML is a data language which is useful for config files. #+begin_src emacs-lisp -(use-package whitespace +(use-package yaml-mode + :defer t + :straight t) +#+end_src +** HTML/CSS/JS +Firstly, web mode for consistent colouring of syntax. + +#+begin_src emacs-lisp +(use-package web-mode + :straight t + :defer t + :mode ("\\.html" . web-mode) + :mode ("\\.css" . web-mode) + :custom + ((web-mode-code-indent-offset 2) + (web-mode-markup-indent-offset 2) + (web-mode-css-indent-offset 2))) +#+end_src +*** Emmet +Emmet for super speed code writing. + +#+begin_src emacs-lisp +(use-package emmet-mode + :straight t :defer t + :hook (web-mode-hook . emmet-mode) :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) - . whitespace-mode) - :init - (setq whitespace-line-column nil - whitespace-style '(face empty spaces tabs newline trailing lines-char - tab-mark))) + (imap + :keymaps 'emmet-mode-keymap + "TAB" #'emmet-expand-line + "M-j" #'emmet-next-edit-point + "M-k" #'emmet-prev-edit-point)) #+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. +*** HTML Auto insert +An auto-insert for HTML buffers, which just adds some nice stuff. #+begin_src emacs-lisp -(use-package emacs - :init - (setq-default fill-column 80) - (add-hook 'text-mode-hook (proc (setq-local fill-column 70))) - :hook - (text-mode-hook . auto-fill-mode) - ((c-mode-hook c++-mode-hook haskell-mode-hook python-mode-hook - org-mode-hook text-mode-hook js-mode-hook) - . display-fill-column-indicator-mode)) +(use-package web-mode + :defer t + :auto-insert + (("\\.html\\'" . "HTML Skeleton") + "" + " + + + + "(read-string "Enter title: ") | """ + + + + + + + + +" + _ + " +")) #+end_src -** Show-paren-mode -Show parenthesis for Emacs +*** Javascript Mode +A better mode for JavaScript that also has automatic integration with +eglot. #+begin_src emacs-lisp -(use-package paren - :hook (prog-mode-hook . show-paren-mode)) +(use-package js + :defer t + :mode ("\\.js" . js-mode) + :hook (js-mode-hook . auto-fill-mode) + :init + (setq js-indent-level 2)) #+end_src -** Smartparens -Smartparens is a smarter electric-parens, it's much more aware of -context and easier to use. +*** Typescript +A language that adds a build step to JavaScript projects for "static" +typing. It's nice because it adds good auto completion. #+begin_src emacs-lisp -(use-package smartparens +(use-package typescript-mode :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)) + :init + (setq typescript-indent-level 2)) #+end_src -** Powerthesaurus -Modern package for thesaurus in Emacs with a transient + hydra. +** Scheme +Another Lisp but simpler than the rest. A beauty of engineering and +fun to write programs in. Here I setup ~geiser~, which is the +premiere way to interact with scheme REPLs. + #+begin_src emacs-lisp -(use-package powerthesaurus +(use-package geiser :defer t :straight t + :display + ("\\*Geiser.*" + (display-buffer-reuse-mode-window display-buffer-at-bottom) + (window-height . 0.3)) :general - (search-leader - "w" #'powerthesaurus-transient)) + (shell-leader + "S" #'geiser) + (local-leader + :keymaps 'scheme-mode-map + "t" #'geiser + "m" #'geiser-doc-look-up-manual + "d" #'geiser-doc-symbol-at-point) + (local-leader + :keymaps 'scheme-mode-map + :infix "e" + "e" #'geiser-eval-last-sexp + "b" #'geiser-eval-buffer + "d" #'geiser-eval-definition + "r" #'geiser-eval-region) + :init + (with-eval-after-load "evil" + (evil-set-initial-state 'geiser-debug-mode-map 'emacs))) + +(use-package geiser-guile + :defer t + :straight t) +#+end_src +** WAIT Ocaml +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +*** Ocaml Setup +Firstly, install ~opam~ and ~ocaml~. Then run the following script: +#+begin_src sh +opam install tuareg ocamlformat odoc utop merlin user-setup; +opam user-setup install; +mv ~/.emacs.d/opam-user-setup.el ~/.config/emacs/elisp; +rm -rf ~/.emacs.d ~/.emacs; #+end_src -** lorem ipsum -Sometimes you need placeholder text for some UI or document. Pretty -easy to guess what text I'd use. +This sets up the necessary packages (particularly Emacs Lisp) and some +configuration that ensures Emacs is consistent with the user +installation. Notice the moving of =opam-user-setup.el= into +=~/.config/emacs/elisp=, which we'll use to setup the ocaml +experience. +*** Ocaml Configuration +Here I load the =opam-user-setup= package setup earlier, with some +neat tips from the default =~/.emacs= generated by ~opam user-setup +install~. #+begin_src emacs-lisp -(use-package lorem-ipsum - :straight t +(use-package opam-user-setup + :defer t + :load-path "elisp/" + :mode ("\\.ml" . tuareg-mode) + :hook (tuareg-mode-hook . whitespace-mode) + :display + ("\\*utop\\*" + (display-buffer-at-bottom) + (window-height . 0.3)) :general - (insert-leader - "p" #'lorem-ipsum-insert-paragraphs)) -#+end_src -* Programming packages -Packages that help with programming in general, providing IDE like -capabilities. -** 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 + (code-leader + :keymaps 'tuareg-mode-map + "f" #'+ocaml/format-buffer) + :config + (defun +ocaml/format-buffer () + (interactive) + (when (eq major-mode 'tuareg-mode) + (let ((name (buffer-file-name (current-buffer))) + (format-str "ocamlformat -i --enable-outside-detected-project %s")) + (save-buffer) + (set-process-sentinel (start-process-shell-command "ocamlformat" "*ocamlformat*" + (format format-str name)) + (lambda (p event) + (when (string= event "finished\n") + (revert-buffer nil t) + (message "[ocamlformat] Finished."))))))) + (add-to-list 'compilation-error-regexp-alist-alist + `(ocaml + "[Ff]ile \\(\"\\(.*?\\)\", line \\(-?[0-9]+\\)\\(, characters \\(-?[0-9]+\\)-\\([0-9]+\\)\\)?\\)\\(:\n\\(\\(Warning .*?\\)\\|\\(Error\\)\\):\\)?" + 2 3 (5 . 6) (9 . 11) 1 (8 compilation-message-face))) + (add-to-list 'compilation-error-regexp-alist + 'ocaml) + :general + (local-leader + :keymaps 'tuareg-mode-map + "u" #'utop) + (local-leader + :keymaps 'tuareg-mode-map + :infix "e" + "r" #'utop-eval-region + "e" #'utop-eval-phrase + "b" #'utop-eval-buffer)) -Eldoc box makes the help buffer a hovering box instead of printing it -in the minibuffer. A lot cleaner. +(use-package merlin-eldoc + :straight t + :after opam-user-setup + :hook + (tuareg-mode-hook . merlin-eldoc-setup) + :init + (setq merlin-eldoc-occurrences nil)) +#+end_src +** Common Lisp +Common Lisp is a dialect of Lisp, the most /common/ one around. Emacs +comes with builtin Lisp support, of course, and it's really good in +comparison to literally everything else. However, I wish it had a +better REPL... +*** WAIT Sly +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +Enter /SLY/. Sly is a fork of /SLIME/ and is *mandatory* for lisp +development on Emacs. -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. +Here I just setup Sly to use ~sbcl~. #+begin_src emacs-lisp -(use-package eldoc +(use-package sly :defer t - :hook (prog-mode-hook . eldoc-mode) + :straight t :init - (global-eldoc-mode 1) + (setq inferior-lisp-program "sbcl" + sly-lisp-loop-body-forms-indentation 0) + :display + ("\\*sly-db" + (display-buffer-at-bottom) + (window-height . 0.5)) + ("\\*sly-" + (display-buffer-at-bottom) + (window-height . 0.3)) + :config + (evil-set-initial-state 'sly-db-mode 'normal) + (with-eval-after-load "org" + (setq-default org-babel-lisp-eval-fn #'sly-eval)) + (with-eval-after-load "company" + (add-hook 'sly-mrepl-hook #'company-mode)) :general - (leader - "h>" #'eldoc-doc-buffer)) + (shell-leader + "s" #'sly) + (nmap + :keymaps 'lisp-mode-map + "gr" #'sly-eval-buffer + "gd" #'sly-edit-definition + "gR" #'sly-who-calls) + (local-leader + :keymaps 'lisp-mode-map + "a" '(sly-apropos :which-key "Apropos") + "d" '(sly-describe-symbol :which-key "Describe symbol") + "D" '(sly-documentation-lookup :which-key "Lookup on lispworks") + "l" '(sly-load-file :which-key "Load file") + "c" '(sly-compile-defun :which-key "Compile defun") + "C" '(sly-compile-file :which-key "Compile file") + "S" '(sly-mrepl-sync :which-key "Sync REPL")) + (local-leader + :keymaps 'lisp-mode-map + :infix "e" + "b" #'sly-eval-buffer + "e" #'sly-eval-last-expression + "f" #'sly-eval-defun + "r" #'sly-eval-region) + (nmap + :keymaps 'sly-mrepl-mode-map + "M-j" #'sly-mrepl-next-input-or-button + "M-k" #'sly-mrepl-previous-input-or-button) + (local-leader + :keymaps 'sly-mrepl-mode-map + "s" '(sly-mrepl-shortcut :which-key "Shortcut")) + (nmap + :keymaps 'sly-db-mode-map + "\C-i" 'sly-db-cycle + "g?" 'describe-mode + "S" 'sly-db-show-frame-source + "e" 'sly-db-eval-in-frame + "d" 'sly-db-pprint-eval-in-frame + "D" 'sly-db-disassemble + "i" 'sly-db-inspect-in-frame + "gj" 'sly-db-down + "gk" 'sly-db-up + (kbd "C-j") 'sly-db-down + (kbd "C-k") 'sly-db-up + "]]" 'sly-db-details-down + "[[" 'sly-db-details-up + (kbd "M-j") 'sly-db-details-down + (kbd "M-k") 'sly-db-details-up + "gg" 'sly-db-beginning-of-backtrace + "G" 'sly-db-end-of-backtrace + "t" 'sly-db-toggle-details + "gr" 'sly-db-restart-frame + "I" 'sly-db-invoke-restart-by-name + "R" 'sly-db-return-from-frame + "c" 'sly-db-continue + "s" 'sly-db-step + "n" 'sly-db-next + "o" 'sly-db-out + "b" 'sly-db-break-on-return + "a" 'sly-db-abort + "q" 'sly-db-quit + "A" 'sly-db-break-with-system-debugger + "B" 'sly-db-break-with-default-debugger + "P" 'sly-db-print-condition + "C" 'sly-db-inspect-condition + "g:" 'sly-interactive-eval + "0" 'sly-db-invoke-restart-0 + "1" 'sly-db-invoke-restart-1 + "2" 'sly-db-invoke-restart-2 + "3" 'sly-db-invoke-restart-3 + "4" 'sly-db-invoke-restart-4 + "5" 'sly-db-invoke-restart-5 + "6" 'sly-db-invoke-restart-6 + "7" 'sly-db-invoke-restart-7 + "8" 'sly-db-invoke-restart-8 + "9" 'sly-db-invoke-restart-9) + (nmap + :keymaps 'sly-inspector-mode-map + "q" #'sly-inspector-quit)) #+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. +*** WAIT Sly-ASDF +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +ASDF is the package declaration system that _most_ Common Lisp +programmers use. Here's a package which integrates some stuff into +SLY for ASDF. #+begin_src emacs-lisp -(use-package flycheck +(use-package sly-asdf :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))) + :after sly) #+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. +*** Emacs lisp +Ligatures and bindings for (Emacs) Lisp. Pretty self declarative. #+begin_src emacs-lisp -(use-package eglot +(use-package elisp-mode :defer t + :pretty + (lisp-mode-hook + ("lambda" . "λ") + ("nil" . "Ø") + ("<=" . "≤") + (">=" . "≥") + ("defun" . "ƒ") + ("loop" . "Σ") + ("mapcar" . "→") + ("reduce" . "↓") + ("some" . "∃") + ("every" . "∀")) + (emacs-lisp-mode-hook + ("lambda" . "λ") + ("nil" . "Ø") + ("defun" . "ƒ") + ("mapcar" . "→")) :general - (code-leader - :keymaps 'eglot-mode-map - "f" #'eglot-format - "a" #'eglot-code-actions - "r" #'eglot-rename - "R" #'eglot-reconnect) - :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) + (:states '(normal motion visual) + :keymaps '(emacs-lisp-mode-map + lisp-mode-map + lisp-interaction-mode-map) + ")" #'sp-next-sexp + "(" #'sp-previous-sexp) + (nmmap + :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) + "gr" #'eval-buffer) + (vmap + :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) + "gr" #'eval-region) + (local-leader + :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) + "e" #'eval-last-sexp + "f" #'eval-defun)) #+end_src +*** WIP Hydra like Lispy +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +A [[*Hydra][Hydra]] which uses the ~Lispy~ package (by +abo-abo) to create a set of motions that allow movement around a lisp +file easily. -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. +2024-04-18: Still working on this, quite rough around the edges. #+begin_src emacs-lisp -(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")))) +(use-package lispy + :after (lisp-mode elisp-mode) + :hydra + (hydra-lispy + nil "Move around quickly in Lisp" + ("h" #'lispy-left) + ("j" ("t" #'lispy-teleport) + #'lispy-down) + ("k" #'lispy-up) + ("l" #'lispy-right) + ("d" #'lispy-different) + ("u" #'lispy-flow) + ("o" #'lispy-oneline) + ("m" #'lispy-multiline) + ("N" #'lispy-narrow) + ("W" #'lispy-widen) + ("c" #'lispy-clone) + ("fp" #'lispy-ace-paren) + ("fs" #'lispy-ace-symbol :exit t) + ("H" #'lispy-slurp) + ("L" #'lispy-barf) + ("M-h" #'lispy-move-left) + ("M-j" #'lispy-move-down) + ("M-k" #'lispy-move-up) + ("M-l" #'lispy-move-right) + ("C-g" nil)) + :general + (nmmap + :keymaps '(emacs-lisp-mode-map lisp-mode-map) + "." #'hydra-lispy/body)) #+end_src -** Hide-show mode -Turn on ~hs-minor-mode~ for all prog-mode. This provides folds for -free. +*** Lisp indent function +Add a new lisp indent function which indents newline lists more +appropriately. #+begin_src emacs-lisp -(use-package hideshow +(use-package lisp-mode :defer t - :hook (prog-mode-hook . hs-minor-mode)) + :config + (defun +oreo/lisp-indent-function (indent-point state) + (let ((normal-indent (current-column)) + (orig-point (point))) + (goto-char (1+ (elt state 1))) + (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) + (cond + ;; car of form doesn't seem to be a symbol, or is a keyword + ((and (elt state 2) + (or (not (looking-at "\\sw\\|\\s_")) + (looking-at ":"))) + (if (not (> (save-excursion (forward-line 1) (point)) + calculate-lisp-indent-last-sexp)) + (progn (goto-char calculate-lisp-indent-last-sexp) + (beginning-of-line) + (parse-partial-sexp (point) + calculate-lisp-indent-last-sexp 0 t))) + ;; Indent under the list or under the first sexp on the same + ;; line as calculate-lisp-indent-last-sexp. Note that first + ;; thing on that line has to be complete sexp since we are + ;; inside the innermost containing sexp. + (backward-prefix-chars) + (current-column)) + ((and (save-excursion + (goto-char indent-point) + (skip-syntax-forward " ") + (not (looking-at ":"))) + (save-excursion + (goto-char orig-point) + (looking-at ":"))) + (save-excursion + (goto-char (+ 2 (elt state 1))) + (current-column))) + (t + (let ((function (buffer-substring (point) + (progn (forward-sexp 1) (point)))) + method) + (setq method (or (function-get (intern-soft function) + 'lisp-indent-function) + (get (intern-soft function) 'lisp-indent-hook))) + (cond ((or (eq method 'defun) + (and (null method) + (> (length function) 3) + (string-match "\\`def" function))) + (lisp-indent-defform state indent-point)) + ((integerp method) + (lisp-indent-specform method state + indent-point normal-indent)) + (method + (funcall method indent-point state)))))))) + (setq-default lisp-indent-function #'+oreo/lisp-indent-function)) #+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. +* Applications +Emacs is an operating system, now with a good text editor through +[[*Evil - Vim emulation][Evil]]. Let's configure some apps for it. +** EWW +Emacs Web Wowser is the inbuilt text based web browser for Emacs. It +can render images and basic CSS styles but doesn't have a JavaScript +engine, which makes sense as it's primarily a text interface. #+begin_src emacs-lisp -(use-package aggressive-indent - :straight t - :demand t +(use-package eww + :defer t + :general + (app-leader + "w" #'eww) + (nmmap + :keymaps 'eww-mode-map + "w" #'evil-forward-word-begin + "Y" #'eww-copy-page-url) :config - (add-multiple-to-list aggressive-indent-excluded-modes - 'c-mode 'c++-mode 'cc-mode - 'asm-mode 'js-mode 'typescript-mode - 'tsx-mode 'typescript-ts-mode 'tsx-ts-mode - 'tuareg-mode) - (global-aggressive-indent-mode)) + (with-eval-after-load "evil-collection" + (evil-collection-eww-setup))) #+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. +** Magit +Magit is *the* git porcelain for Emacs, which perfectly encapsulates +the git CLI. It's so good that some people are use Emacs just for it. +It's difficult to describe it well without using it and it integrates +so well with Emacs that there is very little need to use the git CLI +ever. -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. +In this case I just need to setup the bindings for it. Also, define +an auto insert for commit messages so that I don't need to write +everything myself. #+begin_src emacs-lisp -(use-package compile +(use-package transient + :defer t + :straight (:host github :repo "magit/transient" :tag "v0.7.5")) + +(use-package magit + :straight (:host github :repo "magit/magit" :tag "v4.1.0") :defer t :display - ("\\*compilation\\*" - (display-buffer-reuse-window display-buffer-at-bottom) - (window-height . 0.1) - (reusable-frames . t)) - :hydra - (move-error-hydra - (:hint nil) "Hydra for moving between errors" - ("j" #'next-error) - ("k" #'previous-error)) + ("magit:.*" + (display-buffer-same-window) + (inhibit-duplicate-buffer . t)) + ("magit-diff:.*" + (display-buffer-below-selected)) + ("magit-log:.*" + (display-buffer-same-window)) :general (leader - "j" #'move-error-hydra/next-error - "k" #'move-error-hydra/previous-error) + "g" '(magit-dispatch :which-key "Magit")) (code-leader - "c" #'compile - "C" #'recompile) - (:keymaps 'compilation-mode-map - "g" nil) ;; by default this is recompile - (nmmap - :keymaps 'compilation-mode-map - "c" #'recompile) + "b" #'magit-blame) :init - (setq compilation-scroll-output 'first-error - compilation-context-lines nil - next-error-highlight 'fringe-arrow) + (setq vc-follow-symlinks t + magit-blame-echo-style 'lines + magit-copy-revision-abbreviated t) :config - (add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)) + (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 -** 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]]. +** 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 xref +(use-package calendar :defer t + :commands (+calendar/copy-date +calendar/toggle-calendar) :display - ("\\*xref\\*" + ("\\*Calendar\\*" (display-buffer-at-bottom) (inhibit-duplicate-buffer . t) - (window-height . 0.3)) - :general - (code-leader - "t" '(nil :which-key "Tags")) - (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)) -#+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" -+ write a TAGS command, mimicking projectile's one, so I can quickly - generate them. - + Bind that to "pr" - -#+begin_src emacs-lisp -(use-package project - :defer t - :general - (:keymaps 'project-prefix-map - "r" #'+project/generate-tags) - (leader - "p" `(,project-prefix-map :which-key "Project")) - :config - (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 -** 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 devdocs - :straight t - :defer t - :general - (file-leader - "d" #'devdocs-lookup)) -#+end_src -** 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 rainbow-delimiters - :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)) -#+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 -Oh diffs; the way of the ancient ones. Nowadays we use our newfangled -"pull requests" and "cool web interfaces" to manage changes in our -code repositories, but old school projects use patches to make code -changes. They're a pain to distribute and can be very annoying to use -when applying them to code. Even then I somewhat like patches, if -only for their simplicity. - -[[https://git.aryadevchavali.com/dwm][dwm]] uses patches for adding -new features and Emacs has great functionality to work with patches -effectively. Here I configure ~diff-mode~, which provides most of -this cool stuff, to be a bit more ergonomic with ~evil~. - -#+begin_src emacs-lisp -(use-package diff-mode + (window-height . 0.17)) :general (nmmap - :keymaps 'diff-mode-map - "}" #'diff-hunk-next - "{" #'diff-hunk-prev - "RET" #'diff-goto-source)) -#+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 - -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. + :keymaps 'calendar-mode-map + "Y" #'+calendar/copy-date) + (app-leader + "d" #'calendar) + :config + (with-eval-after-load "evil-collection" + (evil-collection-calendar-setup)) + (defun +calendar/copy-date () + "Copy date under cursor into kill ring." + (interactive) + (if (use-region-p) + (call-interactively #'kill-ring-save) + (let ((date (calendar-cursor-to-date))) + (when date + (setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))) + (kill-new (format-time-string "%Y-%m-%d" date))))))) +#+end_src +** Mail +Mail is a funny thing; most people use it just for business or +advertising and it's come out of use in terms of personal +communication in the west for the most part (largely due to "social" +media applications). However, this isn't true for the open source and +free software movement who heavily use mail for communication. -+ 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) +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 org +(use-package notmuch + :straight t :defer t + :commands (notmuch +mail/flag-thread) + :general + (app-leader "m" #'notmuch) + (nmap + :keymaps 'notmuch-search-mode-map + "f" #'+mail/flag-thread) :init - (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)))) + (defconst +mail/local-dir (no-littering-expand-var-file-name "mail/")) + (setq notmuch-show-logo nil + notmuch-search-oldest-first nil + notmuch-hello-sections '(notmuch-hello-insert-saved-searches + notmuch-hello-insert-alltags + notmuch-hello-insert-recent-searches) + notmuch-archive-tags '("-inbox" "-unread" "+archive") + message-auto-save-directory +mail/local-dir + message-directory +mail/local-dir) + (with-eval-after-load "evil-collection" + (evil-collection-notmuch-setup)) + :config + (defun +mail/flag-thread (&optional unflag beg end) + (interactive (cons current-prefix-arg (notmuch-interactive-region))) + (notmuch-search-tag + (notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end) + (when (eq beg end) + (notmuch-search-next-thread)))) #+end_src -** 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. +*** 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 org +(use-package smtpmail :defer t + :commands mail-send :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")))) + (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 -** 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. +*** Mail signature using fortune +Generate a mail signature using the ~fortune~ executable. Pretty +cool! #+begin_src emacs-lisp -(use-package org - :defer t +(use-package fortune :init - (setq org-edit-src-content-indentation 0 - 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 '{})) + (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 -** Org Core Functionality -Hooks, prettify-symbols and records for auto insertion. +** Dired +Dired: Directory editor for Emacs. An incredibly nifty piece of +software which deeply integrates with Emacs as a whole. I can't think +of a better file management tool than this. + +Here I setup dired with a few niceties ++ Hide details by default (no extra stuff from ~ls~) ++ Omit dot files by default (using ~dired-omit-mode~) ++ If I have two dired windows open, moving or copying files in one + dired instance will automatically target the other dired window + (~dired-dwim~) ++ If opening an application on a PDF file, suggest ~zathura~ ++ Examine all the subdirectories within the same buffer + (~+dired/insert-all-subdirectories~) #+begin_src emacs-lisp -(use-package org +(use-package dired :defer t + :commands (dired find-dired) :hook - (org-mode-hook . prettify-symbols-mode) - :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" - "* " _)) + (dired-mode-hook . auto-revert-mode) + (dired-mode-hook . dired-hide-details-mode) + (dired-mode-hook . dired-omit-mode) + :init + (setq-default dired-listing-switches "-AFBlu --group-directories-first" + dired-omit-files "^\\." ; dotfiles + dired-omit-verbose nil + dired-dwim-target t + dired-kill-when-opening-new-dired-buffer t) + (with-eval-after-load "evil-collection" + (evil-collection-dired-setup)) + :general + (nmmap + :keymaps 'dired-mode-map + "SPC" nil + "SPC ," nil + "(" #'dired-hide-details-mode + ")" #'dired-omit-mode + "T" #'dired-create-empty-file + "H" #'dired-up-directory + "L" #'dired-find-file) + (leader + "D" #'dired-jump) + (dir-leader + "f" #'find-dired + "d" #'dired + "D" #'dired-other-window + "i" #'image-dired + "b" `(,(proc (interactive) + (dired "~/Text/Books/")) + :which-key "Open Books")) + (local-leader + :keymaps 'dired-mode-map + "i" #'dired-maybe-insert-subdir + "I" #'+dired/insert-all-subdirectories + "o" #'dired-omit-mode + "k" #'dired-prev-subdir + "j" #'dired-next-subdir + "K" #'dired-kill-subdir + "m" #'dired-mark-files-regexp + "u" #'dired-undo) + :config + (add-multiple-to-list dired-guess-shell-alist-user + '("\\.pdf\\'" "zathura") + '("\\.epub\\'" "zathura") + '("\\.jpg\\'" "feh") + '("\\.png\\'" "feh") + '("\\.webm\\'" "mpv") + '("\\.mp[34]\\'" "mpv") + '("\\.mkv\\'" "mpv")) + (defun +dired/insert-all-subdirectories () + "Insert all subdirectories currently viewable." + (interactive) + (dired-mark-directories nil) + (mapc #'dired-insert-subdir (dired-get-marked-files)) + (dired-unmark-all-marks))) #+end_src -** 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. +*** 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 org +(use-package dired :defer t - :config - (with-eval-after-load "consult" - (general-def - :keymaps 'org-mode-map - [remap consult-imenu] #'consult-outline)) + :init + (setq image-dired-external-viewer "nsxiv") :general - (file-leader - "l" #'org-store-link - "i" #'org-insert-last-stored-link) - (code-leader - :keymaps 'emacs-lisp-mode-map - "D" #'org-babel-detangle) - (local-leader - :state '(normal motion) - :keymaps 'org-src-mode-map - "o" #'org-edit-src-exit) - (local-leader - :keymaps 'org-mode-map - "l" '(nil :which-key "Links") - "'" '(nil :which-key "Tables") - "c" '(nil :which-key "Clocks") - "r" #'org-refile - "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 - "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)) + (nmmap + :keymaps 'image-dired-thumbnail-mode-map + "h" #'image-dired-backward-image + "l" #'image-dired-forward-image + "j" #'image-dired-next-line + "k" #'image-dired-previous-line + "H" #'image-dired-display-previous + "L" #'image-dired-display-next + "RET" #'image-dired-display-this + "m" #'image-dired-mark-thumb-original-file + "q" #'quit-window)) #+end_src -** Org Agenda -Org agenda provides a nice viewing for schedules. With org mode it's -a very tidy way to manage your time. +*** fd-dired +Uses fd for finding file results in a directory: ~find-dired~ -> +~fd-dired~. #+begin_src emacs-lisp -(use-package org-agenda - :defer t - :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) - :config - (evil-set-initial-state 'org-agenda-mode 'normal) +(use-package fd-dired + :straight t + :after dired :general - (file-leader - "a" `(,(proc (interactive) - (find-file (completing-read "Enter directory: " org-agenda-files nil t))) - :which-key "Open agenda directory")) - (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)) + (dir-leader + "g" #'fd-dired)) #+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. +*** 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 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"))) +(use-package wdired + :after dired + :hook (wdired-mode-hook . undo-tree-mode) :general - (leader - "C" #'org-capture) (nmmap - :keymaps 'org-capture-mode-map - "ZZ" #'org-capture-finalize - "ZR" #'org-capture-refile - "ZQ" #'org-capture-kill)) + :keymaps 'dired-mode-map + "W" #'wdired-change-to-wdired-mode) + (nmmap + :keymaps 'wdired-mode-map + "ZZ" #'wdired-finish-edit + "ZQ" #'wdired-abort-changes) + :config + (eval-after-load "evil" + ;; 2024-09-07: Why does evil-set-initial-state returning a list of modes for + ;; normal state make eval-after-load evaluate as if it were an actual + ;; expression? + (progn (evil-set-initial-state 'wdired-mode 'normal) + nil))) #+end_src -** WIP Org clock-in -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -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. +*** 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 org-clock - :after org +(use-package dired-rsync + :straight t + :after dired :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)) + (nmmap + :keymaps 'dired-mode-map + "M-r" #'dired-rsync)) #+end_src -** WAIT Org ref -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -For bibliographic stuff in $\LaTeX$ export. +** 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 org-ref - :straight t +(use-package eshell :defer t + :display + ("\\*eshell\\*" + (display-buffer-same-window) + (reusable-frames . t)) :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 + (defun +eshell/banner-message () + (concat (shell-command-to-string "fortune") "\n")) -#+begin_src emacs-lisp -(use-package ivy-bibtex - :straight t - :after org-ref - :config - (require 'org-ref-ivy)) + (setq eshell-cmpl-ignore-case t + eshell-cd-on-directory t + eshell-cd-shows-directory nil + eshell-highlight-prompt nil + eshell-banner-message '(+eshell/banner-message)) + + (defun +eshell/good-clear () + (interactive) + (eshell/clear-scrollback) + (eshell-send-input)) + + (add-hook + 'eshell-mode-hook + (defun +eshell/--setup-keymap nil + (interactive) + (general-def + :states '(normal insert visual) + :keymaps 'eshell-mode-map + "M-j" #'eshell-next-prompt + "M-k" #'eshell-previous-prompt + "C-j" #'eshell-next-matching-input-from-input + "C-k" #'eshell-previous-matching-input-from-input) + + (local-leader + :keymaps 'eshell-mode-map + "g" (proc (interactive) + (let ((buffer (current-buffer))) + (eshell/goto) + (with-current-buffer buffer + (eshell-send-input)))) + "l" (proc (interactive) + (eshell-return-to-prompt) + (insert "ls") + (eshell-send-input)) + "c" #'+eshell/good-clear + "k" #'eshell-kill-process)))) #+end_src -** 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). +*** 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 org-msg - :straight t - :hook - (message-mode-hook . org-msg-mode) - (notmuch-message-mode-hook . org-msg-mode) +(use-package eshell-prompt + :load-path "elisp/" :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"))))) + (setq eshell-prompt-function #'+eshell-prompt/make-prompt)) #+end_src -** Org for evil -Evil org for some nice bindings. +*** 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 evil-org - :straight t - :defer t - :hook (org-mode-hook . evil-org-mode) +(use-package eshell-additions + :demand t + :load-path "elisp/" :general - (nmmap - :keymaps 'org-mode-map - "TAB" #'org-cycle)) + (shell-leader + "t" #'+eshell/open) + (leader + "T" #'+eshell/at-cwd + "E" #'eshell-command)) #+end_src -** WAIT Org reveal -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -Org reveal allows one to export org files as HTML presentations via -reveal.js. Pretty nifty and it's easy to use. +*** 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 ox-reveal +(use-package eshell-syntax-highlighting :straight t - :defer t - :init - (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js" - org-reveal-theme "sky")) + :after eshell + :hook (eshell-mode-hook . eshell-syntax-highlighting-mode)) #+end_src -** Org bookmark -I maintain a bookmarks file at =~/Text/bookmarks.org=. I would like -the ability to construct new bookmarks and open bookmarks. They may -be either articles I want to read, useful information documents or -just straight up youtube videos. So I wrote a -[[file:elisp/org-bookmark.el][library]] myself which does the -appropriate dispatching and work for me. Pretty sweet! - -Also I define a template for org-capture here for bookmarks and add it -to the list ~org-capture-templates~. +** VTerm +There are a few times when EShell doesn't cut it, particularly in the +domain of TUI applications like ~cfdisk~. Emacs comes by default with +some terminal emulators that can run a system wide shell like SH or +ZSH (~shell~ and ~term~ for example), but they're pretty terrible. +~vterm~ is an external package using a shared library for terminal +emulation, and is much better than the default Emacs stuff. +Since my ZSH configuration enables vim emulation, using ~evil~ on top +of it would lead to some weird states. Instead, use the Emacs state +so vim emulation is completely controlled by the shell. #+begin_src emacs-lisp -(use-package org-bookmark - :defer t - :load-path "elisp/" +(use-package vterm + :straight t :general - (file-leader - "b" #'org-bookmark/open-bookmark) + (shell-leader + "v" #'vterm) :init - (with-eval-after-load "org-capture" - (add-to-list - 'org-capture-templates - '("b" "Bookmark" entry - (file "bookmarks.org") - "* %? :bookmark: -%T -%^{url|%x}p -" - )))) + (with-eval-after-load "evil" + (evil-set-initial-state 'vterm-mode 'emacs))) #+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++. +** (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 make-mode +(use-package grep :defer t - :auto-insert - (("[mM]akefile\\'" . "Makefile skeleton") - "" - "CC=gcc -OUT=main.out -LIBS= -ARGS= - -RELEASE=0 -GFLAGS=-Wall -Wextra -Werror -Wswitch-enum -std=c11 -DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined -RFLAGS=-O3 -DEPFLAGS=-MT $@ -MMD -MP -MF -ifeq ($(RELEASE), 1) -CFLAGS=$(GFLAGS) $(RFLAGS) -else -CFLAGS=$(GFLAGS) $(DFLAGS) -endif - -SRC=src -DIST=build -CODE=$(addprefix $(SRC)/, ) # add source files here -OBJECTS=$(CODE:$(SRC)/%.c=$(DIST)/%.o) -DEPDIR:=$(DIST)/dependencies -DEPS:=$(CODE:$(SRC)/%.c=$(DEPDIR):%.d) $(DEPDIR)/main.d - -.PHONY: all -all: $(OUT) - -$(OUT): $(DIST)/$(OUT) - -$(DIST)/$(OUT): $(OBJECTS) $(DIST)/main.o | $(DIST) - $(CC) $(CFLAGS) $^ -o $@ $(LIBS) - -$(DIST)/%.o: $(SRC)/%.c | $(DIST) $(DEPDIR) - $(CC) $(CFLAGS) $(DEPFLAGS) $(DEPDIR)/$*.d -c $< -o $@ $(LIBS) - -.PHONY: run -run: $(DIST)/$(OUT) - ./$^ $(ARGS) - -.PHONY: -clean: - rm -rfv $(DIST)/* + :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) -$(DIST): - mkdir -p $(DIST) + (defun grep-file (query filename) + (grep (format "grep --color=auto -nIiHZEe \"%s\" -- %s" + query filename))) -$(DEPDIR): - mkdir -p $(DEPDIR) + (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))))) --include $(DEPS) -" - _)) + (defun grep-config-file () + (interactive) + (let ((query (read-string "Search for: " "^[*]+ .*"))) + (grep-file query (concat user-emacs-directory "config.org"))))) #+end_src -** WAIT SQL -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -The default SQL package provides support for connecting to common -database types (sqlite, mysql, etc) for auto completion and query -execution. I don't use SQL currently but whenever I need it it's -there. - +*** rg #+begin_src emacs-lisp -(use-package sql +(use-package rg + :straight t :defer t + :commands (+rg/project-todo) + :display + ("^\\*\\*ripgrep\\*\\*" + (display-buffer-reuse-window display-buffer-at-bottom) + (window-height . 0.35)) + :general + (search-leader + "r" #'rg) + (:keymaps 'project-prefix-map + "t" `(+rg/project-todo :which-key "Project TODOs")) + (nmmap + :keymaps 'rg-mode-map + "c" #'rg-recompile + "C" #'rg-rerun-toggle-case + "]]" #'rg-next-file + "[[" #'rg-prev-file + "q" #'quit-window + "i" #'wgrep-change-to-wgrep-mode) :init - (setq sql-display-sqli-buffer-function nil)) + (setq rg-group-result t + rg-hide-command t + rg-show-columns nil + rg-show-header t + rg-custom-type-aliases nil + rg-default-alias-fallback "all" + rg-buffer-name "*ripgrep*") + :config + (defun +rg/project-todo () + (interactive) + (rg "TODO" "*" + (if (project-current) + (project-root (project-current)) + default-directory))) + (evil-set-initial-state 'rg-mode 'normal)) #+end_src -** WAIT Ada +** WAIT Elfeed :PROPERTIES: :header-args:emacs-lisp: :tangle no :results none :END: -Check out [[file:elisp/ada-mode.el][ada-mode]], my custom ~ada-mode~ -that replaces the default one. This mode just colourises stuff, and -uses eglot and a language server to do the hard work. - -#+begin_src emacs-lisp -(use-package ada-mode - :load-path "elisp/" - :defer t - :config - (with-eval-after-load "eglot" - (add-hook 'ada-mode-hook #'eglot))) -#+end_src -** NHexl -Hexl-mode is the inbuilt package within Emacs to edit hex and binary -format buffers. There are a few problems with hexl-mode though, -including an annoying prompt on /revert-buffer/. - -Thus, nhexl-mode! It comes with a few other improvements. Check out -the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself. +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 nhexl-mode +(use-package elfeed :straight t - :defer t - :mode ("\\.bin" "\\.out")) -#+end_src -** C/C++ -Setup for C and C++ modes, using Emacs' default package: cc-mode. -*** cc-mode -Tons of stuff, namely: -+ ~auto-fill-mode~ for 80 char limit -+ Some keybindings to make evil statement movement easy -+ Lots of pretty symbols -+ Indenting options and a nice (for me) code style for C -+ Auto inserts to get a C file going - -#+begin_src emacs-lisp -(use-package cc-mode - :defer t - :hook - (c-mode-hook . auto-fill-mode) - (c++-mode-hook . auto-fill-mode) :general - (:keymaps '(c-mode-map - c++-mode-map) - :states '(normal motion visual) - "(" #'c-beginning-of-statement - ")" #'c-end-of-statement - "{" #'c-beginning-of-defun - "}" #'c-end-of-defun) + (app-leader "r" #'elfeed) + (nmmap + :keymaps 'elfeed-search-mode-map + "gr" #'elfeed-update + "s" #'elfeed-search-live-filter + "" #'elfeed-search-show-entry) :init - (setq-default c-basic-offset 2) - (setq-default c-auto-newline nil) - (setq-default c-default-style '((other . "user"))) - (defun +cc/copyright-notice () - (let* ((lines (split-string (+license/copyright-notice) "\n")) - (copyright-line (car lines)) - (rest (cdr lines))) - (concat - "* " - copyright-line - "\n" - (mapconcat - #'(lambda (x) - (if (string= x "") - "" - (concat " * " x))) - rest - "\n")))) - :auto-insert - (("\\.c\\'" . "C skeleton") - "" - "/" (+cc/copyright-notice) "\n\n" - " * Created: " (format-time-string "%Y-%m-%d") "\n" - " * Author: " user-full-name "\n" - " * Description: " _ "\n" - " */\n" - "\n") - (("\\.cpp\\'" "C++ skeleton") - "" - "/" (+cc/copyright-notice) "\n\n" - " * Created: " (format-time-string "%Y-%m-%d") "\n" - " * Author: " user-full-name "\n" - " * Description: " _ "\n" - " */\n" - "\n") - (("\\.\\([Hh]\\|hh\\|hpp\\|hxx\\|h\\+\\+\\)\\'" . "C / C++ header") - (replace-regexp-in-string "[^A-Z0-9]" "_" - (string-replace "+" "P" - (upcase - (file-name-nondirectory buffer-file-name)))) - "/" (+cc/copyright-notice) "\n\n" - " * Created: " (format-time-string "%Y-%m-%d") "\n" - " * Author: " user-full-name "\n" - " * Description: " _ "\n" - " */\n\n" - "#ifndef " str n "#define " str "\n\n" "\n\n#endif") + (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) + + (setq +rss/feed-urls + '(("Arch Linux" + "https://www.archlinux.org/feeds/news/" + News Technology) + ("The Onion" + "https://www.theonion.com/rss" + Social) + ("Protesilaos Stavrou" + "https://www.youtube.com/@protesilaos" + YouTube Technology) + ("Tsoding Daily" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" + YouTube Technology) + ("Tsoding" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" + YouTube Technology) + ("Nexpo" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw" + YouTube Stories) + ("3B1B" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw" + YouTube) + ("Fredrik Knusden" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w" + YouTube Stories) + ("Barely Sociable" + "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw" + YouTube Stories) + ("Atrocity Guide" + "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw" + YouTube Stories) + ("Hacker News" + "https://news.ycombinator.com/rss" + Social News Technology) + ("Hacker Factor" + "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2" + Social))) :config - (c-add-style - "user" - '((c-basic-offset . 2) - (c-comment-only-line-offset . 0) - (c-hanging-braces-alist (brace-list-open) - (brace-entry-open) - (substatement-open after) - (block-close . c-snug-do-while) - (arglist-cont-nonempty)) - (c-cleanup-list brace-else-brace) - (c-offsets-alist - (statement-block-intro . +) - (substatement-open . 0) - (access-label . -) - (inline-open . 0) - (label . 0) - (statement-cont . +))))) + (with-eval-after-load "evil-collection" + (evil-collection-elfeed-setup)) + + (setq elfeed-feeds (cl-map 'list #'(lambda (item) + (append (list (nth 1 item)) (cdr (cdr item)))) + +rss/feed-urls)) + + (advice-add 'elfeed-search-show-entry :after #'+elfeed/dispatch-entry) + + (defun +elfeed/dispatch-entry (entry) + "Process each type of entry differently. + e.g., you may want to open HN entries in eww." + (let ((url (elfeed-entry-link entry))) + (pcase url + ((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch")) + (mpv-play-url url)) + (_ (eww url)))))) #+end_src -*** Clang format -clang-format is a program that formats C/C++ files. It's highly -configurable and quite fast. I have a root configuration in my -Dotfiles (check it out -[[file:~/Dotfiles/ClangFormat/).clang-format][here]]. - -Clang format comes inbuilt with clang/LLVM, so it's quite likely to be -on your machine. +** IBuffer +IBuffer is the dired of buffers. Nothing much else to be said. #+begin_src emacs-lisp -(use-package clang-format - :load-path "/usr/share/clang/" +(use-package ibuffer :defer t - :after cc-mode - :commands (+code/clang-format-region-or-buffer - clang-format-mode) :general - (code-leader - :keymaps '(c-mode-map c++-mode-map) - "f" #'clang-format-buffer) + (buffer-leader + "i" #'ibuffer) :config - (define-minor-mode clang-format-mode - "On save formats the current buffer via clang-format." - :lighter nil - (let ((save-func (proc (interactive) - (clang-format-buffer)))) - (if clang-format-mode - (add-hook 'before-save-hook save-func nil t) - (remove-hook 'before-save-hook save-func t)))) - (defun +code/clang-format-region-or-buffer () - (interactive) - (if (mark) - (clang-format-region (region-beginning) (region-end)) - (clang-format-buffer)))) + (with-eval-after-load "evil-collection" + (evil-collection-ibuffer-setup))) #+end_src -*** cc org babel -To ensure org-babel executes language blocks of C/C++, I need to load -it as an option in ~org-babel-load-languages~. +** Proced +Emacs has two systems for process management: ++ proced: a general 'top' like interface which allows general + management of linux processes ++ list-processes: a specific Emacs based system that lists processes + spawned by Emacs (similar to a top for Emacs specifically) + +Core Proced config, just a few bindings and evil collection setup. #+begin_src emacs-lisp -(use-package org - :after cc-mode +(use-package proced + :defer t + :general + (app-leader + "p" #'proced) + (nmap + :keymaps 'proced-mode-map + "za" #'proced-toggle-auto-update) + :display + ("\\*Proced\\*" + (display-buffer-at-bottom) + (window-height . 0.25)) :init - (org-babel-do-load-languages - 'org-babel-load-languages - '((C . t)))) + (setq proced-auto-update-interval 5) + :config + (with-eval-after-load "evil-collection" + (evil-collection-proced-setup))) #+end_src -*** cc compile fsan -Sanitisers are a blessing for C/C++. An additional runtime on top of -the executable which catches stuff like undefined behaviour or memory -leaks make it super easy to see where and how code is failing. -However, by default, Emacs' compilation-mode doesn't understand the -logs =fsanitize= makes so you usually have to manually deal with it -yourself. - -Compilation mode uses regular expressions to figure out whether -something is an error and how to navigate to the file where that error -is located. So adding support for =-fsanitize= is as simple as making -a regular expression which captures file names and digits +** 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 -#+begin_src emacs-lisp -(use-package compile - :after cc-mode - :config - (add-to-list 'compilation-error-regexp-alist-alist - `(fsan ,(rx (and - line-start " #" digit " 0x" (1+ hex) " in " - (1+ (or word "_")) " " - (group (seq (* any) (or ".c" ".cpp" ".h" ".hpp"))) ":" - (group (+ digit)))) +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. - 1 2)) - (add-to-list 'compilation-error-regexp-alist - 'fsan)) -#+end_src -** Markdown -Why use Markdown when you have org-mode? Because LSP servers -sometimes format their documentation as markdown, which -[[*Eglot][Eglot]] can use to provide nicer views on docs! #+begin_src emacs-lisp -(use-package markdown-mode +(use-package calc :defer t - :straight t) + :display + ("*Calculator*" + (display-buffer-at-bottom) + (window-height . 0.2)) + :general + (app-leader + "c" #'calc-dispatch) + :init + (setq calc-algebraic-mode t) + :config + (with-eval-after-load "evil-collection" + (evil-collection-calc-setup))) #+end_src -** WAIT D -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -D is a systems level programming language with C-style syntax. I -think it has some interesting ideas such as a toggleable garbage -collector. Here I just install the D-mode package, enable ~org-babel~ -execution of d-mode blocks and alias ~D-mode~ with ~d-mode~. +** Zone +Emacs' out of the box screensaver software. #+begin_src emacs-lisp -(use-package d-mode +(use-package zone :defer t - :straight t - :config - (fset 'D-mode 'd-mode) - (with-eval-after-load "org-mode" - (setf (alist-get 'd org-babel-load-languages) t))) + :commands (zone) + :general + (leader + "z" #'zone) + :init + + (setq zone-programs + [zone-pgm-drip + zone-pgm-drip-fretfully])) #+end_src -** WAIT Rust -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -Rust is the systems programming language that also does web stuff and -CLI programs and basically tries to be a jack of all trades. It's got -some interesting stuff but most importantly it's very new, so everyone -must learn it, right? +** (Wo)man +Man pages are the user manuals for most software on Linux. Of course, +Emacs comes out of the box with a renderer for man pages and some +searching capabilities. + +2023-08-17: `Man-notify-method' is the reason the `:display' record +doesn't work here. I think it's to do with how Man pages are rendered +or something, but very annoying as it's a break from standards! + +2024-10-08: Man pages are rendered via a separate process, which is +why this is necessary. #+begin_src emacs-lisp -(use-package rust-mode - :straight t +(use-package man :defer t - :general - (code-leader - :keymaps 'rust-mode-map - "f" #'rust-format-buffer) - (local-leader - :keymaps 'rust-mode-map - "c" #'rust-run-clippy) :init - (setq rust-format-on-save t) - (with-eval-after-load "eglot" - (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer")))) + (setq Man-notify-method 'thrifty) + :display + ("\\*Man.*" + (display-buffer-reuse-mode-window display-buffer-same-window) + (mode . Man-mode)) + :general + (file-leader + "m" #'man) ;; kinda like "find man page" + (nmmap + :keymaps 'Man-mode-map + "RET" #'man-follow)) #+end_src -** WAIT Racket -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -A scheme with lots of stuff inside it. Using it for a language design -book so it's useful to have some Emacs binds for it. +** 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 racket-mode - :straight t +(use-package info :defer t - :hook (racket-mode-hook . racket-xp-mode) - :display - ("\\*Racket REPL*" - (display-buffer-at-bottom) - (window-height . 0.3)) - :init - (setq racket-documentation-search-location 'local) :general - (nmap - :keymaps 'racket-describe-mode-map - "q" #'quit-window) - (nmap - :keymaps 'racket-mode-map - "gr" #'racket-eval-last-sexp) - (local-leader - :keymaps '(racket-mode-map racket-repl-mode-map) - "d" #'racket-repl-describe) - (local-leader - :keymaps 'racket-mode-map - "r" #'racket-run - "i" #'racket-repl - "e" #'racket-send-definition - "sr" #'racket-send-region - "sd" #'racket-send-definition)) + (nmmap + :keymaps 'Info-mode-map + "h" #'evil-backward-char + "k" #'evil-previous-line + "l" #'evil-forward-char + "H" #'Info-history-back + "L" #'Info-history-forward + "C-j" #'Info-forward-node + "C-k" #'Info-backward-node + "RET" #'Info-follow-nearest-node + "m" #'Info-menu + "C-o" #'Info-history-back + "s" #'Info-search + "S" #'Info-search-case-sensitively + "i" #'Info-index + "a" #'info-apropos + "gj" #'Info-next + "gk" #'Info-prev + "g?" #'Info-summary + "q" #'quit-window) + :init + (with-eval-after-load "evil" + (evil-set-initial-state 'Info-mode 'normal))) #+end_src -** WAIT CSharp +** WAIT gif-screencast :PROPERTIES: :header-args:emacs-lisp: :tangle no :results none :END: -Haven't used C# in a while, but Emacs is alright for it with -omnisharp. +Little application that uses =gifsicle= to make essentially videos of +Emacs. Useful for demonstrating features. #+begin_src emacs-lisp -(use-package csharp-mode - :defer t) +(use-package gif-screencast + :straight t + :general + (app-leader + "x" #'gif-screencast-start-or-stop) + :init + (setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/"))) #+end_src -** WAIT Java -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -I kinda dislike Java, but if necessary I will code in it. Just setup -a style and some pretty symbols. You can use LSP to get cooler -features to be fair. +** 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 ob-java +(use-package image-mode :defer t - :config - (with-eval-after-load "cc-mode" - (c-add-style - "java" - '((c-basic-offset . 4) - (c-comment-only-line-offset 0 . 0) - (c-offsets-alist - (inline-open . 0) - (topmost-intro-cont . +) - (statement-block-intro . +) - (knr-argdecl-intro . 5) - (substatement-open . 0) - (substatement-label . +) - (label . +) - (statement-case-open . +) - (statement-cont . +) - (arglist-intro . c-lineup-arglist-intro-after-paren) - (arglist-close . c-lineup-arglist) - (brace-list-intro first c-lineup-2nd-brace-entry-in-arglist c-lineup-class-decl-init-+ +) - (access-label . 0) - (inher-cont . c-lineup-java-inher) - (func-decl-cont . c-lineup-java-throws)))) - (add-to-list 'c-default-style '(java-mode . "java"))) - - (with-eval-after-load "abbrev" - (define-abbrev-table 'java-mode-abbrev-table nil) - (add-hook 'java-mode-hook - (proc (setq-local local-abbrev-table java-mode-abbrev-table))))) + :general + (nmmap + :keymaps 'image-mode-map + "+" #'image-increase-size + "-" #'image-decrease-size + "a" #'image-toggle-animation + "p+" #'image-increase-speed + "p-" #'image-increase-speed + "h" #'image-backward-hscroll + "j" #'image-next-line + "k" #'image-previous-line + "l" #'image-forward-hscroll)) #+end_src -** WAIT Haskell -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -Haskell is a static lazy functional programming language (what a -mouthful). It's quite a beautiful language and really learning it will -change the way you think about programming. However, my preferred -functional language is still unfortunately Lisp so no extra brownie -points there. - -Here I configure the REPL for Haskell via the -~haskell-interactive-mode~. I also load my custom package -[[file:elisp/haskell-multiedit.el][haskell-multiedit]] which allows a -user to create temporary ~haskell-mode~ buffers that, upon completion, -will run in the REPL. Even easier than making your own buffer. - +** empv +Emacs MPV bindings, with very cool controls for queuing files for +playing. #+begin_src emacs-lisp -(use-package haskell-mode +(use-package empv :straight t :defer t - :hook - (haskell-mode-hook . haskell-indentation-mode) - (haskell-mode-hook . interactive-haskell-mode) - :display - ("\\*haskell.**\\*" - (display-buffer-at-bottom) - (window-height . 0.3)) - :general - (shell-leader - "h" #'haskell-interactive-bring) - (local-leader - :keymaps 'haskell-mode-map - "c" #'haskell-compile - "t" #'haskell-process-do-type) - (nmmap - :keymaps 'haskell-mode-map - "C-c C-c" #'haskell-process-load-file) - (local-leader - :keymaps 'haskell-interactive-mode-map - "c" #'haskell-interactive-mode-clear) - (imap - :keymaps 'haskell-interactive-mode-map - "M-k" #'haskell-interactive-mode-history-previous - "M-j" #'haskell-interactive-mode-history-next) :init - (setq haskell-interactive-prompt "[λ] " - haskell-interactive-prompt-cont "{λ} " - haskell-interactive-popup-errors nil - haskell-stylish-on-save t - haskell-process-type 'auto) - :config - (load (concat user-emacs-directory "elisp/haskell-multiedit.el"))) + (setq empv-audio-dir (expand-file-name "~/Media/audio") + empv-video-dir (expand-file-name "~/Media/videos") + empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" + "flac" "aac" "opus") + empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" + "webm")) + :hydra + (empv-hydra + nil "Hydra for EMPV" + ("(" #'empv-chapter-prev + "chapter-prev" :column "playback") + (")" #'empv-chapter-next + "chapter-next" :column "playback") + ("0" #'empv-volume-up + "volume-up" :column "playback") + ("9" #'empv-volume-down + "volume-down" :column "playback") + ("[" #'empv-playback-speed-down + "playback-speed-down" :column "playback") + ("]" #'empv-playback-speed-up + "playback-speed-up" :column "playback") + ("_" #'empv-toggle-video + "toggle-video" :column "playback") + ("q" #'empv-exit + "exit" :column "playback") + ("s" #'empv-seek + "seek" :column "playback") + ("t" #'empv-toggle + "toggle" :column "playback") + ("x" #'empv-chapter-select + "chapter-select" :column "playback") + ("N" #'empv-playlist-prev + "playlist-prev" :column "playlist") + ("C" #'empv-playlist-clear + "playlist-clear" :column "playlist") + ("n" #'empv-playlist-next + "playlist-next" :column "playlist") + ("p" #'empv-playlist-select + "playlist-select" :column "playlist") + ("S" #'empv-playlist-shuffle + "playlist-shuffle" :column "playlist") + ("a" #'empv-play-audio + "play-audio" :column "play") + ("R" #'empv-play-random-channel + "play-random-channel" :column "play") + ("d" #'empv-play-directory + "play-directory" :column "play") + ("f" #'empv-play-file + "play-file" :column "play") + ("o" #'empv-play-or-enqueue + "play-or-enqueue" :column "play") + ("r" #'empv-play-radio + "play-radio" :column "play") + ("v" #'empv-play-video + "play-video" :column "play") + ("i" #'empv-display-current + "display-current" :column "misc") + ("l" #'empv-log-current-radio-song-name + "log-current-radio-song-name" :column "misc") + ("c" #'empv-copy-path + "copy-path" :column "misc") + ("Y" #'empv-youtube-last-results + "youtube-last-results" :column "misc") + ("y" #'empv-youtube + "youtube" :column "misc")) + :general + (app-leader + "e" #'empv-hydra/body)) #+end_src -** Python -Works well for python. If you have ~pyls~ it should be on your path, so -just run eglot if you need. But an LSP server is not necessary for a -lot of my time in python. Here I also setup org-babel for python -source code blocks. +** Grand Unified Debugger (GUD) +GUD is a system for debugging, hooking into processes and +providing an interface to the user all in Emacs. Here I define a +hydra which provides a ton of the useful =gud= keybindings that exist +in an Emacs-only map. +#+begin_src emacs-lisp +(use-package gud + :general + :after hydra + :hydra + (gud-hydra + (:hint nil) "Hydra for GUD" + ("<" #'gud-up "Up" + :column "Control Flow") + (">" #'gud-down "Down" + :column "Control Flow") + ("b" #'gud-break "Break" + :column "Breakpoints") + ("d" #'gud-remove "Remove" + :column "Breakpoints") + ("f" #'gud-finish "Finish" + :column "Control Flow") + ("J" #'gud-jump "Jump" + :column "Control Flow") + ("L" #'gud-refresh "Refresh" + :column "Misc") + ("n" #'gud-next "Next" + :column "Control Flow") + ("p" #'gud-print "Print" + :column "Misc") + ("c" #'gud-cont "Cont" + :column "Control Flow") + ("s" #'gud-step "Step" + :column "Control Flow") + ("t" #'gud-tbreak "Tbreak" + :column "Control Flow") + ("u" #'gud-until "Until" + :column "Control Flow") + ("v" #'gud-go "Go" + :column "Control Flow") + ("w" #'gud-watch "Watch" + :column "Breakpoints") + ("TAB" #'gud-stepi "Stepi" + :column "Control Flow")) + :general + (code-leader "d" #'gud-hydra/body + "D" #'gud-gdb)) +#+end_src +** WAIT esup +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +I used to be able to just use +[[file:elisp/profiler-dotemacs.el][profile-dotemacs.el]], when my +Emacs config was smaller, but now it tells me very little information +about where my setup is inefficient due to the literate config. Just +found this ~esup~ thing and it works perfectly, exactly how I would +prefer getting this kind of information. It runs an external Emacs +instance and collects information from it, so it doesn't require +restarting Emacs to profile, and I can compile my configuration in my +current instance to test it immediately. + +2023-10-16: Unless I'm doing some optimisations or tests, I don't +really need this in my config at all times. Enable when needed. #+begin_src emacs-lisp -(use-package python +(use-package esup + :straight t :defer t :general - (nmmap - :keymaps 'python-mode-map - "C-M-x" #'python-shell-send-defun) - (local-leader - :keymaps 'python-mode-map - "c" #'python-check) - (local-leader - :keymaps 'python-mode-map - :infix "e" - "e" #'python-shell-send-statement - "r" #'python-shell-send-region - "f" #'python-shell-send-buffer) - :pretty - (python-mode-hook - ("None" . "Ø") - ("list" . "ℓ") - ("List" . "ℓ") - ("str" . "𝕊") - ("!" . "¬") - ("for" . "∀") - ("print" . "φ") - ("lambda" . "λ") - ("reduce" . "↓") - ("map" . "→") - ("return" . "≡") - ("yield" . "≈")) - :init - (setq python-indent-offset 4) + (leader + "qe" #'esup)) +#+end_src +* Miscellaneous +** Evil additions +Additional packages that add the functionality of plugins in Vim I +really liked, as well as some new stuff. +*** Evil surround +A port for vim-surround, providing the ability to mutate delimiters +around some text. + +#+begin_src emacs-lisp +(use-package evil-surround + :after evil + :straight t :config - (with-eval-after-load "org-mode" - (setf (alist-get 'python org-babel-load-languages) t))) + (global-evil-surround-mode)) #+end_src -*** Python shell -Setup for python shell, including a toggle option +*** Evil commentary +A port of vim-commentary, providing generalised commenting of objects. #+begin_src emacs-lisp -(use-package python - :defer t - :commands +python/toggle-repl +(use-package evil-commentary + :after evil + :straight t + :config + (evil-commentary-mode)) +#+end_src +*** Evil multi cursor +Setup for multi cursors in Evil mode, which is a bit of very nice +functionality. Don't let evil-mc setup it's own keymap because it +uses 'gr' as its prefix, which I don't like. + +#+begin_src emacs-lisp +(use-package evil-mc + :after evil + :straight t + :init + (defvar evil-mc-key-map (make-sparse-keymap)) :general - (shell-leader - "p" #'run-python) - :hook - (inferior-python-mode-hook . company-mode) - :display - ("\\*Python\\*" - (display-buffer-at-bottom) - (window-height . 0.3))) + (nmap + :infix "gz" + "m" 'evil-mc-make-all-cursors + "u" 'evil-mc-undo-last-added-cursor + "q" 'evil-mc-undo-all-cursors + "s" 'evil-mc-pause-cursors + "r" 'evil-mc-resume-cursors + "f" 'evil-mc-make-and-goto-first-cursor + "l" 'evil-mc-make-and-goto-last-cursor + "h" 'evil-mc-make-cursor-here + "j" 'evil-mc-make-cursor-move-next-line + "k" 'evil-mc-make-cursor-move-prev-line + "N" 'evil-mc-skip-and-goto-next-cursor + "P" 'evil-mc-skip-and-goto-prev-cursor + "n" 'evil-mc-skip-and-goto-next-match + "p" 'evil-mc-skip-and-goto-prev-match + "I" 'evil-mc-make-cursor-in-visual-selection-beg + "A" 'evil-mc-make-cursor-in-visual-selection-end + "d" #'evil-mc-make-and-goto-next-match) + :config + (global-evil-mc-mode)) #+end_src -** YAML -YAML is a data language which is useful for config files. +*** Evil multi edit +Evil-ME provides a simpler parallel editing experience within the same +buffer. I use it in-tandem with Evil-MC, where I use Evil-ME for +textual changes and Evil-MC for more complex motions. #+begin_src emacs-lisp -(use-package yaml-mode +(use-package evil-multiedit + :straight t :defer t - :straight t) + :init + (setq evil-multiedit-scope 'visible) + :general + (:states '(normal visual) + :keymaps 'override + "M-e" #'evil-multiedit-match-and-next + "M-E" #'evil-multiedit-match-and-prev)) #+end_src -** HTML/CSS/JS -Firstly, web mode for consistent colouring of syntax. +*** Evil collection +Provides a community based set of keybindings for most modes in +Emacs. I don't necessarily like all my modes having these bindings +though, as I may disagree with some. So I use it in a mode to mode basis. #+begin_src emacs-lisp -(use-package web-mode +(use-package evil-collection :straight t - :defer t - :mode ("\\.html" . web-mode) - :mode ("\\.css" . web-mode) - :custom - ((web-mode-code-indent-offset 2) - (web-mode-markup-indent-offset 2) - (web-mode-css-indent-offset 2))) + :after evil) #+end_src -*** Emmet -Emmet for super speed code writing. +*** Evil number +Increment/decrement a number at point like Vim does, but use bindings +that don't conflict with Emacs default. #+begin_src emacs-lisp -(use-package emmet-mode +(use-package evil-numbers :straight t :defer t - :hook (web-mode-hook . emmet-mode) :general - (imap - :keymaps 'emmet-mode-keymap - "TAB" #'emmet-expand-line - "M-j" #'emmet-next-edit-point - "M-k" #'emmet-prev-edit-point)) + (nmmap + "+" #'evil-numbers/inc-at-pt + "-" #'evil-numbers/dec-at-pt)) #+end_src -*** HTML Auto insert -An auto-insert for HTML buffers, which just adds some nice stuff. - +*** Evil goggles +Make it easier to notice edits and changes using Vim motions! #+begin_src emacs-lisp -(use-package web-mode - :defer t - :auto-insert - (("\\.html\\'" . "HTML Skeleton") - "" - " - - - - "(read-string "Enter title: ") | """ - - - +(use-package evil-goggles + :straight t + :after evil + :init + (setq evil-goggles-duration 0.1 + evil-goggles-blocking-duration 0.1 + evil-goggles-async-duration 0.9 + evil-goggles-default-face 'pulsar-cyan) + :config + (evil-goggles-mode) + (evil-goggles-use-diff-faces)) +#+end_src +** Save place +Saves current place in a buffer permanently, so on revisiting the file +(even in a different Emacs instance) you go back to the place you were +at last. - - - - -" - _ - " -")) +#+begin_src emacs-lisp +(use-package saveplace + :demand t + :config + (save-place-mode)) #+end_src -*** Javascript Mode -A better mode for JavaScript that also has automatic integration with -eglot. +** Tabs +Tabs in vscode are just like buffers in Emacs but way slower and +harder to use. Tabs in Emacs are essentially window layouts, similar +to instances in Tmux. With this setup I can use tabs quite +effectively. #+begin_src emacs-lisp -(use-package js +(use-package tab-bar :defer t - :mode ("\\.js" . js-mode) - :hook (js-mode-hook . auto-fill-mode) + :hook (after-init-hook . tab-bar-mode) :init - (setq js-indent-level 2)) + (setq tab-bar-close-button-show nil + tab-bar-format '(tab-bar-format-history + tab-bar-format-tabs tab-bar-separator) + tab-bar-show 1 + tab-bar-auto-width t + tab-bar-auto-width-max '((100) 20) + tab-bar-auto-width-min '((20) 2)) + :general + (tab-leader + "R" #'tab-rename + "c" #'tab-close + "d" #'tab-close + "f" #'tab-detach + "h" #'tab-move-to + "j" #'tab-next + "k" #'tab-previous + "l" #'tab-move + "n" #'tab-new + "r" #'tab-switch + "w" #'tab-window-detach)) #+end_src -*** Typescript -A language that adds a build step to JavaScript projects for "static" -typing. It's nice because it adds good auto completion. +** Registers +Emacs comes by default with the notion of "registers". Registers are +essentially an alist of symbols mapped to some Lisp object, which +can be easily accessed and manipulated. Some common use cases of +registers are: ++ Marking locations in a file to quickly go to (using Emacs' in-built + notion of marks) ++ Copying and pasting text without the clipboard (essentially even + more clipboards) ++ Creating number counters (usually for macros) + +Of course, Vim has its own notion of registers which are frankly much +less capable than Emacs. Evil emulates this limited notion of +registers, but I prefer Emacs' hence the configuration here. #+begin_src emacs-lisp -(use-package typescript-mode - :straight t - :defer t - :init - (setq typescript-indent-level 2)) +(use-package register + :config + (defmacro +register/jump-to (reg) + `(proc (interactive) + (jump-to-register ,reg))) + :general + (nmmap + "m" #'point-to-register + "'" #'jump-to-register + "g1" (+register/jump-to ?1) + "g2" (+register/jump-to ?2) + "g3" (+register/jump-to ?3) + "g4" (+register/jump-to ?4) + "g5" (+register/jump-to ?5) + "g6" (+register/jump-to ?6) + "g7" (+register/jump-to ?7) + "g8" (+register/jump-to ?8) + "g9" (+register/jump-to ?9))) #+end_src -** Scheme -Another Lisp but simpler than the rest. A beauty of engineering and -fun to write programs in. Here I setup ~geiser~, which is the -premiere way to interact with scheme REPLs. +** Recentf +Recentf provides a method of keeping track of recently opened files. #+begin_src emacs-lisp -(use-package geiser +(use-package recentf :defer t - :straight t - :display - ("\\*Geiser.*" - (display-buffer-reuse-mode-window display-buffer-at-bottom) - (window-height . 0.3)) + :hook (after-init-hook . recentf-mode) :general - (shell-leader - "S" #'geiser) - (local-leader - :keymaps 'scheme-mode-map - "t" #'geiser - "m" #'geiser-doc-look-up-manual - "d" #'geiser-doc-symbol-at-point) - (local-leader - :keymaps 'scheme-mode-map - :infix "e" - "e" #'geiser-eval-last-sexp - "b" #'geiser-eval-buffer - "d" #'geiser-eval-definition - "r" #'geiser-eval-region) - :init - (with-eval-after-load "evil" - (evil-set-initial-state 'geiser-debug-mode-map 'emacs))) + (file-leader + "r" #'recentf)) +#+end_src +** Memory-report +New feature of Emacs-29, gives a rough report of memory usage with +some details. Useful to know on a long Emacs instance what could be +eating up memory. -(use-package geiser-guile +#+begin_src emacs-lisp +(use-package memory-report :defer t - :straight t) -#+end_src -** WAIT Ocaml -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -*** Ocaml Setup -Firstly, install ~opam~ and ~ocaml~. Then run the following script: -#+begin_src sh -opam install tuareg ocamlformat odoc utop merlin user-setup; -opam user-setup install; -mv ~/.emacs.d/opam-user-setup.el ~/.config/emacs/elisp; -rm -rf ~/.emacs.d ~/.emacs; + :general + (leader + "qm" #'memory-report)) #+end_src +** Helpful +Helpful provides a modern interface for some common help commands. I +replace ~describe-function~, ~describe-variable~ and ~describe-key~ by +their helpful counterparts. -This sets up the necessary packages (particularly Emacs Lisp) and some -configuration that ensures Emacs is consistent with the user -installation. Notice the moving of =opam-user-setup.el= into -=~/.config/emacs/elisp=, which we'll use to setup the ocaml -experience. -*** Ocaml Configuration -Here I load the =opam-user-setup= package setup earlier, with some -neat tips from the default =~/.emacs= generated by ~opam user-setup -install~. #+begin_src emacs-lisp -(use-package opam-user-setup +(use-package helpful + :straight t :defer t - :load-path "elisp/" - :mode ("\\.ml" . tuareg-mode) - :hook (tuareg-mode-hook . whitespace-mode) + :commands (helpful-callable helpful-variable) + :general + ([remap describe-function] #'helpful-callable + [remap describe-variable] #'helpful-variable + [remap describe-key] #'helpful-key) :display - ("\\*utop\\*" + ("\\*helpful.*" (display-buffer-at-bottom) - (window-height . 0.3)) - :general - (code-leader - :keymaps 'tuareg-mode-map - "f" #'+ocaml/format-buffer) + (inhibit-duplicate-buffer . t) + (window-height . 0.25)) :config - (defun +ocaml/format-buffer () - (interactive) - (when (eq major-mode 'tuareg-mode) - (let ((name (buffer-file-name (current-buffer))) - (format-str "ocamlformat -i --enable-outside-detected-project %s")) - (save-buffer) - (set-process-sentinel (start-process-shell-command "ocamlformat" "*ocamlformat*" - (format format-str name)) - (lambda (p event) - (when (string= event "finished\n") - (revert-buffer nil t) - (message "[ocamlformat] Finished."))))))) - (add-to-list 'compilation-error-regexp-alist-alist - `(ocaml - "[Ff]ile \\(\"\\(.*?\\)\", line \\(-?[0-9]+\\)\\(, characters \\(-?[0-9]+\\)-\\([0-9]+\\)\\)?\\)\\(:\n\\(\\(Warning .*?\\)\\|\\(Error\\)\\):\\)?" - 2 3 (5 . 6) (9 . 11) 1 (8 compilation-message-face))) - (add-to-list 'compilation-error-regexp-alist - 'ocaml) - :general - (local-leader - :keymaps 'tuareg-mode-map - "u" #'utop) - (local-leader - :keymaps 'tuareg-mode-map - :infix "e" - "r" #'utop-eval-region - "e" #'utop-eval-phrase - "b" #'utop-eval-buffer)) + (evil-define-key 'normal helpful-mode-map "q" #'quit-window)) +#+end_src +** Avy and Ace +Avy is a package that provides "jump" functions. Given some input, +the current buffer is scanned and any matching candidates are given a +tag which the user can input to perform some action (usually moving +the cursor to that point). +*** Avy core +Setup avy with leader. As I use ~avy-goto-char-timer~ a lot, use the +~C-s~ bind which replaces isearch. Switch isearch to M-s in case I +need to use it. -(use-package merlin-eldoc +#+begin_src emacs-lisp +(use-package avy :straight t - :after opam-user-setup - :hook - (tuareg-mode-hook . merlin-eldoc-setup) - :init - (setq merlin-eldoc-occurrences nil)) + :defer t + :general + (nmmap + :keymaps 'override + "C-s" #'avy-goto-char-timer + "M-s" #'isearch-forward + "gp" #'avy-copy-region + "gP" #'avy-move-region + "gl" #'avy-goto-line + "gw" #'avy-goto-word-1)) #+end_src -** Common Lisp -Common Lisp is a dialect of Lisp, the most /common/ one around. Emacs -comes with builtin Lisp support, of course, and it's really good in -comparison to literally everything else. However, I wish it had a -better REPL... -*** WAIT Sly -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -Enter /SLY/. Sly is a fork of /SLIME/ and is *mandatory* for lisp -development on Emacs. - -Here I just setup Sly to use ~sbcl~. +*** Ace window +Though evil provides a great many features in terms of window +management, ace window can provide some nicer chords for higher +management of windows (closing, switching, etc). #+begin_src emacs-lisp -(use-package sly - :defer t +(use-package ace-window :straight t - :init - (setq inferior-lisp-program "sbcl" - sly-lisp-loop-body-forms-indentation 0) - :display - ("\\*sly-db" - (display-buffer-at-bottom) - (window-height . 0.5)) - ("\\*sly-" - (display-buffer-at-bottom) - (window-height . 0.3)) - :config - (evil-set-initial-state 'sly-db-mode 'normal) - (with-eval-after-load "org" - (setq-default org-babel-lisp-eval-fn #'sly-eval)) - (with-eval-after-load "company" - (add-hook 'sly-mrepl-hook #'company-mode)) - :general - (shell-leader - "s" #'sly) - (nmap - :keymaps 'lisp-mode-map - "gr" #'sly-eval-buffer - "gd" #'sly-edit-definition - "gR" #'sly-who-calls) - (local-leader - :keymaps 'lisp-mode-map - "a" '(sly-apropos :which-key "Apropos") - "d" '(sly-describe-symbol :which-key "Describe symbol") - "D" '(sly-documentation-lookup :which-key "Lookup on lispworks") - "l" '(sly-load-file :which-key "Load file") - "c" '(sly-compile-defun :which-key "Compile defun") - "C" '(sly-compile-file :which-key "Compile file") - "S" '(sly-mrepl-sync :which-key "Sync REPL")) - (local-leader - :keymaps 'lisp-mode-map - :infix "e" - "b" #'sly-eval-buffer - "e" #'sly-eval-last-expression - "f" #'sly-eval-defun - "r" #'sly-eval-region) - (nmap - :keymaps 'sly-mrepl-mode-map - "M-j" #'sly-mrepl-next-input-or-button - "M-k" #'sly-mrepl-previous-input-or-button) - (local-leader - :keymaps 'sly-mrepl-mode-map - "s" '(sly-mrepl-shortcut :which-key "Shortcut")) - (nmap - :keymaps 'sly-db-mode-map - "\C-i" 'sly-db-cycle - "g?" 'describe-mode - "S" 'sly-db-show-frame-source - "e" 'sly-db-eval-in-frame - "d" 'sly-db-pprint-eval-in-frame - "D" 'sly-db-disassemble - "i" 'sly-db-inspect-in-frame - "gj" 'sly-db-down - "gk" 'sly-db-up - (kbd "C-j") 'sly-db-down - (kbd "C-k") 'sly-db-up - "]]" 'sly-db-details-down - "[[" 'sly-db-details-up - (kbd "M-j") 'sly-db-details-down - (kbd "M-k") 'sly-db-details-up - "gg" 'sly-db-beginning-of-backtrace - "G" 'sly-db-end-of-backtrace - "t" 'sly-db-toggle-details - "gr" 'sly-db-restart-frame - "I" 'sly-db-invoke-restart-by-name - "R" 'sly-db-return-from-frame - "c" 'sly-db-continue - "s" 'sly-db-step - "n" 'sly-db-next - "o" 'sly-db-out - "b" 'sly-db-break-on-return - "a" 'sly-db-abort - "q" 'sly-db-quit - "A" 'sly-db-break-with-system-debugger - "B" 'sly-db-break-with-default-debugger - "P" 'sly-db-print-condition - "C" 'sly-db-inspect-condition - "g:" 'sly-interactive-eval - "0" 'sly-db-invoke-restart-0 - "1" 'sly-db-invoke-restart-1 - "2" 'sly-db-invoke-restart-2 - "3" 'sly-db-invoke-restart-3 - "4" 'sly-db-invoke-restart-4 - "5" 'sly-db-invoke-restart-5 - "6" 'sly-db-invoke-restart-6 - "7" 'sly-db-invoke-restart-7 - "8" 'sly-db-invoke-restart-8 - "9" 'sly-db-invoke-restart-9) - (nmap - :keymaps 'sly-inspector-mode-map - "q" #'sly-inspector-quit)) + :defer t + :custom + (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) + :general + (nmmap + [remap evil-window-next] #'ace-window)) #+end_src -*** WAIT Sly-ASDF -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -ASDF is the package declaration system that _most_ Common Lisp -programmers use. Here's a package which integrates some stuff into -SLY for ASDF. +*** Ace link +Avy-style link following! #+begin_src emacs-lisp -(use-package sly-asdf +(use-package ace-link :straight t - :after sly) + :defer t + :general + (nmmap + :keymaps 'override + "gL" #'ace-link)) #+end_src -*** Emacs lisp -Ligatures and bindings for (Emacs) Lisp. Pretty self declarative. +** Drag Stuff +Drag stuff around, like my favourite russian programmer (Tsoding). +Useful mechanism which works better than any vim motion. #+begin_src emacs-lisp -(use-package elisp-mode +(use-package drag-stuff + :straight t :defer t - :pretty - (lisp-mode-hook - ("lambda" . "λ") - ("nil" . "Ø") - ("<=" . "≤") - (">=" . "≥") - ("defun" . "ƒ") - ("loop" . "Σ") - ("mapcar" . "→") - ("reduce" . "↓") - ("some" . "∃") - ("every" . "∀")) - (emacs-lisp-mode-hook - ("lambda" . "λ") - ("nil" . "Ø") - ("defun" . "ƒ") - ("mapcar" . "→")) :general - (:states '(normal motion visual) - :keymaps '(emacs-lisp-mode-map - lisp-mode-map - lisp-interaction-mode-map) - ")" #'sp-next-sexp - "(" #'sp-previous-sexp) (nmmap - :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) - "gr" #'eval-buffer) - (vmap - :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) - "gr" #'eval-region) - (local-leader - :keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map) - "e" #'eval-last-sexp - "f" #'eval-defun)) + "C-M-h" #'drag-stuff-left + "C-M-j" #'drag-stuff-down + "C-M-k" #'drag-stuff-up + "C-M-l" #'drag-stuff-right)) #+end_src -*** WIP Hydra like Lispy -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -A [[*Hydra][Hydra]] which uses the ~Lispy~ package (by -abo-abo) to create a set of motions that allow movement around a lisp -file easily. +** Separedit +Edit anything anywhere all at once! -2024-04-18: Still working on this, quite rough around the edges. +#+begin_src emacs-lisp +(use-package separedit + :defer t + :straight t + :general + (leader "e" #'separedit) + :init + (setq separedit-default-mode 'org-mode + separedit-remove-trailing-spaces-in-comment t)) +#+end_src +** Undo tree +Undo tree sits on top of Emacs' undo capabilities. It provides a nice +visual for the history of a buffer and is a great way to produce +branches of edits. This history may be saved to and loaded from the +disk, which makes Emacs a quasi version control system in and of +itself. The only feature left is describing changes... #+begin_src emacs-lisp -(use-package lispy - :after (lisp-mode elisp-mode) - :hydra - (hydra-lispy - nil "Move around quickly in Lisp" - ("h" #'lispy-left) - ("j" ("t" #'lispy-teleport) - #'lispy-down) - ("k" #'lispy-up) - ("l" #'lispy-right) - ("d" #'lispy-different) - ("u" #'lispy-flow) - ("o" #'lispy-oneline) - ("m" #'lispy-multiline) - ("N" #'lispy-narrow) - ("W" #'lispy-widen) - ("c" #'lispy-clone) - ("fp" #'lispy-ace-paren) - ("fs" #'lispy-ace-symbol :exit t) - ("H" #'lispy-slurp) - ("L" #'lispy-barf) - ("M-h" #'lispy-move-left) - ("M-j" #'lispy-move-down) - ("M-k" #'lispy-move-up) - ("M-l" #'lispy-move-right) - ("C-g" nil)) +(use-package undo-tree + :demand t + :straight t :general - (nmmap - :keymaps '(emacs-lisp-mode-map lisp-mode-map) - "." #'hydra-lispy/body)) + (leader + "u" #'undo-tree-visualize) + :init + (setq undo-tree-auto-save-history t + undo-tree-history-directory-alist backup-directory-alist) + :config + (global-undo-tree-mode)) #+end_src -*** Lisp indent function -Add a new lisp indent function which indents newline lists more -appropriately. +** Searching common directories +Using [[file:elisp/search.el][search.el]] I can search a set of +directories particularly efficiently. #+begin_src emacs-lisp -(use-package lisp-mode +(use-package search + :defer t + :load-path "elisp/" + :general + (search-leader + "a" #'+search/search-all) + (file-leader + "p" #'+search/find-file)) +#+end_src +** Abbrevs +Just define a few abbrevs for various date-time operations. Also +define a macro that will assume a function for the expansion, helping +with abstracting a few things away. + +#+begin_src emacs-lisp +(use-package abbrev :defer t + :hook + (prog-mode-hook . abbrev-mode) + (text-mode-hook . abbrev-mode) + :init + (defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs) + `(progn + ,@(mapcar #'(lambda (abbrev) + `(define-abbrev + ,abbrev-table + ,(car abbrev) + "" + (proc (insert ,(cadr abbrev))))) + abbrevs))) + (setq save-abbrevs nil) :config - (defun +oreo/lisp-indent-function (indent-point state) - (let ((normal-indent (current-column)) - (orig-point (point))) - (goto-char (1+ (elt state 1))) - (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) - (cond - ;; car of form doesn't seem to be a symbol, or is a keyword - ((and (elt state 2) - (or (not (looking-at "\\sw\\|\\s_")) - (looking-at ":"))) - (if (not (> (save-excursion (forward-line 1) (point)) - calculate-lisp-indent-last-sexp)) - (progn (goto-char calculate-lisp-indent-last-sexp) - (beginning-of-line) - (parse-partial-sexp (point) - calculate-lisp-indent-last-sexp 0 t))) - ;; Indent under the list or under the first sexp on the same - ;; line as calculate-lisp-indent-last-sexp. Note that first - ;; thing on that line has to be complete sexp since we are - ;; inside the innermost containing sexp. - (backward-prefix-chars) - (current-column)) - ((and (save-excursion - (goto-char indent-point) - (skip-syntax-forward " ") - (not (looking-at ":"))) - (save-excursion - (goto-char orig-point) - (looking-at ":"))) - (save-excursion - (goto-char (+ 2 (elt state 1))) - (current-column))) - (t - (let ((function (buffer-substring (point) - (progn (forward-sexp 1) (point)))) - method) - (setq method (or (function-get (intern-soft function) - 'lisp-indent-function) - (get (intern-soft function) 'lisp-indent-hook))) - (cond ((or (eq method 'defun) - (and (null method) - (> (length function) 3) - (string-match "\\`def" function))) - (lisp-indent-defform state indent-point)) - ((integerp method) - (lisp-indent-specform method state - indent-point normal-indent)) - (method - (funcall method indent-point state)))))))) - (setq-default lisp-indent-function #'+oreo/lisp-indent-function)) + (+abbrev/define-abbrevs + global-abbrev-table + ("sdate" + (format-time-string "%Y-%m-%d" (current-time))) + ("stime" + (format-time-string "%H:%M:%S" (current-time))) + ("sday" + (format-time-string "%A" (current-time))) + ("smon" + (format-time-string "%B" (current-time))))) +#+end_src +** Yasnippet +Look at the snippets [[file:../.config/yasnippet/snippets/][folder]] +for all snippets I've got. + +#+begin_src emacs-lisp +(use-package yasnippet + :straight t + :defer t + :hook + (prog-mode-hook . yas-minor-mode) + (text-mode-hook . yas-minor-mode) + :general + (insert-leader + "i" #'yas-insert-snippet) + :config + (yas-load-directory (no-littering-expand-etc-file-name + "yasnippet/snippets"))) +#+end_src +** Amx +Amx is a fork of Smex that works to enhance the +~execute-extended-command~ interface. It provides a lot of niceties +such as presenting the key bind when looking for a command. + +#+begin_src emacs-lisp +(use-package amx + :straight t + :demand t + :init + (setq amx-backend 'auto) + :config + (amx-mode)) #+end_src -- cgit v1.2.3-13-gbd6f