#+title: Applications for Emacs #+author: Aryadev Chavali #+description: Applications for my Emacs OS™ #+date: 2023-09-29 #+property: header-args:emacs-lisp :tangle app.el :comments link :results none #+options: toc:nil #+startup: noindent Applications are greater than packages; they provide a set of functionality to create an interface in Emacs. Emacs comes with applications and others may be installed. * WAIT Dashboard :PROPERTIES: :header-args:emacs-lisp: :tangle no :END: Dashboard creates a custom dashboard for Emacs that replaces the initial startup screen in default Emacs. It has a lot of customising options. Unfortunately not that useful, many things are easier to invoke directly such as recent files or project changing. #+begin_src emacs-lisp (use-package dashboard :straight t :demand t :general (app-leader "b" #'dashboard-refresh-buffer) (:states '(normal motion emacs) :keymaps 'dashboard-mode-map "q" (proc (interactive) (kill-this-buffer))) (nmmap :keymaps 'dashboard-mode-map "r" #'dashboard-jump-to-recent-files "p" #'dashboard-jump-to-projects "}" #'dashboard-next-section "{" #'dashboard-previous-section) :init (setq initial-buffer-choice nil dashboard-banner-logo-title "Oreomacs" dashboard-center-content t dashboard-set-init-info t dashboard-startup-banner (no-littering-expand-etc-file-name "dashboard/logo.png") dashboard-set-footer t dashboard-set-navigator t dashboard-items '((projects . 5) (recents . 5)) dashboard-footer-messages (list "Collecting parentheses..." "Linking 'coffee_machine.o'..." "Uploading ip to hacker named 4chan..." "Dividing by zero..." "Solving 3-sat..." "Obtaining your health record..." (format "Recompiling Emacs for the %dth time..." (random 1000)) "Escaping the cycle of samsara...")) :config (dashboard-setup-startup-hook)) #+end_src * EWW Emacs Web Wowser is the inbuilt text based web browser for Emacs. It can render images and basic CSS styles but doesn't have a JavaScript engine, which makes sense as it's primarily a text interface. #+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" #'shr-probe-and-copy-url) :straight nil :config (with-eval-after-load "evil-collection" (evil-collection-eww-setup))) #+end_src * Calendar Calendar is a simple inbuilt application that helps with date functionalities. I add functionality to copy dates from the calendar to the kill ring and bind it to "Y". #+begin_src emacs-lisp (use-package calendar :straight nil :defer t :commands (+calendar/copy-date +calendar/toggle-calendar) :display ("\\*Calendar\\*" (display-buffer-at-bottom) (inhibit-duplicate-buffer . t) (window-height . 0.17)) :general (nmmap :keymaps 'calendar-mode-map "Y" #'+calendar/copy-date) (app-leader "d" #'calendar) :config (defun +calendar/copy-date () "Copy date under cursor into kill ring." (interactive) (if (use-region-p) (call-interactively #'kill-ring-save) (let ((date (calendar-cursor-to-date))) (when date (setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))) (kill-new (format-time-string "%Y-%m-%d" date))))))) #+end_src * Mail Mail is a funny thing; most people use it just for business or advertising and it's come out of use in terms of personal communication in the west for the most part (largely due to "social" media applications). However, this isn't true for the open source and free software movement who heavily use mail for communication. Integrating mail into Emacs helps as I can send source code and integrate it into my workflow just a bit better. ** Notmuch #+begin_src emacs-lisp (use-package notmuch :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/signature "---------------\nAryadev Chavali") (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") mail-signature +mail/signature mail-default-directory +mail/local-dir mail-source-directory +mail/local-dir message-signature +mail/signature message-auto-save-directory +mail/local-dir message-directory +mail/local-dir) (defun +mail/sync-mail () "Sync mail via mbsync." (interactive) (start-process-shell-command "" nil "mbsync -a")) (defun +mail/trash-junk () "Delete any mail in junk" (interactive) (start-process-shell-command "" nil "notmuch search --output=files --format=text0 tag:deleted tag:spam tag:trash tag:junk | xargs -r0 rm")) :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))) (advice-add #'notmuch-poll-and-refresh-this-buffer :before #'+mail/sync-mail) (advice-add #'notmuch-poll-and-refresh-this-buffer :after #'+mail/trash-junk) (with-eval-after-load "evil-collection" (evil-collection-notmuch-setup))) #+end_src ** Smtpmail #+begin_src emacs-lisp (use-package smtpmail :straight nil :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 * Dired Setup for dired. Make dired-hide-details-mode the default mode when using dired-mode, as it removes the clutter. Setup evil collection for dired (even though dired doesn't really conflict with evil, there are some corners I'd like to adjust). #+begin_src emacs-lisp (use-package dired :straight nil :commands (dired find-dired) :hook (dired-mode-hook . auto-revert-mode) (dired-mode-hook . dired-hide-details-mode) :init (setq-default dired-listing-switches "-AFBlu --group-directories-first" dired-omit-files "^\\." dired-dwim-target t) (with-eval-after-load "evil-collection" (evil-collection-dired-setup)) :general (nmmap :keymaps 'dired-mode-map "T" #'dired-create-empty-file "H" #'dired-up-directory "L" #'dired-find-file) (dir-leader "f" #'find-dired "d" #'dired "D" #'dired-other-frame "i" #'image-dired "p" `(,(proc (interactive) (dired "~/Text/PDFs/")) :which-key "Open PDFs")) :config (add-to-list 'dired-guess-shell-alist-user '("\\.pdf\\'" "zathura")) (defun +dired/insert-all-subdirectories () "Insert all subdirectories currently viewable." (interactive) (dired-mark-directories nil) (dolist #'dired-insert-subdir (dired-get-marked-files)) (dired-unmark-all-marks)) (nmmap :keymaps 'dired-mode-map "SPC" nil "SPC ," nil) (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) (local-leader :keymaps 'dired-mode-map "l" #'dired-maybe-insert-subdir "m" #'dired-mark-files-regexp "u" #'dired-undo)) #+end_src ** fd-dired Uses fd for finding file results in a directory: ~find-dired~ -> ~fd-dired~. #+begin_src emacs-lisp (use-package fd-dired :after dired :straight t :general (dir-leader "g" #'fd-dired)) #+end_src ** wdired Similar to [[file:config.org::*(Rip)grep][wgrep]] =wdired= provides the ability to use Emacs motions and editing on file names. This makes stuff like mass renaming and other file management tasks way easier than even using the mark based system. #+begin_src emacs-lisp (use-package wdired :after dired :straight t :general (nmmap :keymaps 'dired-mode-map "." #'browse-url-of-dired-file "W" #'wdired-change-to-wdired-mode)) #+end_src * WAIT Xwidget :PROPERTIES: :header-args:emacs-lisp: :tangle no :END: Xwidget is a package which allows for the insertion of arbitrary xwidgets into Emacs through buffers. It must be compiled into Emacs so you might need to customise your install. One of its premier uses is in navigating the web which it provides through the function ~xwidget-webkit-browse-url~. This renders a fully functional web browser within Emacs. Though I am not to keen on using Emacs to browse the web /via/ xwidget (EWW does a good job on its own), I am very interested in its capability to render pages with JavaScript, as it may come of use when doing web development. I can see the results of work very quickly without switching windows all within Emacs. 2023-10-20: Disabled as it didn't seem to work, and honestly wasn't that useful. ** Xwidget Core #+begin_src emacs-lisp (use-package xwidget :straight nil :general (app-leader "u" #'xwidget-webkit-browse-url) (nmmap :keymaps 'xwidget-webkit-mode-map "q" #'quit-window "h" #'xwidget-webkit-scroll-backward "j" #'xwidget-webkit-scroll-up "k" #'xwidget-webkit-scroll-down "l" #'xwidget-webkit-scroll-forward "+" #'xwidget-webkit-zoom-in "-" #'xwidget-webkit-zoom-out (kbd "C-f") #'xwidget-webkit-scroll-up (kbd "C-b") #'xwidget-webkit-scroll-down "H" #'xwidget-webkit-back "L" #'xwidget-webkit-forward "gu" #'xwidget-webkit-browse-url "gr" #'xwidget-webkit-reload "gg" #'xwidget-webkit-scroll-top "G" #'xwidget-webkit-scroll-bottom)) #+end_src ** Xwidget Extensions Define a function ~+xwidget/render-file~ that reads a file name and presents it in an xwidget. If the current file is an HTML file, ask if user wants to open current file. Bind it to ~aU~ in the leader. Also define a function ~+xwidget/search-query~ that first asks the user what search engine they want to use ([[https://duckduckgo.com][Duck Duck Go]] and [[https://devdocs.io][DevDocs]] currently) then asks for a query, which it parses then presents in an xwidget window. Bind to ~as~ in the leader. #+begin_src emacs-lisp (use-package xwidget :straight nil :commands (+xwidget/render-file +xwidget/search) :general (app-leader "U" #'+xwidget/render-file "s" #'+xwidget/search) :config (setenv "WEBKIT_FORCE_SANDBOX" "0") (defun +xwidget/render-file (&optional FORCE) "Find file (or use current file) and render in xwidget." (interactive) (cond ((and (not FORCE) (or (string= (replace-regexp-in-string ".*.html" "html" (buffer-name)) "html") (eq major-mode 'web-mode) (eq major-mode 'html-mode))) ; If in html file (if (y-or-n-p "Open current file?: ") ; Maybe they want to open a separate file (xwidget-webkit-browse-url (format "file://%s" (buffer-file-name))) (+xwidget/render-file t))) ; recurse and open file via prompt (t (xwidget-webkit-browse-url (format "file://%s" (read-file-name "Enter file to open: ")))))) (defun +xwidget/search () "Run a search query on some search engine and display in xwidget." (interactive) (let* ((engine (completing-read "Engine: " '("duckduckgo.com" "devdocs.io") nil t)) (query-raw (read-string "Enter query: ")) (query (cond ((string= engine "duckduckgo.com") query-raw) ((string= engine "devdocs.io") (concat "_ " query-raw))))) (xwidget-webkit-browse-url (concat "https://" engine "/?q=" query))))) #+end_src * Eshell ** Why Eshell? Eshell is an integrated shell environment for Emacs, written in Emacs Lisp. I argue that it is the best shell/command interpreter to use in Emacs. Eshell is unlike the alternatives in Emacs as it's a /shell/ first, not a terminal emulator. It has the ability to spoof some aspects of a terminal emulator (through the shell parser), but it is NOT a terminal emulator. The killer benefits of eshell (which would appeal to Emacs users) are a direct result of eshell being written in Emacs lisp: - incredible 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 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 result of this powerful feature. These evaluators are described below. Lisp evaluator: works on braced expressions, evaluating them as Lisp expressions (e.g. ~(message "Hello, World!\n")~). Any returned objects are printed. This makes eshell a LISP REPL! External evaluator: works within curly braces, evaluating them via some external shell process (like sh) (e.g. ~{echo "Hello, world!\n"}~). This makes eshell a (kinda dumb) terminal emulator! The alias evaluator is the top level evaluator. It is the main evaluator for each expression given to eshell. When given an expression it tries to evaluate it by testing against these conditions: - it's an alias defined by the user or in the ~eshell/~ namespace of functions (simplest evaluator) - it's some form of lisp expression (lisp evaluator) - it's an external command (bash evaluator) Essentially, you get the best of both Emacs and external shell programs *ALL WITHIN* Emacs for free. ** Eshell functionality Bind some evil-like movements for easy shell usage, and a toggle function to pull up the eshell quickly. #+begin_src emacs-lisp (use-package eshell :defer t :general (shell-leader "t" #'eshell) :init (add-hook 'eshell-mode-hook (proc (interactive) (general-def :states '(normal insert) :keymaps 'eshell-mode-map "M-j" #'eshell-next-matching-input-from-input "M-k" #'eshell-previous-matching-input-from-input) (local-leader :keymaps 'eshell-mode-map "c" (proc (interactive) (eshell/clear) (recenter)) "k" #'eshell-kill-process)))) #+end_src ** Eshell pretty symbols and display Pretty symbols and a display record. #+begin_src emacs-lisp (use-package eshell :defer t :pretty (eshell-mode-hook ("lambda" . "λ") ("numberp" . "ℤ") ("t" . "⊨") ("nil" . "Ø")) :display ("\\*e?shell\\*" ; for general shells as well (display-buffer-at-bottom) (window-height . 0.30))) #+end_src ** Eshell variables and aliases Set some sane defaults, a banner and a prompt. The prompt checks for a git repo in the current directory and provides some extra information in that case (in particular, branch name and if there any changes that haven't been committed). #+begin_src emacs-lisp (use-package eshell :defer t :config (defun +eshell/--git-get-remote-status () (let* ((branch-status (split-string (shell-command-to-string "git status | grep 'Your branch is'"))) (status (nth 3 branch-status)) (diff (cl-position "by" branch-status :test #'string=))) (if (null diff) (propertize "=" 'font-lock-face '(:foreground "green")) (let ((n (nth (+ 1 diff) branch-status))) (concat (cond ((string= status "ahead") (propertize "→ " 'font-lock-face '(:foreground "dodger blue"))) ((string= status "behind") (propertize "← " 'font-lock-face '(:foreground "orange red")))) n))))) (defun +eshell/--git-get-change-status () (let ((changed-files (- (length (split-string (shell-command-to-string "git status -s" ) "\n")) 1))) (if (= changed-files 0) (propertize "✓" 'font-lock-face '(:foreground "green")) (propertize (number-to-string changed-files) 'font-lock-face '(:foreground "red"))))) (defun +eshell/get-git-properties () (let ((git-branch (shell-command-to-string "git branch"))) (if (or (string= git-branch "") (not (string= "*" (substring git-branch 0 1)))) "" (format "(%s<%s>[%s])" (nth 2 (split-string git-branch "\n\\|\\*\\| ")) (+eshell/--git-get-change-status) (+eshell/--git-get-remote-status))))) (defun +eshell/prompt-function () (let ((git (+eshell/get-git-properties))) (mapconcat (lambda (item) (if (listp item) (propertize (car item) 'read-only t 'font-lock-face (cdr item) 'front-sticky '(font-lock-face read-only) 'rear-nonsticky '(font-lock-face read-only)) item)) (list '("[") `(,(abbreviate-file-name (eshell/pwd)) :foreground "LimeGreen") '("]") (if (string= git "") "" (concat "-" git "")) "\n" `(,(format-time-string "[%H:%M:%S]") :foreground "purple") "\n" '("𝜆> " :foreground "DeepSkyBlue"))))) (defun +eshell/banner-message () (concat (shell-command-to-string (let ((possible-cows '("default" "cower" "moofasa" "moose" "mutilated" "satanic" "sheep" "small" "tux" "vader"))) (format "fortune | cowsay -f %s" (nth (random (length possible-cows)) possible-cows)))) "\n")) (setq eshell-cmpl-ignore-case t eshell-cd-on-directory t eshell-banner-message '(+eshell/banner-message) eshell-highlight-prompt nil eshell-prompt-function #'+eshell/prompt-function eshell-prompt-regexp "^𝜆> ")) #+end_src ** Eshell change directory quickly Add ~eshell/goto~, which is actually a command accessible from within eshell (this is because ~eshell/*~ creates an accessible function within eshell with name ~*~). ~eshell/goto~ makes it easier to change directories by using Emacs' find-file interface (which is much faster than ~cd ..; ls -l~). ~eshell/goto~ is a better ~cd~ for eshell. However it is really just a plaster over a bigger issue for my workflow; many times I want eshell to be present in the current directory of the buffer I am using. So here's also a command for opening eshell with the current directory. #+begin_src emacs-lisp (use-package eshell :defer t :general (shell-leader "T" #'+eshell/current-buffer) :config (defun eshell/goto (&rest args) "Use `read-directory-name' to change directories." (eshell/cd (list (read-directory-name "Directory?: ")))) (with-eval-after-load "projectile" (defun eshell/goto-project-root (&rest args) "Change to directory `projectile-project-root'" (if (projectile-project-root) (eshell/cd (list (projectile-project-root))) (eshell/echo "Projectile not active here...")))) (defun +eshell/current-buffer () (interactive) (let ((dir (if buffer-file-name (file-name-directory buffer-file-name) default-directory)) (buf (eshell))) (if dir (with-current-buffer buf (eshell/cd dir) (eshell-send-input)) (message "Could not switch eshell: buffer is not real file"))))) #+end_src * Elfeed Elfeed is the perfect RSS feed reader, integrated into Emacs perfectly. I've got a set of feeds that I use for a large variety of stuff, mostly media and entertainment. I've also bound " ar" to elfeed for loading the system. #+begin_src emacs-lisp (use-package elfeed :general (app-leader "r" #'elfeed) (nmmap :keymaps 'elfeed-search-mode-map "gr" #'elfeed-update "s" #'elfeed-search-live-filter "" #'elfeed-search-show-entry) :init (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) (setq +rss/feed-urls '(("Arch Linux" "https://www.archlinux.org/feeds/news/" News Technology) ("The Onion" "https://www.theonion.com/rss" Social) ("Protesilaos Stavrou" "https://www.youtube.com/@protesilaos" YouTube Technology) ("Tsoding Daily" "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" YouTube Technology) ("Tsoding" "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg" YouTube Technology) ("Nexpo" "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw" YouTube Stories) ("3B1B" "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw" YouTube) ("Fredrik Knusden" "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w" YouTube Stories) ("Barely Sociable" "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw" YouTube Stories) ("Atrocity Guide" "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw" YouTube Stories) ("Hacker News" "https://news.ycombinator.com/rss" Social News Technology) ("Hacker Factor" "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2" Social))) :config (with-eval-after-load "evil-collection" (evil-collection-elfeed-setup)) (setq elfeed-feeds (cl-map 'list #'(lambda (item) (append (list (nth 1 item)) (cdr (cdr item)))) +rss/feed-urls)) (advice-add 'elfeed-search-show-entry :after #'+elfeed/dispatch-entry) (defun +elfeed/dispatch-entry (entry) "Process each type of entry differently. e.g., you may want to open HN entries in eww." (let ((url (elfeed-entry-link entry))) (pcase url ((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch")) (mpv-play-url url)) (_ (eww url)))))) #+end_src * Magit Magit is *the* git porcelain for Emacs, which perfectly encapsulates the git cli. In this case I just need to setup the bindings for it. As magit will definitely load after evil (as it must be run by a binding, and evil will load after init), I can use evil-collection freely. Also, define an auto insert for commit messages so that I don't need to write everything myself. #+begin_src emacs-lisp (use-package magit :defer t :display ("magit:.*" (display-buffer-same-window) (inhibit-duplicate-buffer . t)) ("magit-diff:.*" (display-buffer-below-selected)) ("magit-log:.*" (display-buffer-same-window)) :general (leader "g" '(magit-dispatch :which-key "Magit")) (code-leader "b" #'magit-blame) :auto-insert (("COMMIT_EDITMSG" . "Commit skeleton") "" "(" (read-string "Enter feature/module: ") ")" (read-string "Enter simple description: ") "\n\n") :init (setq vc-follow-symlinks t magit-blame-echo-style 'lines magit-copy-revision-abbreviated t) :config (with-eval-after-load "evil" (evil-set-initial-state 'magit-status-mode 'motion)) (with-eval-after-load "evil-collection" (evil-collection-magit-setup))) #+end_src * IBuffer IBuffer is the dired of buffers: providing the ability to mark buffers, mass rename/delete and just observe stuff. #+begin_src emacs-lisp (use-package ibuffer :general (buffer-leader "i" #'ibuffer) :config (with-eval-after-load "evil-collection" (evil-collection-ibuffer-setup))) #+end_src * Proced Emacs has two systems for process management: + proced: a general 'top' like interface which allows general management of linux processes + list-processes: a specific Emacs based system that lists processes spawned by Emacs (similar to a top for Emacs specifically) Core proced config, just a few bindings and evil collection setup. #+begin_src emacs-lisp (use-package proced :straight nil :general (app-leader "p" #'proced) (nmap :keymaps 'proced-mode-map "za" #'proced-toggle-auto-update) :display ("\\*Proced\\*" (display-buffer-at-bottom) (window-height . 0.25)) :init (setq proced-auto-update-interval 0.5) :config (with-eval-after-load "evil-collection" (evil-collection-proced-setup))) #+end_src ** Proced narrow Along with that I setup the package ~proced-narrow~ which allows further filtering of the process list. #+begin_src emacs-lisp (use-package proced-narrow :straight t :after proced :general (nmap :keymaps 'proced-mode-map "%" #'proced-narrow)) #+end_src * Calculator Surprise, surprise Emacs comes with a calculator. Greater surprise, this thing is over powered. It can perform the following (and more): - Matrix calculations - Generalised calculus operations - Equation solvers for n-degree multi-variable polynomials - Embedded mode (check below)! ~calc-mode~ is a calculator system within Emacs that provides a diverse array of mathematical operations. It uses reverse polish notation to do calculations (though there is a standard infix algebraic notation mode). Embedded mode allows computation with the current buffer as the echo area. This basically means I can compute stuff within a buffer without invoking calc directly: $1 + 2\rightarrow_{\text{calc-embed}} 3$. #+begin_src emacs-lisp (use-package calc :straight nil :display ("*Calculator*" (display-buffer-at-bottom) (window-height . 0.18)) :general (app-leader "c" #'calc-dispatch) (mode-leader "c" #'calc-embedded) :init (setq calc-algebraic-mode t) :config (with-eval-after-load "evil-collection" (evil-collection-calc-setup))) #+end_src ** WAIT Calctex :PROPERTIES: :header-args:emacs-lisp: :tangle no :END: ~calc-mode~ also has a 3rd party package called ~calctex~. It renders mathematical expressions within calc as if they were rendered in TeX. You can also copy the expressions in their TeX forms, which is pretty useful when writing a paper. I've set a very specific lock on this repository as it's got quite a messy work-tree and this commit seems to work for me given the various TeX utilities installed via Arch. #+begin_src emacs-lisp (use-package calctex :after calc :straight (calctex :type git :host github :repo "johnbcoughlin/calctex") :hook (calc-mode-hook . calctex-mode)) #+end_src * WAIT Ledger :PROPERTIES: :header-args:emacs-lisp: :tangle no :END: #+begin_src emacs-lisp (use-package ledger-mode :defer t) (use-package evil-ledger :after ledger-mode) #+end_src * Zone Of course Emacs has a cool screensaver software. #+begin_src emacs-lisp (use-package zone-matrix :straight t :commands (zone) :general (leader "z" #'zone) :init (setq zone-programs [zone-pgm-drip zone-pgm-drip-fretfully zone-pgm-martini-swan-dive zone-pgm-stress zone-pgm-random-life])) #+end_src * (Wo)man Man pages are the user manuals for most software on Linux. Really useful when writing code for Un*x systems, though they can be very verbose. 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! #+begin_src emacs-lisp (use-package man :demand t :straight nil :init (setq Man-notify-method 'pushy) :display ("^\\*Man.*" (display-buffer-reuse-mode-window display-buffer-same-window)) :general (file-leader "m" #'man) ;; kinda like "find man page" (nmmap :keymaps 'Man-mode-map "RET" #'man-follow)) #+end_src * WAIT gif-screencast :PROPERTIES: :header-args:emacs-lisp: :tangle no :END: Little application that uses =gifsicle= to make essentially videos of Emacs. Useful for demonstrating features. #+begin_src emacs-lisp (use-package gif-screencast :straight t :general (app-leader "x" #'gif-screencast-start-or-stop) :init (setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/"))) #+end_src * Image-mode Image mode, for viewing images. Supports tons of formats, easy to use and integrates slickly into image-dired. Of course, #+begin_src emacs-lisp (use-package image-mode :straight nil :general (nmmap :keymaps 'image-mode-map "+" #'image-increase-size "-" #'image-decrease-size "p" #'image-animate "P" #'image-animate-set-speed "h" #'image-backward-hscroll "j" #'image-next-line "k" #'image-previous-line "l" #'image-forward-hscroll)) #+end_src * ERC #+begin_src emacs-lisp (use-package erc :defer t :init (setq erc-server "irc.libera.chat" erc-nick "oreodave" erc-buffer-display "current")) #+end_src * MPV Basically a porcelain over mpv via the IPC interface. #+begin_src emacs-lisp (use-package mpv :defer t :straight t :config (with-eval-after-load "org" (defun org-mpv-complete-link (&optional arg) (replace-regexp-in-string "file:" "mpv:" (org-link-complete-file arg) t t)) (org-link-set-parameters "mpv" :follow #'mpv-play :complete #'org-mpv-complete-link))) #+end_src