Finn Sauer About Contact Dotemacs Blog Feeds License

Dotemacs

The last Git commit is shown below
3ac2ef0 Denote: notetaking made simple
For the short period of testing denote I was amazed.  It it simple, yet
powerful to have a full directory of files.  Its magic lies behind a
simple file-naming scheme:
  ISO_DATE--FILENAME-SEPERATED-WITH-HYPTHEN__TAGS_AND_TAGS.EXT

This makes it possible to querry all the files outside of Emacs with
simple Unix tools such as grep.  I want to querry all the files
containing a tag called Emacs:
  find $DENOTE_DIR -type f -regex '.*_emacs.*'
will print me all the files containing said tag.


DISCLAIMER: [2022-02-20 Sun]+[2023-10-18 Wed]:

This entire configuration file will be completely overhauled and a lot might be removed, rearranged, rewritten, et cetera. Currently I'm unhappy with this configuration; and– possibly– could remove everything. A lack of commits reflect this statement…

I probably will still use Org Mode, but some things might change.

Finn Sauer (https://finnsauer.com/)


This document is my own personal configuration for the extensible, customizable, self-documenting real-time display editor called Emacs. This configuration file is written in the so called Literate Programming paradigm: the focus lies upon the text. The actual code is just an addition to the text and should merely underline the thought. This is made possible via Org Mode– the best Markdown out there.

Refer to the section This Document to get more information about this file and its conventions. This file is source controlled with Git, a link to the repository can be found here.

Table of Contents

1. Essentials

1.1. Auxiliary Functions, Macros, and Commands

There are a few minor hindrances to what I would call: essential functions. I, hereby, declare them here to be used throughout this document (configuration file).

(defun file->string (file)
  "Convert FILE to string."
  (when (and (file-readable-p file)
             (file-writable-p file))
    (with-temp-buffer
      (insert-file-contents-literally file)
      (buffer-substring-no-properties (point-min) (point-max)))))

(defun file->list (file)
  "Convert FILE to list of strings separated via new-line."
  (when (and (file-readable-p file)
             (file-writable-p file))
    (s-lines
     (with-temp-buffer
       (insert-file-contents-literally file)
       (buffer-substring-no-properties (point-min) (point-max))))))

(defun re-seq (regexp string)
  "Return list of all REGEXP match in STRING."
  (unless (or (= 0 (length regexp))
              (= 0 (length string)))
    (save-match-data
      (let ((pos 0)
            (match nil))
        (while (string-match regexp string pos)
          (push (match-string 0 string) match)
          (setq pos (match-end 0)))
        (reverse match)))))

(defun index/call-process (command)
  "Call a process of COMMAND.
COMMAND must be a string separated via white space or a list of
strings, which represents its arguments."
  (when (stringp command)
    (setq command (s-split "[ ]+" command)))
  (apply
   #'call-process
   `(,(car command) nil 0 nil ,@(cdr command))))

(defmacro test (&rest body)
  "Run a test of BODY.
Repeat it 10000 times and return the time it took in human
readable form."
  (declare (indent 0) (debug t))
  `(->> (benchmark-run 10000
          ,@body)
        car
        seconds-to-string))

1.2. Recompile this document

Whenever I quit Emacs, I want to load my new configurations from this file. Thus this function will recompile the preexisting Emacs-lisp file to a newer version every time Emacs closes.

(leaf emacs
  :config
  (defun index/rebuild-emacs-init ()
    "Compile the ORG configuration file to ELN/ELC/EL."
    (let* ((b (expand-file-name "config" user-emacs-directory))
           (o (s-append ".org" b))
           (s (s-append ".org.sha" b))
           (e (s-append ".el" b))
           (c (s-append ".elc" b))
           (n (s-append ".eln" b)))
      ;; Do not compile if `config.org' hasn't changed since the last
      ;; session.
      (unless (s-equals?
               (secure-hash 'sha256 (file->string o))
               (file->string s))
        (dolist (f (list c n e))
          (when (file-writable-p f)
            (delete-file f)))
        ;; Convert `config.org' to `config.el'.
        (org-babel-tangle-file o e)
        ;; Compile to `config.eln', when native-comp is available; else
        ;; compile to byte-code `config.elc'.
        (if (ignore-errors (native-comp-available-p))
            (native-compile e n)
          (byte-compile-file e))
        ;; If the `config.elc' file or `config.eln' file has not been
        ;; created, do not remove it.  This is so that we can source it
        ;; in `init.el' when the compilation didn't work.  This is
        ;; solely a safety measure.
        (when (or (file-writable-p c)
                  (file-writable-p n))
          (delete-file e))
        ;; Write current `config.org' SHA-256 to `config.org.sha'.
        (with-temp-file s
          (insert (secure-hash 'sha256 (file->string o)))))))
  :hook ((kill-emacs-hook . index/rebuild-emacs-init)))

1.3. Startup Files

1.3.1. The init.el file

The original file can be found here. The source is shown below:

;;; init.el --- initialisation file for `config.org'

;; Copyright (C) 2020-2021 Finn Sauer <info@finnsauer.com>
;;
;; This file is part of my `config.org' ecosystem.
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or (at
;; your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This is the initialisation file for GNU/Emacs.  At the end of this
;; file, it will call the proper configuration file written in
;; `org-mode'.  Visit that document to see the proper / full
;; documentation, code-snippets, and the idea behind it.
;;
;; The sole purpose of this file is to initialise loading the proper
;; configuration file.  Everything else is out-of-scope.
;;
;; Although you will find some code that SHOULD belong in the
;; `config.org' file, but I have put it in here since it is important to
;; be set at the beginning.

;;; Code :

;(eval-and-compile
  ;; If `native-comp' is available use `gcc -O3' instead of `gcc -O2'.
  (when (ignore-errors (native-comp-available-p))
    (setq comp-speed 3))

  ;; This is for deferring the check for modifications mechanism to
  ;; `check-on-save' instead of `find-at-startup'-- which as the name
  ;; implies runs a command at startup.  The command is quite slow.
  (setq straight-check-for-modifications '(check-on-save))

  ;; Bootstrap code for `straight.el'.  This has been slightly modified.
  (let ((bootstrap-file (expand-file-name
                         "straight/repos/straight.el/bootstrap.el"
                         user-emacs-directory))
        (bootstrap-version 5))
    (unless (file-exists-p bootstrap-file)
      (with-current-buffer (url-retrieve-synchronously
                            (concat
                             "https://raw.githubusercontent.com/"
                             "raxod502/straight.el/develop/install.el")
                            'silent 'inhibit-cookies)
        (goto-char (point-max))
        (eval-print-last-sexp)))
    (load bootstrap-file nil 'nomessage))
  ;; Bootstrap ends here

  ;; Install various essential packages such as `leaf' and
  ;; `leaf-keywords'.  The `cl-lib' package is already required in
  ;; straight's `bootstrap.el'.  I require it here again for completion
  ;; sake.
  (dolist (p '(leaf leaf-keywords org cl-lib dash s))
    (straight-use-package p)
    (require p))

  ;; Uses a different file for `custom' in order to not clutter the
  ;; `init.el' file with automatic settings made by custom.  It resides
  ;; in a temporary file, that will be newly generated each time Emacs
  ;; is launched---thus disabling it.
  (setq custom-file (make-temp-file "emacs-custom-"))

  ;; Do not ask to follow symlinks that are inside a Git directory.
  ;; Reason being mainly loading the configuration file
  ;; `config.org'---since it is a symlink which resides in a Git
  ;; repository.
  (setq vc-follow-symlinks t)

  ;; This is the actual configuration file.  This will try to load four
  ;; files: `config.eln', `config.elc', `config.el', and `config.org'.
  ;; If none are found throw an error.  Both `config.elc' and
  ;; `config.el'-- and if `native-comp' is available: `config.eln'-- are
  ;; recompiled on exit with `index/rebuild-emacs-init'.
  (let* ((b (expand-file-name "config" user-emacs-directory))
         (o (s-append ".org" b))
         (e (s-append ".el" b))
         (c (s-append ".elc" b))
         (n (s-append ".eln" b)))
    (leaf-keywords-init)
    (cond
     ((file-readable-p n) (load-file n))           ; Load `config.eln'
     ((file-readable-p c) (load-file c))           ; Load `config.elc'
     ((file-readable-p e) (load-file e))           ; Load `config.el'
     ((file-readable-p o) (org-babel-load-file o)) ; Load `config.org'
     (:else (error "Found no init file in `user-emacs-directory'"))));)

;;; init.el ends here

1.3.2. The early-init.el file

The original file can be found here. The source is shown below:

;;; early-init.el --- init.el just earlier

;; Copyright (C) 2020-2021 Finn Sauer <info@finnsauer.com>
;;
;; This file is part of my `config.org' ecosystem.
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or (at
;; your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Code:

;; Stopping the Garbage Collection.  Number should be high enought to
;; never trigger Garbage Collection, but low enough to not cause
;; GNU/Linux to page.  Garbage collection is later enabled by
;; `gcmh-mode', in order to smartly Garbage Collect depending on the
;; idle time.
(setq gc-cons-threshold #x40000000)

;; Disable GUI Elements
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

;; Found on `https://github.com/miklos1/dotemacs/blob/master/early-init.el'.
;; Ignore X resources; its settings would be redundant with the other settings
;; in this file and can conflict with later config (particularly where the
;; cursor color is concerned).
(advice-add #'x-apply-session-resources :override #'ignore)

(setq frame-inhibit-implied-resize t)
(setq inhibit-splash-screen t)
(setq use-dialog-box t)
(setq use-file-dialog nil)

(setq inhibit-startup-echo-area-message user-login-name)
(setq inhibit-startup-screen t)
(setq inhibit-startup-buffer-menu t)

;; Inhibit `native-comp' compile warnings.
(setq native-comp-async-report-warnings-errors 'silent)

;;; early-init.el ends here

1.4. Enforce Keybindings

I have some keybindings that interfere with other keybindings. For example I have bound C-M-q to a custom function. But this usually is mapped by another mode. Therefore I have this minor-mode defined that overwrites those keybindings.

It is important that this minor-mode is first or nearly fist in the minor-mode-map-alist. Putting this near the end of your configuration file ought to be enough. But there is another variable which takes higher precedence over minor-mode-map-alist and that is emulation-mode-map-alists, thus we use that instead.

In order to now force a keybinding you have to add the keybindings to the index/keys-minor-mode-map. This can be achieved easily with leaf/use-package.

(leaf emacs
  :bind ((index/keys-minor-mode-map
          ("C-M-q" . some-function))))

Or if you don't use something like leaf/use-package then you could define the keybindings like this:

(define-key 'index/keys-minor-mode-map (kbd "C-M-q") #'some-function)

This method is neat, because when you want to disable the now enforced keybindings you can simply type M-x index/keys-minor-mode and get back the default keybindings.

(leaf emacs
  :init
  (defcustom index/keys-minor-mode-map (make-sparse-keymap)
    "Keymap of `index/keys-minor-mode'."
    :type 'map)

  (defcustom index/keys-minor-mode-blacklist
    '(minibuffer-inactive-mode magit-mode)
    "List of major modes to disable `index/keys-minor-mode' in."
    :type 'list)

  (define-minor-mode index/keys-minor-mode
    "A minor mode for overwriting keybindings."
    :init-value t
    :keymap index/keys-minor-mode-map
    :global t
    :lighter "")

  ;; https://github.com/jwiegley/use-package/blob/master/bind-key.el
  ;; The keymaps in `emulation-mode-map-alists' take precedence over
  ;; `minor-mode-map-alist'.
  (add-to-list 'emulation-mode-map-alists
               `((index/keys-minor-mode . ,index/keys-minor-mode-map)))

  ;; Disable `index/keys-minor-mode' in certain modes defined in
  ;; `index/keys-minor-mode-blacklist'.
  (dolist (hook (--map (intern (s-append "-hook" (symbol-name it)))
                       index/keys-minor-mode-blacklist))
    (add-hook hook (lambda () (setq-local index/keys-minor-mode nil))))

  :hook ((emacs-startup-hook . index/keys-minor-mode)))

The following has been DEPRECATED, as there is no need for it anymore with the emulation-mode-map-alists variable.

The following code snippet will ensure that when a new minor mode will add new keybindings, this function will then remove all keybindings associated with index/keys-minor-mode. After that it will re enable the keys. This hook will only get fired when index/keys-minor-mode is not at the top of the minor-mode-map-alist.

(leaf emacs
  :config
  (defun index/keys-have-priority (&rest _)
    "Ensure that `index/keys-minor-mode' keybindings retain
priority over other minor modes.  Called via the
`after-load-functions' hook."
    (unless (eq (caar minor-mode-map-alist) 'index/keys-minor-mode)
      (let ((keys (assq 'index/keys-minor-mode minor-mode-map-alist)))
        (assq-delete-all 'index/keys-minor-mode minor-mode-map-alist)
        (add-to-list 'minor-mode-map-alist keys))))

  :hook ((after-load-functions . index/keys-have-priority)))

2. REVIEW Garbage Collection

As of 2021-02-20 this section is under REVIEW: make it more descriptive.

2.1. Garbage Collector Magic Hack - GCMH

Once Emacs is fully loaded we want to activate the Garbage Collector Magic Hack. This will, during normal Emacs usage, be a higher value than usual, but not infinite as shown above. When idling it turns Emacs' Garbage Collection back on.

(leaf gcmh
  :straight t
  :hook ((after-init-hook . gcmh-mode)))

3. Server: Client calls upon the Daemon

I use emacs --daemon with emacsclient -nw. The reason is that emacs becomes very startup heavy, meaning it will take longer time to initialize emacs. With the emacs --daemon starting up a new window is very responsive. One other useful functionality is to add new buffers from the terminal with emacsclient -n <file>.

(leaf server
  :hook ((after-init-hook . server-start)))

If I close Emacs and open it up later, I want it to be exactly in the state I left it in. The reason is, when you leave your workspace, or test something out, or completely shutdown your system, you won't be left in the blue. You can immediately work on what you left.

The “Desktop” package will keep my registers and buffers available.

(leaf desktop
  :config
  (setq desktop-auto-save-timeout 300)
  (setq desktop-dirname user-emacs-directory)
  (setq desktop-base-file-name "desktop")
  (setq desktop-files-not-to-save nil)
  ;; (setq desktop-globals-to-clear nil)
  (setq desktop-load-locked-desktop t)
  (setq desktop-missing-file-warning nil)
  (setq desktop-restore-eager 0)
  (setq desktop-restore-frames nil)
  (setq desktop-save 'ask-if-new)
  ;; (add-to-list 'desktop-globals-to-save 'register-alist)
  ;; TODO: grays out modus theme background...
  ;; (desktop-save-mode 1)
  )

Make sure to always go to the scratch buffer upon spawning a new Emacs frame.

(leaf emacs
  :config
  (setq initial-buffer-choice (lambda () (get-buffer "*scratch*"))))

4. Prettify Emacs

We all been there– opening Emacs for the first time and then were hit with Emacs' raw untouched gaze.

I've come across various blog posts / threads saying that Emacs looks like a old peace of software– which is true, but these people still haven't seen Emacs' true aesthetics: Emacs is a blank canvas.

Emacs can and should be whatever you want it to be: you are the artist staring at your blank canvas and you should be the one drawing.

If you dislike Emacs, because of its looks, then it is not Emacs fault, but yours. Thus you have to start drawing!

You dislike the tool-bar and scroll-bar, but you quite fond of the menu-bar, then so be it. Nobody is going to criticize you for liking it that way– only the people I addressed earlier would.

4.1. TODO Pulsar

(leaf pulsar
  :straight (pulsar :host gitlab :repo "protesilaos/pulsar")
  ;; :init (pulsar-setup)
  :config (setq pulsar-face 'pulsar-magenta)
  :bind ((index/keys-minor-mode-map
          ("H-<return>" . pulsar-pulse-line))))

4.2. Modeline

Doom Emacs gained a lot of popularity. Even though I encourage people to craft their own configuration, it is nice for the lazy one. But with a lot of people working on it and great work by Hlsinger, there has been an influx of new packages. One of them is called: Doom Modeline.

This adds a quite nice looking modeline– you would say “modern” modeline, but the word modern kinda lost its meaning as everything is modern these days.

Even though I am not opposed of the default modeline, this one looks better in my opinion.

(leaf doom-modeline
  :straight t
  :hook ((after-init-hook . doom-modeline-mode))
  :config
  (setq doom-modeline-height nil)
  (setq doom-modeline-icon nil)
  (setq doom-modeline-minor-modes nil)
  (setq doom-modeline-irc nil)
  (setq doom-modeline-lsp nil)
  (setq doom-modeline-buffer-eppncoding nil)
  (setq doom-modeline-buffer-encoding nil)
  (setq doom-modeline-enable-word-count t))

4.2.1. Line and Column Number

(line-number-mode 1)
(column-number-mode 1)

4.3. Theme

  ;; (leaf modus-themes
  ;;   :straight t
  ;;   :config
  ;;   (setq modus-themes-org-blocks t)
  ;;   ;; (setq modus-themes-operandi-color-overrides
  ;;   ;;       '((bg-main . "#efefef")
  ;;   ;;         (bg-hl-line . "#fefefe")
  ;;   ;;         (fg-main . "#000000")
  ;;   ;;         (bg-dim . "#f8f8f8")
  ;;   ;;         (fg-dim . "#282828")
  ;;   ;;         (bg-alt . "#f0f0f0")
  ;;   ;;         (fg-alt . "#505050")
  ;;   ;;         (bg-active . "#d7d7d7")
  ;;   ;;         (fg-active . "#0a0a0a")
  ;;   ;;         (bg-inactive . "#efefef")
  ;;   ;;         (fg-inactive . "#404148")))
  ;;   (modus-themes-load-themes)
  ;;   (modus-themes-load-operandi)
  ;;
  ;;   ;; Light theme at sunrise
  ;;   ;;(run-at-time (nth 1 (split-string (sunrise-sunset)))
  ;;   ;;             (* 60 60 24)
  ;;   ;;             (lambda () (enable-theme 'modus-operandi)))
  ;;
  ;;   ;; Dark theme at sunset
  ;;   ;;(run-at-time (nth 4 (split-string (sunrise-sunset)))
  ;;   ;;             (* 60 60 24)
  ;;   ;;             (lambda () (enable-theme 'modus-vivendi)))
  ;;   ;;:bind (("C-c c o" modus-themes-toggle))
  ;;   )

      ;; (leaf doom-themes :straight t)
      ;;
      ;; (load-theme 'doom-dracula)
      ;; (enable-theme 'doom-dracula)

      ;; (load-theme 'doom-nord)
      ;; (enable-theme 'doom-nord)

      ;; (load-theme 'doom-nord-light)
      ;; (enable-theme 'doom-nord-light)


(leaf dracula-theme
  :straight t
  :config
  (load-theme 'dracula :no-confirm))

4.3.1. Org Modifications

;; (leaf org
;;   :config
;;   (set-face-attribute 'org-level-1 nil
;;                       :height 1.2
;;                       :weight 'normal)
;;   (set-face-attribute 'org-level-2 nil
;;                       :height 1.0
;;                       :weight 'normal)
;;   (set-face-attribute 'org-level-3 nil
;;                       :height 1.0
;;                       :weight 'normal)
;;   (set-face-attribute 'org-level-4 nil
;;                       :height 1.0
;;                       :weight 'normal)
;;   (set-face-attribute 'org-level-5 nil
;;                       :weight 'normal)
;;   (set-face-attribute 'org-level-6 nil
;;                       :weight 'normal)
;;   (set-face-attribute 'org-document-title nil
;;                       :family "UnifrakturCook"
;;                       :height 2.5
;;                       :weight 'bold))

4.4. Font

(leaf emacs
  :init
  ;; The font can be found on the source hut git repository:
  ;; <https://git.sr.ht/~protesilaos/iosevka-comfy>.
  ;;
  ;; To install it, simply copy the desired font's ttf files into
  ;; ~/.local/share/fonts directory.
  (set-face-attribute 'default nil
                      :family "Iosevka Comfy"
                      :height 120
                      :weight 'normal)
  (set-face-attribute 'fixed-pitch nil
                      :family "Iosevka Comfy"
                      :height 120
                      :weight 'normal)
  (set-face-attribute 'variable-pitch nil
                      :family "Iosevka Comfy Duo"
                      ;; :family "Roboto"
                      :height 120
                      :weight 'normal))

4.5. Blinking Cursor

(leaf emacs
  :config
  (setq blink-cursor-interval 0.75)
  (setq blink-cursor-delay 1)
  (setq blink-cursor-blinks 0)
  :hook ((after-init-hook . blink-cursor-mode)))

4.6. Line Highlight

(global-hl-line-mode t)

4.7. Pretty symbols

(global-prettify-symbols-mode 1)

4.8. TODO Bell

Currently not working

I don't really like the bell. Since it, by default makes sounds. I find this more irretating than useful. Though the idea of the bell is, that you get notified when you try something that doesn't work. I support this idea, since when I define a kmacro I sometimes accidentally ring the bell and cancel the kmacro with it. In that situation it would be nice to see it… That's where a visual bell comes into place: mode-line-bell. This package will invert the background coloring of the modeline and blink for a predefined amount. This does not annoy you but tells you when the bell rang.

(leaf mode-line-bell
  :straight t
  :config
  (setq mode-line-bell-flash-time 0.1)
  (mode-line-bell-mode 1))

4.9. Prism

Prism is a package that highlights complex syntax trees differently to its parent or child. Every syntax tree has a different color.

Highlight Code Via Depth Level.

(leaf prism
  :straight t
  :config
  (setq prism-comments nil)
  (setq prism-num-faces 12)

  :hook ((emacs-lisp-mode-hook . prism-mode)
         (scheme-mode-hook . prism-mode)
         (clojure-mode-hook . prism-mode)
         (common-lisp-mode-hook . prism-mode)))

5. REVIEW Generic Settings

As of 2021-01-31 this section is under REVIEW: merge this section with the Miscellaneous section.

5.1. No backup files cluttering

(leaf emacs
  :config
  (setq backup-by-copying t)
  (setq backup-directory-alist '(("." . "~/.emacs.d/backup")))
  (setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))
  (setq delete-old-versions t)
  (setq kept-new-versions 5)
  (setq kept-old-versions 0)
  (setq version-control t)
  (setq create-lockfiles nil))

5.2. Yes or No

(defalias 'yes-or-no-p 'y-or-n-p)

5.3. Highlight Linked Parentheses

(show-paren-mode)

5.4. Tab Width

I use spaces for tabs. Some might disagree but that's fine. Some people prefer tabs and some prefer spaces, some even mix them together and that is called “smart tabs”. I simply enforce my indentation for others…

(leaf emacs
  :config
  (setq-default tab-always-indent 'complete)
  (setq-default tab-width 4)
  (setq-default indent-tabs-mode nil))

5.5. Undo Tree

Best way of saving your Ass; trust me, you'll need it!

(leaf undo-tree
  :straight t
  :config
  (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))
  (global-undo-tree-mode 1)
  :bind (("C-c u" . undo-tree-visualize)))

5.6. Smart Parens

(leaf smartparens
  :straight t
  :require smartparens-config
  :config (smartparens-global-mode 1))

5.7. Mouse Settings

The standard way how to scroll with the mouse is too fast for me, so I decreased it a fair amount.

(leaf emacs
  :config
  (setq scroll-conservatively 1)
  (setq mouse-wheel-scroll-amount '(5))
  (setq mouse-wheel-progressive-speed nil))

5.8. Goto Last Change

Yet again something that I stole from prot and thought it would be useful. It probably is, but I haven't really used it since then. In theory I could use the C-z binding for different things, but the position of C-z is horrible.

(leaf goto-last-change
  :straight t
  :config
  (defun index/goto-last-change ()
    "Modification of the `goto-last-change' function.
This will call `beacon-blink' after `goto-last-change'."
    (interactive)
    (goto-last-change)
    (beacon-blink))
  :bind (("C-z" . index/goto-last-change)))

5.9. Delete Trailing White Spaces

After each save the delete-trailing-whitespace function will– as the name implies– remove all trailing white spaces.

A trailing white space is a Tab or Space at the end of a line without any other character preceding it, making it useless. In a way they take up storage on the file system which– with this function– we can easily mitigate it. I know it's only a few bytes but still: a few bytes that need to be stored and send over the network.

Disclaimer: by that nature it will delete the trailing new lines \n at the end of the document too. It makes sense if you think about it, but can be rather annoying when you want to add a few new lines at the end for coding purposes. My best advice for escaping that: don't save the buffer when adding new lines at the end of the document.

(leaf emacs
  :hook ((before-save-hook . delete-trailing-whitespace)))

5.10. Enable Some Disabled Commands

For narrow-to-region, upcase-region, and downcase-region look at this section.

(leaf emacs
  :config
  (put 'narrow-to-region 'disabled nil)
  (put 'upcase-region 'disabled nil)
  (put 'downcase-region 'disabled nil)
  (put 'dired-find-alternate-file 'disabled nil)
  (put 'overwrite-mode 'disabled nil))

5.11. Save History

This will allow the storage of the minibuffer history. This functionality will be enhanced by any powerful completion framework.

(leaf savehist
  :config
  (setq savehist-file (expand-file-name "savehist" user-emacs-directory))
  (setq history-length 1000)
  (setq history-delete-duplicates t)
  (setq savehist-save-minibuffer-history t)
  :hook ((after-init-hook . savehist-mode)))

5.12. Save Cursor

It is nice to have your cursor be exactly where it was when you opened a file. This is good for picking up work you used to do.

(leaf saveplace
  :config
  (setq save-place-file (expand-file-name "savecursor" user-emacs-directory))
  (setq save-place-forget-unreadable-files t)
  (save-place-mode 1))

5.13. Overwrite Region

If the region is active and I start typing, the region will be overwritten– effectively deleting it.

(delete-selection-mode 1)

5.14. This file's accessibility

Quick and easy editing and reloading of my emacs config file.

To access this file with a keybinding; easy opening.

(leaf emacs
  :config
  (defun index/find-config-file ()
    "Goto `config.org' inside emacs' user directory."
    (interactive)
    (let ((vc-follow-symlinks t))
      (find-file
       (expand-file-name
        "config.org"
        user-emacs-directory))))
  :bind (("C-c e" . index/find-config-file)))

5.15. Edebug

C-u C-M-x Will evaluate the defun form and mark it as edebug. Once you call this function it will enter edebug-mode.

KeybindingDescription
SPCStep through sexpr
qQuit
gGo => Continue Evaluation as normal until breakpoint
hHere => Will jump to sexpr and debug that
bSet a Breakpoint on point
GSame as g but ignores Breakpoints
iJumps into the definition of a function and edebug it

To stop edebugging, just re-evaluate the lisp s-expression.

5.16. All the Icons

(leaf all-the-icons
  :straight t
  :config
  (unless (file-readable-p "~/.local/share/fonts/all-the-icons.ttf")
    (all-the-icons-install-fonts 'ignore-prompt)))

5.17. Preserve System Clipboard

This is for those nagging times when you copy something in the browser to be pasted into Emacs, but before that you kill something inside Emacs. This will preserve that.

(leaf emacs
  :config
  (setq save-interprogram-paste-before-kill t))

5.18. Visual (query) replace

The build-in query-replace is very powerful. But there was one feature missing, and that is to immediately see the changes in the buffer. This package provides this functionality.

(leaf visual-regexp
  :straight t
  :bind (("C-%" . vr/query-replace)
         ("C-M-%" . vr/query-replace)))

5.19. Suggest Elisp Function

Suggest is a very complicated and magical package. It provides you with an interactive interface accessible via M-x suggest. In there you can type in Inputs lets say "Hello World" and give it an output '("Hello" "World") and it will print out possible function that achieve that functionality.

(leaf suggest
  :straight t)

5.20. Elisp Virtual Text

One of the coolest features about CIDER: virtual text. This package will overwrite C-x C-e and C-M-x with its own versions. These will print the output in the minibuffer (default) and next to the evaluated s-expression.

It seems like a redundant feature, but I think it forces the user to stop the Lisp experience of trial and error to look at the minibuffer and then look back. This will negate it.

Table 1: The function name and description where automatically generated; see how.
KeybindingFunction NameDescription
C-x C-eeval-last-sexpEvaluate sexp before point; print value in the echo area.
C-M-xeval-defunThis function has :override advice: ‘edebug-eval-defun’.
(leaf eros
  :straight t
  :config
  (eros-mode 1))
Table 2: The function name and description where automatically generated; see how.
KeybindingFunction NameDescription
C-x C-eeros-eval-last-sexpWrapper for ‘eval-last-sexp’ that overlays results.
C-M-xeros-eval-defunWrapper for ‘eval-defun’ that overlays results.

5.21. DEPRECATED Vterm

As of 2021-02-23 this section is deprecated; there is no need for it if I can spawn URxvt, frankly the best terminal emulator out there.

I barely use this one as I use eshell– covered in this section– but when I need to go into a native terminal, be it that I need to run an ncurses program or similar tools.

What I would do is press <C-M-F2> what would bring me to a TTY. But the TTY is limited, as it only supports 8 colors. For those rare occasions I like to keep vterm around; just to be save.

(leaf vterm
  :straight t)

5.22. Obscure File Syntax Highlighting

For example highlighting in /etc/passwd and alike.

(require 'generic-x)

5.23. Tramp

Better have the backups local; you can never know.

(leaf tramp
  :commands eshell
  :config
  (setq tramp-backup-directory-alist backup-directory-alist))

5.24. Authinfo

Setting the auth-sources to a .gpg file is mandatory for obvious reasons. If you are ever asked to store a password, you can safely say yes.

(leaf auth-info
  :config
  (setq auth-sources '("~/.authinfo.gpg")))

5.25. Change Page Break ^L to Something Fancier

TODO: Make it toggle-able.

;; Make sure the variable `standard-display-table' is set.
(or standard-display-table
    (setq standard-display-table (make-display-table)))

(aset standard-display-table ?\f
      (vconcat (make-string 30 ?\s) "* * *"))

6. Writing

TODO : Structure these sub headers more nicely.

6.1. Org Mode - Your Life in Plain Text

6.1.1. Basic Configuration

(leaf org
  :straight t
  :require (ox org-macro)
  :config
  (provide 'org-compat)
  (setq org-directory (expand-file-name "~/wtn"))
  (setq org-special-ctrl-a/e nil)
  (setq org-special-ctrl-k nil)
  (setq org-confirm-babel-evaluate nil)
  (setq org-html-postamble nil)
  (setq org-latex-listings t)
  (setq org-structure-template-alist
        '(("a" . "export ascii")
          ("c" . "center")
          ("C" . "comment")
          ("e" . "example")
          ("E" . "src emacs-lisp")
          ("q" . "quote")
          ("s" . "src")
          ("v" . "verse")
          ("?" . "question")))
  (and
   (setf (nthcdr 4 org-emphasis-regexp-components) '(5))
   (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components))

  (setq org-babel-default-header-args
        (cons '(:noweb . "yes")
              (assq-delete-all :noweb org-babel-default-header-args)))
  ;; (setq org-babel-default-header-args
  ;;       (cons '(:exports . "both")
  ;;             (assq-delete-all :exports org-babel-default-header-args)))
  ;; (setq org-babel-default-header-args
  ;;       (cons '(:results . "output verbatim replace")
  ;;             (assq-delete-all :results org-babel-default-header-args)))

  (setq org-export-options-alist
        (cons '(:with-toc nil nil nil)
              (assq-delete-all :with-toc org-export-options-alist)))

  (setq org-export-headline-levels 8)

  ;; Do not modify HTML output!
  (setq org-html-htmlize-output-type nil) ; NO!
  (setq org-html-head-include-default-style nil)
  (setq org-html-head-include-scripts nil)

  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (shell . t)
     (C . t)
     (clojure . t)))

  :hook ((org-mode-hook . org-indent-mode)))

6.1.2. Theme

(leaf org
  :config
  (setq org-hide-emphasis-markers nil)
  (setq org-fontify-whole-heading-line t)
  (setq org-fontify-done-headline t)
  (setq org-fontify-quote-and-verse-blocks t))

6.1.3. Org links

(leaf ol
  :config
  (setq org-link-keep-stored-after-insertion t)
  :bind (("C-c l" . org-store-link)
         (org-mode-map
          ("C-c H-l" . org-toggle-link-display)
          ("C-c M-H-l" . org-insert-last-stored-link))))

6.1.4. SRC-Blocks

(leaf org-src
  :after org
  :config
  (setq org-src-window-setup 'current-window)
  (setq org-edit-src-persistent-message nil)
  (setq org-src-fontify-natively t)
  (setq org-src-preserve-indentation t)
  (setq org-src-tab-acts-natively t)
  (setq org-edit-src-content-indentation 0)
  :bind ((org-src-mode-map
          ("C-c C-c" . org-edit-src-exit))))

6.1.5. Custom Id

This will prevent the situation described at this blog posts. The entire code is stolen from there, too.

You can use, by default, C-c C-l for inserting a link. The command is very intuitive: once hitting the keybinding it will ask for a link. There you can insert an external link or choose from a list an already stored link. To store a link one must type M-x org-store-link. I of course have a keybinding for that. Now with this you can refer to different section.

(leaf org
  :config
  (setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
  (setq org-link-keep-stored-after-insertion t)

  (defun tributi/org-custom-id-get (&optional pom create prefix)
    "Get the CUSTOM_ID property of the entry at point-or-marker POM.
   If POM is nil, refer to the entry at point. If the entry does
   not have an CUSTOM_ID, the function returns nil. However, when
   CREATE is non nil, create a CUSTOM_ID if none is present
   already. PREFIX will be passed through to `org-id-new'. In any
   case, the CUSTOM_ID of the entry is returned."
    (interactive)
    (org-with-point-at pom
      (let ((id (org-entry-get nil "CUSTOM_ID")))
        (cond
         ((and id (stringp id) (string-match "\\S-" id))
          id)
         (create
          (setq id (org-id-new (concat prefix "h")))
          (org-entry-put pom "CUSTOM_ID" id)
          (org-id-add-location id (buffer-file-name (buffer-base-buffer)))
          id)))))

  (defun tributi/org-add-ids-to-headlines-in-file ()
    "Add CUSTOM_ID properties to all headlines in the
   current file which do not already have one."
    (interactive)
    (org-map-entries (lambda () (tributi/org-custom-id-get (point) 'create))))

  (defvar index/org-save-buffer-hook nil
    "Hook for `index/org-save-buffer'.")

  (defun index/org-save-buffer (&optional arg)
    "Saves buffer and invokes hook `index/org-save-buffer-hook'"
    (interactive "p")
    (run-hooks 'index/org-save-buffer-hook)
    (save-buffer arg))

  (org-defkey org-mode-map (kbd "C-x C-s") #'index/org-save-buffer)

  :hook ((index/org-save-buffer-hook . tributi/org-add-ids-to-headlines-in-file))
  :bind ((org-mode-map
          ("C-c C-M-l" . org-store-link))))

6.1.6. Inline Images

(leaf org
  :config

  (defun index/org-display-inline-images ()
    "Wrapper around `org-display-inline-images'.
This is added to the hook `index/org-save-buffer-hook'.  The hook
will be run each time after saving the buffer, so C-x C-s."
    (interactive)
    ;; The inline image width; times 2.5 here means 25% whereas times 10
    ;; would mean 100%.
    (setq org-image-actual-width
          (* 2.5 (frame-parameter (selected-frame) 'width)))
    (org-redisplay-inline-images)
    (org-display-inline-images t t (point-min) (point-max)))
  :hook ((index/org-save-buffer-hook . index/org-display-inline-images)))

6.1.7. To Do Keywords

(leaf org
  :config
  (setq org-todo-keywords
        (quote ((sequence "TODO(t)" "NEXT(n)" "REVIEW(r)" "|" "DONE(d)")
                (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)"))))
  (setq org-log-into-drawer "LOGBOOK")
  (setq org-log-note-headings
        (quote ((done . "CLOSING NOTE %t")
                (state . "State: %S => %s %t")
                (note . "Note taken on %t")
                (refile . "Refiled on %t")
                (reschedule  . "Schedule changed: %t %S => %s")
                (delschedule . "Not scheduled, was %S on %t")
                (redeadline  . "Deadline changed: %t %S => %s")
                (deldeadline . "Removed deadline, was %S on %t")
                (clock-out . ""))))

  (defun index/find-todo-file ()
    "Opens `~/TDO' file."
    (interactive)
    (let ((enable-local-variables :all)
          (enable-local-eval t))
      (find-file
       (expand-file-name
        "~/TDO"))))

  (defun index/org-todo-archive ()
    "Move all the sections with DONE/CANCELED to Archive section."
    (interactive)
    (ignore-errors
      (save-excursion
        (beginning-of-buffer)
        (search-forward-regexp
         (concat
          "^"
          outline-regexp
          "Checklist"))
        (org-mark-subtree)
        (let* ((beg (region-beginning))
               (end (region-end))
               (lb (line-number-at-pos beg))
               (le (line-number-at-pos end))
               (diff (- le lb)))
          (deactivate-mark)
          ;; Might overflow; when removing a section and, lets say it had
          ;; 5 lines, those 5 lines will still be continued.
          (dotimes (_ diff)
            (when (s-match
                   (concat
                    "^"
                    outline-regexp
                    "\\(DONE\\|CANCELLED\\)")
                   (thing-at-point 'line t))
              (org-archive-subtree))
            (next-line 1))))))

  (define-minor-mode index/org-todo-archive-mode
    "Upon save archive the `Checklist' section."
    :global t
    (if index/org-todo-archive-mode
        (progn
          (add-hook 'index/org-save-buffer-hook
                    #'index/org-todo-archive)
          (remove-hook 'index/org-save-buffer-hook
                       #'tributi/org-add-ids-to-headlines-in-file))
      (remove-hook 'index/org-save-buffer-hook
                   #'index/org-todo-archive)
      (add-hook 'index/org-save-buffer-hook
                #'tributi/org-add-ids-to-headlines-in-file)))

  (defun index/org-capture-todo ()
    "Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."
    (s-join
     "\n"
     `("\n"
       ,(concat "* TODO " (read-from-minibuffer "Post Title: "))
       "\n%i%?\n")))

  ;; (add-to-list 'org-capture-templates
  ;;              '("t"
  ;;                "Todo entry"
  ;;                entry
  ;;                (file+olp "~/TDO" "Checklist")
  ;;                (function index/org-capture-todo)))

  :bind (("C-c t" . index/find-todo-file)))

6.1.8. Appear

As of 2021-04-03 this section is under review; add more wording to this section.

When hovering over emphasis markers such as * or / it will display them, otherwise hide them.

(leaf org-appear
  :straight (org-appear :host github :repo "awth13/org-appear")
  :config
  (setq org-hide-emphasis-markers t)
  :hook ((org-mode-hook . org-appear-mode)))

6.1.9. Modern

(leaf org-modern
  :straight (org-modern :host github :repo "minad/org-modern")
  :config
  (setq org-modern-label-border 0.1)

  (setq org-modern-star [">" ">>" ">>>" ">>>>" ">>>>>"])
  (setq org-modern-timestamp nil)
  (setq org-modern-table t)
  (setq org-modern-table-vertical 1)
  (setq org-modern-table-horizontal 0)
  (setq org-modern-priority
        '((?A . "α")
          (?B . "β")
          (?C . "γ")))
  (setq org-modern-list
        '((?+ . "•")
          (?- . "–")
          (?* . "◦")))

  :hook ((org-mode-hook . org-modern-mode)))

6.2. LaTeX

(leaf tex-mode
  :config
  (defvar index/latex-compile nil
    "Stores the function `index/latex-compile' process.")

  (defun index/latex-compile (&optional arg)
    "Call `latexmk' on the current buffer.
With optinal ARG as prefix argument (\\[universal-argument]),
open the output buffer of the current process, if it exists."
    (interactive "P")
    (cond
     ((and arg (process-live-p index/latex-compile))
      (switch-to-buffer-other-window
       (process-buffer index/latex-compile)))
     ((process-live-p index/latex-compile)
      ;; Calls `process-live-p' twice, but whatever; this makes it
      ;; cleaner code.
      (+index/latex-buffer-cleanup))
     (:else
      (setq index/latex-compile
            (make-process
             :name "latex-compile"
             :buffer "*Latex Compile*"
             :command (list "latexmk"
                            "-pdf"
                            "-pvc"
                            "-interaction=nonstopmode"
                            (expand-file-name (buffer-name)))))
      (message "Started Automatic Compilation")
      (add-hook 'kill-buffer-hook #'+index/latex-buffer-cleanup))))

  (defun +index/latex-buffer-cleanup ()
    (when (and (eq major-mode 'latex-mode)
               (process-live-p index/latex-compile))
      (kill-process index/latex-compile)
      (call-process "latexmk"
                    nil nil nil
                    "-c" (expand-file-name (buffer-name)))
      (message "Stoped Automatic Compilation")
      (remove-hook 'kill-buffer-hook #'+index/latex-buffer-cleanup)))

  (defun index/latex-open-pdf ()
    (interactive)
    "Open current buffer's PDF"
    (let ((display-buffer-alist '(("*" (display-buffer-no-window)))))
      (async-shell-command
       (s-concat
        "zathura "
        (s-replace ".tex" ".pdf"
                   (expand-file-name (buffer-name)))))))

  :bind ((latex-mode-map
          ("C-c C-c" . index/latex-compile)
          ("C-c C-o" . index/latex-open-pdf))))

6.3. Focused Writing

6.3.1. Olivetti

This package is used to center the entire frame.

(leaf olivetti
  :straight t
  :config
  (setq olivetti-body-width (1- (/ (1+ (sqrt 5)) 2)))
  (setq olivetti-minimum-body-width 80) ; instead of 72; extra leeway
  (setq olivetti-recall-visual-line-mode-entry-state t)

  :hook ((org-mode-hook . olivetti-mode)))

6.3.2. Focus

This package will dim down other sentences, paragraphs, and code-blocks.

(leaf focus
  :straight t
  :config
  (setq focus-mode-to-thing '((prog-mode . defun)
                              (text-mode . paragraph))))

6.4. Auto Fill Mode

(leaf emacs
  :config
  (setq-default fill-column 72)

  (defun index/auto-fill-mode (&optional arg)
    "Wrapper around `auto-fill-mode'.
When executing, firstly call `fill-paragraph', and then enable
`auto-fill-mode'.  If `auto-fill-mode' is disabled, then do not
call `fill-paragraph', since paragraph should already be
indented.

Calls `index/focused-writing' when called with an argument.
\\{index/focused-writing}"
    (interactive "P")
    (when arg
      (index/focused-writing-mode 'toggle))
    (unless (bound-and-true-p auto-fill-function)
      (if (region-active-p)
          (fill-paragraph nil (region-bounds))
        (fill-paragraph)))
    (auto-fill-mode 'toggle))
  :hook ((text-mode . auto-fill-mode))
  :bind ((index/keys-minor-mode-map
          ("C-M-q" . index/auto-fill-mode))))

6.5. Treating Sentences

(leaf emacs
  :config
  (setq sentence-end-double-space t)
  (setq colon-double-space nil)
  (setq use-hard-newlines nil)
  (setq sentence-end-without-period nil))

6.6. Flyspell

Everyone makes mistakes, even in writing. It is nice to have a spell checker do the work for you: when a wrong word is entered into the buffer, the spell checker should know which words can be substituted with it.

In order for it to work, you'll need the folowing system packages:

aspell
An ispell derivative
aspell-en
English ispell dictionary
aspell-de
German ispell dictionary

But there is a caveat: I want the spellchecking to not distract me when I am writing, as it can easily lead to distraction. Thus, flyspell-mode is not a possibility, and such a little code must to the trick.

(leaf ispell
  :straight t
  :commands index/spell-dwim
  :config
  (setq flyspell-issue-message-flag nil)
  (setq flyspell-issue-welcome-flag nil)
  (setq ispell-program-name "aspell")
  (setq ispell-dictionary "en_GB")

  (defcustom index/spell-dictionaries-alist
    '(("English" . "en_GB")
      ("Deutsch" . "de_DE"))
    "Alist of `aspell' dictionaries.
Can be queried with the command line command: 'aspell dump
dicts'."
    :type 'alist)

  (defvar +index/spell-dwim-history '()
    "History of the command `index/spell-dwim'.")

  (defun index/spell-dwim (beg end)
    (interactive "r")
    (cond
     ((region-active-p) (flyspell-region beg end))
     ((thing-at-point 'word) (ispell-word))
     (:else (let ((ret (completing-read "Select A Dictionary: "
                                        index/spell-dictionaries-alist
                                        nil nil nil
                                        +index/spell-dwim-history)))
              (ispell-change-dictionary
               (alist-get ret index/spell-dictionaries-alist
                          nil nil #'string=))
              (message "Changed dictionary to '%s'" ret)))))

  :bind (("C-." . index/spell-dwim)))

6.7. Writing Mode

(leaf emacs
  :config
  (define-minor-mode index/focused-writing-mode
    "Toggle between Focused Writing."
    :init-value nil
    :global t
    (if index/focused-writing-mode
        (progn
          (setq-local mode-line-format nil)
          (setq cursor-type 'bar)
          (olivetti-mode 1)
          ;; (focus-mode 1)
          ;; (variable-pitch-mode 1)
          )
      (kill-local-variable 'mode-line-format)
      (force-mode-line-update)
      (setq cursor-type 'box)
      (olivetti-mode -1)
      ;; (variable-pitch-mode -1)
      ;; (focus-mode -1)
      ))

  :bind (("C-c w" . index/focused-writing-mode)))

6.8. denote – Note Taking Application

(leaf denote
  :straight (denote :repo "https://git.sr.ht/~protesilaos/denote")
  :config
  (setq denote-directory (expand-file-name "~/wtn/"))
  (setq denote-org-front-matter
        (s-join "\n"
                `("#+title:      %s"
                  "#+author:     Finn Sauer"
                  "#+email:      finn@finnsauer.com"
                  "#+date:       %s"
                  "#+filetags:   %s"
                  "#+identifier: %s"
                  "" "")))
  :hook ((denote-mode-hook . denote-dired-mode-in-directories))
  :bind (("C-c n n" . denote)
         ("C-c n l" . denote-link)
         ("C-c n N" . denote-type)
         ("C-c n b" . denote-link-backlinks)
         (dired-mode-map
          ("," . denote-dired-rename-file))))

7. Minibuffer

7.1. Keybindings

(leaf emacs
  :config
  ;; (defun tributi/describe-symbol-at-point (&optional arg)
  ;;     "Get help (documentation) for the symbol at point.
  ;;
  ;; With a prefix argument (\\[universal-argument]), switch to the
  ;; *Help* window.  If that is already focused, switch to the most
  ;; recently used window instead."
  ;;     (interactive "P")
  ;;     (let ((symbol (symbol-at-point)))
  ;;       (when symbol
  ;;         (describe-symbol symbol)))
  ;;     (when arg
  ;;       (let ((help (get-buffer-window "*Help*")))
  ;;         (when help
  ;;           (if (not (eq (selected-window) help))
  ;;               (select-window help)
  ;;             (select-window (get-mru-window)))))))

  (defun index/describe-symbol-dwim ()
    "TODO Documentation"
    (interactive)
    (let* ((char (save-excursion
                   (backward-char 1)
                   (thing-at-point 'char t)))
           (sym (if (string= char ")")
                    (save-excursion
                      (backward-list 1)
                      (forward-symbol 1)
                      (thing-at-point 'symbol t))
                  (save-excursion
                    (while (not (thing-at-point 'symbol t))
                      (backward-up-list 1)
                      (forward-symbol 1))
                    (thing-at-point 'symbol t)))))
      (describe-symbol (intern sym))))

  :bind (("H-h" . index/describe-symbol-dwim)))

7.2. Selectrum

(leaf vertico
  :straight t
  :config
  (setq vertico-count 7)
  (setq vertico-cycle t)
  (vertico-mode 1))

;; (leaf selectrum
;;   :straight t
;;   :config
;;   (setq selectrum-num-candidates-displayed 7)
;;   (setq selectrum-fix-minibuffer-height 7)
;;   (setq selectrum-max-window-height 7)
;;   (selectrum-mode 1))

(leaf selectrum-prescient
  :straight t
  :after selectrum
  :config
  (setq prescient-save-file
        (expand-file-name "prescienthist" user-emacs-directory))
  (selectrum-prescient-mode 1)
  (prescient-persist-mode 1))

(leaf marginalia
  :straight t
  :commands selectrum-read
  :config
  (setq marginalia-annotators '(marginalia-annotators-heavy nil))
  (advice-add #'marginalia-cycle
              :after
              (lambda ()
                (when (bound-and-true-p selectrum-mode)
                  (selectrum-exhibit))))
  :hook ((minibuffer-setup-hook . marginalia-mode))
  :bind ((minibuffer-local-map
          ("C-M-a" . marginalia-cycle))))

(leaf embark
  :straight t
  :bind (("H-e" . embark-act)))

(leaf orderless
  :straight t
  :config
  (setq completion-styles '(orderless)))

;; (leaf mini-frame
;;   :straight t
;;   :commands (selectrum-completing-read selectrum-read)
;;   :config
;;   (add-to-list 'mini-frame-ignore-commands #'vr/query-replace)
;;   (setq mini-frame-show-parameters
;;         `((left . 0.5)
;;           (top . 0.3)
;;           (width . 0.7)
;;           (height . 8)
;;           (background-color . ,(face-attribute 'default :background))
;;           (alpha . 92)
;;           ;; This is for EXWM; without this the mini-frame would be drawn
;;           ;; as a child frame-- which is actually better-- but EXWM draws
;;           ;; X windows on top of Emacs.  If, then, the mini-frame is drawn
;;           ;; it will be overlapped by the X window.  Comment the following
;;           ;; s-expression out, if you don't use EXWM.
;;           ;; (parent-frame)
;;           ))
;;   :hook (after-init-hook . mini-frame-mode))

7.3. TODO Consult

(leaf consult
  :straight t
  :require t
  :bind ((org-mode-map
          ("C-c C-j" . consult-org-heading))
         (index/keys-minor-mode-map
          ("M-y" . consult-yank-pop)
          ("C-x C-r" . consult-recent-file))
         ("M-g M-g" . consult-line)
         ("M-s M-s" . consult-ripgrep)
         ("M-s M-f" . consult-focus-lines)
         ("M-s M-k" . consult-kmacro)
         ("C-x r r" . consult-register)
         ("M-s M-r" . consult-register-store)
         ("C-x b" . consult-buffer)))

8. Buffer, Directory, and Window Management

8.1. Buffer

8.1.1. Auto Complete

(leaf company
  :straight t
  :hook ((after-init-hook . global-company-mode))
  :config
  (leaf company-prescient
    :straight t
    :config
    (company-prescient-mode 1))

  (defun index/company-abort-and-space ()
    "Abort company and insert SPC."
    (interactive)
    (company-abort)
    (insert " "))
  :bind ((company-active-map
          ("SPC" . index/company-abort-and-space))))

8.1.2. Yasnippet

(leaf yasnippet
  :straight t
  :hook ((after-init-hook . yas-global-mode)))
(leaf yasnippet-snippets :straight t :after yasnippet)

8.1.3. Comments

(leaf newcomment
  :config
  (setq comment-empty-lines t)
  (setq comment-fill-column nil)
  (setq comment-multi-line t)
  (setq comment-style 'multi-line)

  (defun index/comment-line ()
    (interactive)
    "A modified version of `comment-line'."
    (if (region-active-p)
        (comment-or-uncomment-region
         (save-excursion
           (goto-char (region-beginning))
           (line-beginning-position))
         (save-excursion
           (goto-char (region-end))
           (line-end-position)))
      (save-excursion
        (comment-line 1))))

  :bind ((index/keys-minor-mode-map
          ("C-;" . index/comment-line)
          ("C-M-;" . comment-kill))))

8.1.4. Insert Simple Skeleton

This hand-written small function asks the user if– when a new file is being created– a skeleton ought to be inserted or not. This skeleton is predefined inside a property list. Thus being easy to change and manipulate. A major-mode is predefined with a property list which represents the strings to be inserted. When its a list, then untangle– i.e. join– the strings and if there is another list inside this list, then simply evaluate it.

When visiting a new org-mode file the predefined property list will be executed—the property list is shown below. In this example, it prompts the user for a title. Let's say the supplied text is: A neat title. It would generate the following text– as shown below.

This is the example property list:

'(("#+title: " (read-string "Title for this document? "))
  "#+author: Finn Sauer (https://finnsauer.com)"
  ("#+date: " (format-time-string "%F")))

The final text that will be inserted would be:

#+title: A neat title
#+author: Finn Sauer
#+date: 2022-04-27

(defcustom index/new-file-skeleton
  '((org-mode (("#+title: " (read-string "Title for this document? "))
               "#+author: Finn Sauer (https://finnsauer.com)"
               ("#+date: " (format-time-string "%F"))
               ""))
    ;; (emacs-lisp-mode ((";;; " (buffer-name) " --- "
    ;;                    (read-string "Header Text? "))
    ;;                   ""
    ;;                   ;; Insert GPLv3 via `index/generate-GPLv3'.
    ;;                   ((s-join
    ;;                     "\n"
    ;;                     (--map (s-prepend ";; " it)
    ;;                            (s-split "\n" (index/generate-GPLv3)))))
    ;;                   ""
    ;;                   ";;; Commentary:"
    ;;                   ""
    ;;                   ";;; Code:"
    ;;                   "" "" ""
    ;;                   (";;; " (buffer-name) " ends here")))
    )
  "Alist for any new file for the major mode.

First element should be the major mode to be matched.  The second
element should be a list of strings, whose elements represent new
lines.  When the string is a list, then evaluate it instead (see
the example)."
  :type 'alist)

(defun index/new-file-skeleton ()
  "When a new file is visited or created, insert skeleton."
  (when (and buffer-file-name
             (not (file-exists-p buffer-file-name))
             (bobp) (eobp)
             (assoc major-mode index/new-file-skeleton)
             (yes-or-no-p
              (concat
               "Insert '" (symbol-name major-mode) "' skeleton? ")))
    (dolist (line (--map
                   (if (not (stringp it))
                       (s-join "" (--map (if (listp it) (eval it) it) it))
                     it)
                   (cadr (assoc major-mode index/new-file-skeleton))))
      (insert line "\n"))))

(add-hook 'find-file-hook #'index/new-file-skeleton)

8.2. Directory

8.2.1. Dired - Directory Editor

(leaf dired
  :config
  (setq dired-recursive-copies 'always)
  (setq dired-recursive-deletes 'always)
  (setq delete-by-moving-to-trash t)
  (setq dired-listing-switches
        "-AGFhlv --group-directories-first --time-style=long-iso")
  (setq dired-dwim-target t)
  (setq trash-directory (expand-file-name "~/.trash"))

  (defun index/dired-mpv-open ()
    "Open file in `mpv_open'."
    (interactive)
    (index/call-process
     `("mpv_open"
       ,(dired-get-filename)))
    (message
     (concat
      "Launching MPV for "
      (propertize
       (file-name-base
        (dired-get-filename))
       'face
       'success))))

  (defun index/dired-hide-dot-files ()
    "Hides all files/directories staring with a dot.
This operation can be refreshed with the key g.  Alternatively
this function can be called to do the same."
    (interactive)
    (if (dired-mark-files-regexp "^\\.")
        (dired-do-kill-lines)
      (revert-buffer)))

  (defun index/dired-call-process ()
    "Run `index/call-process' with command on file."
    (interactive)
    (index/call-process
     `(,(completing-read
         "Launch in $PATH: "
         (flatten-tree
          (--map (directory-files-recursively it ".")
                 (s-split ":" (getenv "PATH")))))
       ,(dired-get-filename))))

  :hook ((dired-mode-hook . dired-hide-details-mode))

  :bind ((dired-mode-map
          ("<return>" . dired-find-alternate-file)
          ("C-<return>" . dired-find-file-other-window)
          ("o" . index/dired-mpv-open)
          ("h" . index/dired-hide-dot-files)
          ("e" . index/dired-call-process)
          ("<tab>" . dired-subtree-toggle)
          ("<C-tab>" . dired-subtree-cycle)
          ("P" . peep-dired))))

(leaf dired-async
  :after (dired async)
  :hook ((dired-mode-hook . dired-async-mode)))

(leaf wdired
  :after dired
  :commands wdired-change-to-wdired-mode
  :config
  (setq wdired-allow-to-change-permissions t)
  (setq wdired-create-parent-directories t))

(leaf dired-subtree
  :straight t
  :after dired
  :commands dired
  :config
  (setq dired-subtree-use-backgrounds nil))

(leaf dired-x
  :after dired
  :bind (("C-x C-j" . dired-jump)
         ("H-j" . dired-jump)))

(leaf peep-dired
  :straight t
  :after dired
  :commands dired
  :config
  ;; https://emacs.stackexchange.com/q/46664
  (defun tributi/toggle-window-split ()
    (interactive)
    (if (= (count-windows) 2)
        (let* ((this-win-buffer (window-buffer))
               (next-win-buffer (window-buffer (next-window)))
               (this-win-edges (window-edges (selected-window)))
               (next-win-edges (window-edges (next-window)))
               (this-win-2nd (not (and (<= (car this-win-edges)
                                           (car next-win-edges))
                                       (<= (cadr this-win-edges)
                                           (cadr next-win-edges)))))
               (splitter
                (if (= (car this-win-edges)
                       (car (window-edges (next-window))))
                    'split-window-horizontally
                  'split-window-vertically)))
          (delete-other-windows)
          (let ((first-win (selected-window)))
            (funcall splitter)
            (if this-win-2nd (other-window 1))
            (set-window-buffer (selected-window) this-win-buffer)
            (set-window-buffer (next-window) next-win-buffer)
            (select-window first-win)
            (if this-win-2nd (other-window 1))))))

  (defun index/peep-dired-mark ()
    "Mark current file and move to next file."
    (interactive)
    (save-excursion
      (dired-mark nil))
    (peep-dired-next-file))

  (defun index/peep-dired-unmark ()
    "Unmark current file and move to next file."
    (interactive)
    (save-excursion
      (dired-unmark nil))
    (peep-dired-next-file))

  :bind ((dired-mode-map
          ("T" . tributi/toggle-window-split))
         (peep-dired-mode-map
          ("n" . peep-dired-next-file)
          ("p" . peep-dired-prev-file)
          ("m" . index/peep-dired-mark)
          ("u" . index/peep-dired-unmark))))

(leaf all-the-icons-dired
  :straight t
  :after dired
  :hook (dried-mode-hook . all-the-icons-dired-mode))

8.3. Window

8.3.1. Rules

(leaf emacs
  :config
  ;; TODO: implement φ = (+ .5 (/ (sqrt 5) 2))
  (setq display-buffer-alist
        '(("\\*Help.*"
           (display-buffer-in-side-window)
           (window-height . 0.20)
           (side . left)
           (slot . -1))
          ("\\*undo-tree\\*"
           (display-buffer-in-side-window)
           (window-height . 0.20)
           (side . right)
           (slot . -1))
          ("\\*Kill Ring\\*"
           (display-buffer-in-side-window)
           (window-height . 0.10)
           (side . right)
           (slot . 0))
          ("\\*Messages.*"
           (display-buffer-in-side-window)
           (window-height . 0.16)
           (side . bottom)
           (slot . 1)
           (window-parameters . ((no-other-window . t))))
          ("\\*\\(Backtrace\\|Warnings\\|Compile-Log\\)\\*"
           (display-buffer-in-side-window)
           (window-height . 0.16)
           (side . bottom)
           (slot . 2))))
  :hook ((help-mode-hook . visual-line-mode)
         (custom-mode-hook . visual-line-mode)))

*

8.3.2. DEPRECATED Functions

The following has been DEPRECATED, as I never used these functions; again they where copied from prot without thinking.

(leaf emacs
  :config
  (defun tributi/window-dired-vc-root-left ()
    "Open project or dir `dired' in a side window."
    (interactive)
    (let ((dir (if (eq (vc-root-dir) nil)
                   (dired-noselect default-directory)
                 (dired-noselect (vc-root-dir)))))
      (display-buffer-in-side-window
       dir `((side . left)
             (slot . -1)
             (window-width . 0.16)
             (window-parameters
              . ((no-other-window . t)
                 (no-delete-other-windows . t)
                 (mode-line-format
                  . (" "
                     mode-line-buffer-identification))))))
      (with-current-buffer dir
        (rename-buffer "*Dired-Side*")
        (setq-local window-size-fixed 'width)))
    (with-eval-after-load 'ace-window
      (when (boundp 'aw-ignored-buffers)
        (add-to-list 'aw-ignored-buffers "*Dired-Side*")))))
(leaf emacs
  :config
  (defvar tributi/window-configuration nil
    "Current window configuration.
Intended for use by `tributi/window-monocle'.")

  (define-minor-mode tributi/window-single-toggle
    "Toggle between multiple windows and single window.
This is the equivalent of maximising a window.  Tiling window
managers such as DWM, BSPWM refer to this state as 'monocle'."
    :lighter " [M]"
    :global nil
    (if (one-window-p)
        (when tributi/window-configuration
          (set-window-configuration tributi/window-configuration))
      (setq tributi/window-configuration (current-window-configuration))
      (delete-other-windows)))

  (defun tributi/kill-buffer-current (&optional arg)
    "Kill current buffer or abort recursion when in minibuffer."
    (interactive "P")
    (if (minibufferp)
        (abort-recursive-edit)
      (kill-buffer (current-buffer)))
    (when (and arg (not (one-window-p)))
      (delete-window)))
  :bind (("H-m" . tributi/window-single-toggle)
         ("H-k" . tributi/kill-buffer-current)))

8.3.3. Keybindings

(leaf emacs
  :bind (("H-o" . other-window)
         ("H-0" . delete-window)
         ("H-1" . delete-other-windows)
         ("H-2" . split-window-below)
         ("H-3" . split-window-right)))

8.3.4. Window History

This little build-in package keeps track of windows that have been closed. This is useful when you accidentally delete a window and you wish to undo that particular deletion process.

(leaf winner
  :hook ((after-init-hook . winner-mode))
  :bind (("<H-backspace>" . winner-undo)
         ("<C-H-backspace>" . winner-redo)))

9. REVIEW Miscellaneous

As of 2021-01-31 this section is under REVIEW: merge this section with the Generic Settings section.

9.1. Scratch Buffer for Current Major Mode

(leaf scratch
  :straight t
  :config
  (defun tributi/scratch-buffer-setup ()
    "Add contents to `scratch' buffer and name it accordingly."
    (let* ((mode (format "%s" major-mode))
           (string (concat "Scratch buffer for: " mode "\n\n")))
      (when scratch-buffer
        (save-excursion
          (insert string)
          (goto-char (point-min))
          (comment-region (point-at-bol) (point-at-eol)))
        (forward-line 2))
      (rename-buffer (concat "*Scratch for " mode "*") t))) ; Derived from
  :hook ((scratch-create-buffer-hook . tributi/scratch-buffer-setup))
  :bind (("C-c b" . scratch)))

9.2. Fish shell

(leaf fish-mode
  :straight t
  :config
  (defun index/fish-exit-editor ()
    "Exits the command line edit buffer."
    (interactive)
    (if (string-match "^tmp\..\\{10\\}\.fish$" (buffer-name))
        (progn
          (with-editor-finish 'force))
      (message "This is not a Command Line Buffer")))
  :bind ((fish-mode-map
          ("C-c C-c" . index/fish-exit-editor))))

9.3. Code Keywords Highlight

This package will highlight code keywords such as TODO. This is one thing I missed from (n)vim OOTB-Experience.

(leaf hl-todo
  :straight t
  :config
  (add-to-list 'hl-todo-keyword-faces
               (cons "DEPRECATED" "#FF79C6"))
  :hook ((prog-mode-hook . hl-todo-mode)
         (text-mode-hook . hl-todo-mode))
  :bind (hl-todo-mode-map
         ("C-c h p" . hl-todo-previous)
         ("C-c h n" . hl-todo-next)
         ("C-c h o" . hl-todo-occur)
         ("C-c h i" . hl-todo-insert)))

9.4. Flymake

(leaf flymake
  :hook ((prog-mode-hook . flymake-mode)))

9.5. Open All Root Files with Super User

Usually when I already navigate to some non-user's directory– outside the home directory– I want to edit the file as root. It's kinda impossible to mistakenly edit some file under /etc/ for example.

This minor mode will reopen every file outside the home directory as sudo with tramp.

;; These functions where found in the package `crux.el'.
(leaf tramp
  :require t
  :config
  (defun tributi/file-owner-uid (filename)
    "Return the UID of the FILENAME as an integer.
See `file-attributes' for more info."
    (nth 2 (file-attributes filename 'integer)))

  (defun tributi/file-owned-by-user? (filename)
    "Return t if file FILENAME is owned by the currently logged
in user."
    (equal (tributi/file-owner-uid filename)
           (user-uid)))

  (defun tributi/already-root? ()
    (let ((remote-method (file-remote-p default-directory 'method))
          (remote-user (file-remote-p default-directory 'user)))
      (and remote-method
           (or (member remote-method '("sudo" "su" "ksu" "doas"))
               (string= remote-user "root")))))

  (defun tributi/find-alternate-file-as-root (filename)
    "Wraps `find-alternate-file' with opening FILENAME as root."
    (let ((remote-method (file-remote-p default-directory 'method))
          (remote-host (file-remote-p default-directory 'host))
          (remote-localname (file-remote-p filename 'localname)))
      (find-alternate-file (format "/%s:root@%s:%s"
                                   (or remote-method "sudo")
                                   (or remote-host "localhost")
                                   (or remote-localname filename)))))

  (defun tributi/sudo-edit (&optional arg)
    "Edit currently visited file as root.
With a prefix ARG prompt for a file to visit.  Will also prompt
for a file to visit if current buffer is not visiting a file."
    (interactive "P")
    (if (or arg (not buffer-file-name))
        (let ((remote-method (file-remote-p default-directory 'method))
              (remote-host (file-remote-p default-directory 'host))
              (remote-localname (file-remote-p default-directory 'localname)))
          (find-file (format "/%s:root@%s:%s"
                             (or remote-method "sudo")
                             (or remote-host "localhost")
                             (or remote-localname
                                 (read-file-name "Find file (as root): ")))))

      (if (tributi/already-root?)
          (message "Already editing this file as root.")
        (let ((place (point)))
          (tributi/find-alternate-file-as-root buffer-file-name)
          (goto-char place)))))

  (defun tributi/reopen-as-root ()
    "Find file as root if necessary.
Meant to be used as `find-file-hook'.  See also
`tributi/reopen-as-root-mode'."
    (unless (or (tramp-tramp-file-p buffer-file-name)
                (equal major-mode 'dired-mode)
                (not (file-exists-p (file-name-directory buffer-file-name)))
                (file-writable-p buffer-file-name)
                (tributi/file-owned-by-user? buffer-file-name))
      (tributi/find-alternate-file-as-root buffer-file-name)))

  (define-minor-mode tributi/reopen-as-root-mode
    "Automatically reopen files as root if we can't write to them
as the current user."
    :global t
    (if tributi/reopen-as-root-mode
        (add-hook 'find-file-hook #'tributi/reopen-as-root)
      (remove-hook 'find-file-hook #'tributi/reopen-as-root)))

  (tributi/reopen-as-root-mode 1))

9.6. Keyboard Macro

(leaf kmacro
  :config
  (defvar index/kmacro-file (expand-file-name "kmacro.el" user-emacs-directory)
    "File used to store/load keyboard macros (`kmacro').")

  (defun index/save-last-kmacro ()
    "Inserts the last recorded kmacro into `index/kmacro-file'.
It will prompt for a name.  This will store the name in said file
with the namespace of `kmacro/'; every named kmacro begins as
such.

Example:

name: 'my-super-macro'
kmacro name: 'kmacro/my-super-macro'"
    (interactive)
    (let ((name (intern
                 (concat
                  "kmacro/"
                  (read-string "Keyboard Macro Name: ")))))
      (kmacro-name-last-macro name)
      (with-temp-file index/kmacro-file
        (insert-file-contents index/kmacro-file)
        (end-of-buffer)
        (insert-kbd-macro name)
        (insert (concat
                 "(put '"
                 (symbol-name name)
                 " 'kmacro t)"))
        (newline 2))))

  (load-file index/kmacro-file)

  (defvar index/execute-named-kmacro-last nil
    "Last called `kmacro' from `index/execute-named-kmacro'.")

  (defun index/execute-named-kmacro (&optional arg)
    "Execute a named keyboard macro.
With a prefix argument call the last executed keyboard macro.  If
there is no last keyboard macro in`index/execute-named-kmacro-last'
 just call this function normally."
    (interactive "P")
    (if arg
        (if index/execute-named-kmacro-last
            (funcall (symbol-function index/execute-named-kmacro-last))
          (index/execute-named-kmacro))
      (let ((kmacro (intern
                     (completing-read
                      "Execute Named Keyboard Macro: "
                      obarray
                      #'kmacro-keyboard-macro-p
                      t))))
        (setq index/execute-named-kmacro-last kmacro)
        (funcall (symbol-function kmacro)))))

  :bind (("C-x M-k" . index/save-last-kmacro)
         ("C-x C-M-k" . index/execute-named-kmacro)))

9.7. Jump to definition

(leaf dumb-jump
  :straight t
  :commands dumb-jump-go
  :hook ((xref-backend-functions . dumb-jump-xref-activate))
  :bind (("C-x g" . dumb-jump-go)))

9.8. Upcase / Downcase / Capitalize

These build-in functions are a blessing. I use them all the time even though they seem kinda specific. But multiple occasions I want to convert a word to upper case. For example in the docstring, where the convention is, that you put the function's argument name in upper case.

The keybinding for these are the following:

Table 3: The function name and description where automatically generated; see how.
KeybindingFunction NameDescription
C-x C-ldowncase-regionConvert the region to lower case. In programs, wants two arguments.
C-x C-uupcase-regionConvert the region to upper case. In programs, wants two arguments.
M-ldowncase-wordConvert to lower case from point to end of word, moving over.
M-uupcase-wordConvert to upper case from point to end of word, moving over.
M-ccapitalize-wordCapitalize from point to the end of word, moving over.

But why are there no DO WHAT I MEAN versions? It seems to be a perfect fit. When I have a region active operate on that else operate on word. This will save up the first two keybindings.

Since to my luck someone already thought about this already and defined these functions, we just need to bind them.

(leaf emacs
  :bind (("M-u" . upcase-dwim)
         ("M-l" . downcase-dwim)
         ("M-c" . capitalize-dwim)
         ("C-x C-u" . nil)
         ("C-x C-l" . nil)))

Afterwards the table will look like this:

Table 4: The function name and description where automatically generated; see how.
KeybindingFunction NameDescription
C-x C-lnilnil
C-x C-unilnil
M-ldowncase-dwimDowncase words in the region, if active; if not, downcase word at point.
M-uupcase-dwimUpcase words in the region, if active; if not, upcase word at point.
M-ccapitalize-dwimCapitalize words in the region, if active; if not, capitalize word at point.

9.9. Insert Date

(leaf emacs
  :config
  (defun index/insert-date (&optional arg)
    (interactive "*P")
    (insert (format-time-string (if arg
                                    "%F %R %z"
                                  "%F"))))
  :bind ((index/keys-minor-mode-map
          ("H-i" . index/insert-date))))

9.10. Dummy Text – Lorem Ipsum

It is always nice to insert some dummy text into the current buffer. As I want to have some text to play on– be it a keyboard macro I'm currently writing or something else.

There is a function in emmet-mode called emmet-lorem-generate and it works perfectly fine. I had some problems requiring it, so in the end I wrote my own function as a little exercise.

Why Lorem Ipsum?

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

You can read up more about it on here.

(leaf emacs
  :require cl
  :config

  (defcustom +index/lorem-words
    '("sed" "ut" "perspiciatis" "unde" "omnis" "iste" "natus" "error"
      "sit" "voluptatem" "accusantium" "doloremque" "laudantium" "totam"
      "rem" "aperiam" "eaque" "ipsa" "quae" "ab" "illo" "inventore"
      "veritatis" "et" "quasi" "architecto" "beatae" "vitae" "dicta"
      "sunt" "explicabo" "nemo" "enim" "ipsam" "quia" "voluptas"
      "aspernatur" "aut" "odit" "fugit" "consequuntur" "magni" "dolores"
      "eos" "qui" "ratione" "sequi" "nesciunt" "neque" "porro"
      "quisquam" "est" "dolorem" "ipsum" "dolor" "amet" "consectetur"
      "adipisci" "velit" "non" "numquam" "eius" "modi" "tempora"
      "incidunt" "labore" "dolore" "magnam" "aliquam" "quaerat" "ad"
      "minima" "veniam" "quis" "nostrum" "exercitationem" "ullam"
      "corporis" "suscipit" "laboriosam" "nisi" "aliquid" "ex" "ea"
      "commodi" "consequatur" "autem" "vel" "eum" "iure" "reprehenderit"
      "in" "voluptate" "esse" "quam" "nihil" "molestiae" "illum"
      "fugiat" "quo" "nulla" "pariatur" "at" "vero" "accusamus" "iusto"
      "odio" "dignissimos" "ducimus" "blanditiis" "praesentium"
      "voluptatum" "deleniti" "atque" "corrupti" "quos" "quas"
      "molestias" "excepturi" "sint" "occaecati" "cupiditate"
      "provident" "similique" "culpa" "officia" "deserunt" "mollitia"
      "animi" "id" "laborum" "dolorum" "fuga" "harum" "quidem" "rerum"
      "facilis" "expedita" "distinctio" "nam" "libero" "tempore" "cum"
      "soluta" "nobis" "eligendi" "optio" "cumque" "impedit" "minus"
      "quod" "maxime" "placeat" "facere" "possimus" "assumenda"
      "repellendus" "temporibus" "quibusdam" "officiis" "debitis"
      "necessitatibus" "saepe" "eveniet" "voluptates" "repudiandae"
      "recusandae" "itaque" "earum" "hic" "tenetur" "a" "sapiente"
      "delectus" "reiciendis" "voluptatibus" "maiores" "alias"
      "perferendis" "doloribus" "asperiores" "repellat")
    "Words to be used randomly by `+index/lorem-generator'."
    :type 'list)

  (defcustom +index/lorem-punctuation
    '("." "." "?" "!")
    "Punctuation to be used randomly by `+index/lorem-generator'."
    :type 'list)

  (defun +tributi/lorem-choice-words (count &optional s)
    (let* ((l (length +index/lorem-words))
           (s (random l))
           (f (+ s count))
           (e (if (< l f) l f)))
      (append
       (subseq +index/lorem-words s e)
       (if (= e l) (+tributi/lorem-choice-words (- f l) 0)))))

  (defun +index/lorem-generator (count)
    (let* ((l (+tributi/lorem-choice-words count))
           (r (random (length +index/lorem-punctuation)))
           (! (car (subseq +index/lorem-punctuation r (1+ r)))))
      (setf (car l) (capitalize (car l)))
      (concat (s-join " " l) !)))

  (defun index/lorem-insert (&optional arg)
    "Lorem Lipsum Generator.
If this function is called without any universal argument it will
insert 10 words.  When an argument is provided use this word
count instead.  When called repetitively-- so multiple
invocations-- it will insert a double space ' ' before each dummy
text after the second one."
    (interactive "*P")
    (when (eq last-command 'index/lorem-insert)
      (insert "  "))
    (if (listp arg)
        (insert (+index/lorem-generator 10))
      (insert (+index/lorem-generator arg))))
  :bind ((index/keys-minor-mode-map
          ("M-i" . index/lorem-insert))))

9.11. Insert the AGPLv3

Having the ability to quickly insert the AGPL license into the buffer is great. I use it all the time when creating a new script, or editing an old script, where I see that the license is missing. Simply call this function and finished!

I don't have to write a complicated function for a prompt to insert a different license. I will always use this license for any programming piece. Although when licensing written work such as my website, then I will be using CC-BY-SA-4.0 a copyleft license such as AGPL where you are free to use my written work on the sole premise to refer to me and also license it under a license that respects, and, thus, enforces the users freedom.

(leaf emacs
  :config
  (defmacro index/insert-agplv3--variable (var)
    `(if (eq (getenv ,(upcase (symbol-name var))) nil)
         (read-string
          ,(concat
            (capitalize (symbol-name var))
            "? "))
       (getenv ,(upcase (symbol-name var)))))

  (defun index/generate-agplv3 ()
    "Return the agplv3 as string."
    (concat
     "Copyright (C) "
     (format-time-string "%Y ")
     (index/insert-agplv3--variable name)
     " "
     (index/insert-agplv3--variable surname)
     " <"
     (index/insert-agplv3--variable mail)
     ">

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>."))

  (defun index/insert-agplv3 (&optional arg)
    "Insert the agplv3 as a comment.
This requires the NAME, SURNAME, and MAIL environmental variable
to be set.  If they don't exist, it will prompt for it.  When
called without a prefix, add newlines around the text block.  If
a prefix is provided omit the newlines."
    (interactive "P")
    (let ((old-point (point)))
      (save-excursion
        (unless arg
          (newline))
        (insert (index/generate-agplv3))
        (comment-region old-point (point))
        (unless arg
          (newline)))))
  :bind ((index/keys-minor-mode-map
          ("C-M-i" . index/insert-agplv3))))

9.12. Automatically Update the Lisence Header

I don't want to manually update the date in the header when I update a file, and the year has changed. For such a task we a a computer to do the repetive tasks.

(leaf emacs
  :config

  (defun index/agpl-update-header ()
    "Update the AGPL header date."
    (let ((s (->> (buffer-substring-no-properties
                   (point-min) (point-max))
                  (s-split "\n")
                  (--map
                   (s-match (rx bol (* " ") (+ (literal comment-start))
                                (* " ") "Copyright (C) " (= 4 num)
                                (* "-" (= 4 num)) (* any) eol)
                            it))
                  flatten-tree
                  car)))
      (when s
        (save-excursion
          (goto-char (point-min))
          (search-forward s)
          (beginning-of-line)
          (search-forward-regexp (rx (= 4 num) (* "-" (= 4 num))))
          (let ((nums (s-split "-" (thing-at-point 'sexp :no-properties)))
                (end (point)))
            (if (= (length nums) 1)
                (unless (string= (car nums) (format-time-string "%Y"))
                  (goto-char end)
                  (insert "-" (format-time-string "%Y")))
              (unless (string= (cadr nums) (format-time-string "%Y"))
                (delete-region (- end 4) end)
                (goto-char (- end 4))
                (insert (format-time-string "%Y")))))))))

  (define-minor-mode index/agpl-auto-update-header-mode
    "Automatically update the AGPL header date."
    :global t
    (if index/agpl-auto-update-header-mode
        (add-hook 'before-save-hook #'index/agpl-update-header)
      (remove-hook 'before-save-hook #'index/agpl-update-header)))

  :hook ((after-init-hook . index/agpl-auto-update-header-mode)))

9.13. Password Store (pass) Interface

Pass or alternatively known as password store is a great utility for storing passwords. Pass is a simple bash script that hacks multiple programs together to create a password manager. gpg being the main entry point of encryption.

What is the benefit of a small bash script?

At any time you can look at the source code with $EDITOR $(which pass)– EDITOR being Emacs of course.

Why not KeePassXC?

This is a great password manager if you like GUIs. I highly recommend it instead of pass if you prefer a GUI. Pass is great if you want the ability to script it. Thus this Emacs package even exist because of that.

(leaf password-store
  :straight t)

9.14. Workspaces (Emacs 27 Tabs)

(leaf tab-bar
  :config
  (setq tab-bar-close-button-show nil)
  (setq tab-bar-close-last-tab-choice 'tab-bar-mode-disable)
  (setq tab-bar-close-tab-select 'recent)
  (setq tab-bar-new-tab-choice t)
  (setq tab-bar-new-tab-to 'right)
  (setq tab-bar-position nil)
  (setq tab-bar-show nil)
  (setq tab-bar-tab-hints nil)
  (setq tab-bar-tab-name-function 'tab-bar-tab-name-all)

  (tab-bar-mode -1)
  (tab-bar-history-mode -1)

  (defun tributi/tab-bar-select-tab-dwim ()
    "Do-What-I-Mean function for getting to a `tab-bar-mode' tab.
If no other tab exists, create one and switch to it.  If there is
one other tab (so two in total) switch to it without further
questions.  Else use completion to select the tab to switch to."
    (interactive)
    (let ((tabs (--map (alist-get 'name it)
                       (tab-bar--tabs-recent))))
      (cond ((eq tabs nil)
             (tab-new))
            ((eq (length tabs) 1)
             (tab-next))
            (t
             (tab-bar-switch-to-tab
              (completing-read "Select tab: " tabs nil t))))))

  :bind (("H-t" . tributi/tab-bar-select-tab-dwim)
         ("C-x t s" . tab-switcher)))

9.15. Make Screenshot

(defun index/make-screenshot (&optional desktop?)
  "Make a screenshot of the current Emacs frame.
If DESKTOP? is provided, make an image with PNG format and
capture the whole desktop instead of SVG and only Emacs."
  (if desktop?
      (prog1 "/tmp/emacs-screenshot.png"
        (shell-command "import -window root /tmp/emacs-screenshot.png"))
    (prog1 "/tmp/emacs-screenshot.svg"
      (write-region
       (x-export-frames nil 'svg)
       nil
       "/tmp/emacs-screenshot.svg"))))

9.16. Eww

TODO: This will be the new section on eww.

(leaf browse-url
  :require t
  :config
  (setq browse-url-browser-function #'eww-browse-url)
  (setq browse-url-secondary-browser-function
        #'browse-url-default-browser)

  (defun browse-url-xdg-open (url &optional ignored)
    "Open in env var BROWSER instead of xdg-open."
    (interactive (browse-url-interactive-arg "URL: "))
    (call-process (getenv "BROWSER") nil 0 nil url)))

(leaf shr
  :config
  ;; To wrap paragraphs at `fill-column'.  Which is to be expected when
  ;; inside Emacs...
  (setq shr-width fill-column)
  (setq shr-use-colors nil)             ; No colours please!
  (setq shr-use-fonts nil)
  (setq shr-bullet "• ")                ; Real bullet points instead of -
  (setq shr-hr-line ?─)                 ; Has the effect of a full line
  ;; Golden Ratio as image width, please:
  (setq shr-max-image-proportion (/ (1- (sqrt 5)) 2))
  (setq shr-image-animate nil)          ; Too much lag!
  (setq shr-cookie-policy nil))         ; NO!

(leaf eww
  :after browse-url shr
  :commands index/eww-search
  :config
  (setq eww-restore-desktop t)
  (setq eww-desktop-remove-duplicates t)
  (setq eww-header-line-format nil)
  (setq eww-search-prefix "http://127.0.0.1:8888/search?q=")

  (defcustom index/eww-search-prefix
    '((v "https://yewtu.be/search?q=")
      (a "https://wiki.archlinux.org/?search=")
      (d "https://en.wiktionary.org/wiki/"))
    "Alist of possible search engines."
    :type 'alist)

  (defun +tributi/eww-rename-buffer ()
    "Rename EWW buffer using page title or URL.
To be used by `eww-after-render-hook'."
    (let ((name (if (eq "" (plist-get eww-data :title))
                    (plist-get eww-data :url)
                  (plist-get eww-data :title))))
      (rename-buffer (format "*%s ~ eww*" name) t)))

  (advice-add 'eww-back-url :after #'+tributi/eww-rename-buffer)
  (advice-add 'eww-forward-url :after #'+tributi/eww-rename-buffer)

  (defun index/eww-reload (&optional arg)
    "Prefer to reload from cache, and not from the web server.
With optinal ARG as prefix argument (\\[universal-argument]),
force loading from the web server."
    (interactive "P")
    (or (and (not arg)
             (prog1 t (eww-reload :local)))
        (eww-reload)))

  (defun index/eww ()
    "Calls `eww' with a prefix arg; thus opening in new buffer"
    (interactive)
    (let ((current-prefix-arg 4))
      (call-interactively #'eww)))

  (defun index/eww-search (query)
    (interactive (list (read-string "Enter URL or keywords: "
                                    (thing-at-point 'url))))
    (let* ((query-split (s-split " +" query))
           (ret (alist-get (intern (car query-split))
                           index/eww-search-prefix)))
      (eww (if ret
               (s-concat (car ret) (s-join " " (cdr query-split)))
             query))))

  (defun index/eww-mpv-open-link ()
    "Open URL at point in `mpv'."
    (interactive)
    (let ((uris (eww-suggested-uris)))
      (if (null uris) (user-error "No URLs at point.")
        (index/call-process
         `("mpv_open"
           ,(car uris)))
        (message (concat "Launching MPV for "
                         (propertize (car uris) 'face 'success))))))

  (defun index/previous-hr (&optional arg)
    "Move to the next <hr> element."
    (interactive "p")
    (re-search-backward "^ *─+ *$" nil :noerror arg))

  (defun index/next-hr (&optional arg)
    "Move to the next <hr> element."
    (interactive "p")
    (re-search-forward "^ *─+ *$" nil :noerror arg))

  (defun index/eww-image-toggle-animation ()
    "Toggle image animation of current buffer."
    (interactive)
    (if (bound-and-true-p shr-image-animate)
        (setq-local shr-image-animate nil)
      (setq-local shr-image-animate t))
    (eww-reload :local))

  :hook ((eww-mode-hook . olivetti-mode)
         (eww-after-render-hook . +tributi/eww-rename-buffer))
  :bind (("C-x w" . index/eww-search)
         (eww-mode-map
          ("n" . next-line)
          ("p" . previous-line)
          ("M-n" . index/next-hr)
          ("M-p" . index/previous-hr)
          ("g" . index/eww-reload)
          ("G" . index/eww)
          ("o" . index/eww-mpv-open-link)
          ("A" . index/eww-image-toggle-animation)
          ("<return>" . eww-open-in-new-buffer))
         (eww-image-link-keymap
          ("o" . index/eww-mpv-open-link))))

9.17. Open Street Map

(leaf solar
  :config
  ;; I store my Latitude and Longitude as environmental variables for it
  ;; to be easily accessable by any program.  For example, `redshift'
  ;; uses these variables.
  (setq calendar-latitude
        (or (string-to-number (getenv "LATITUDE")) 0))
  (setq calendar-longitude
        (or (string-to-number (getenv "LONGITUDE")) 0)))

(leaf osm
  :straight (osm :host github :repo "minad/osm")
  :bind (("C-c M s" . osm-search)
         ("C-c M h" . osm-home)
         ("C-c M j" . osm-bookmark-jump))
  :config (with-eval-after-load 'org
            (require 'osm-ol)))

9.18. Email – notmuch

(leaf smtpmail
  :commands compose-mail
  :require sendmail
  :config
  (setq smtpmail-default-smtp-server "mail.finnsauer.com")
  (setq smtpmail-smtp-server "mail.finnsauer.com")
  (setq smtpmail-stream-type nil)
  (setq smtpmail-smtp-service 587)
  (setq smtpmail-queue-mail nil)

  (setq send-mail-function 'smtpmail-send-it))

(leaf notmuch
  :straight t
  :commands notmuch
  :config
  (setq notmuch-show-logo nil)
  (setq notmuch-column-control 1.0)
  (setq notmuch-hello-recent-searches-max 20)
  (setq notmuch-hello-thousands-separator "")
  (setq notmuch-hello-sections '(notmuch-hello-insert-saved-searches notmuch-hello-insert-alltags))
  (setq notmuch-show-all-tags-list t)

  (setq notmuch-search-oldest-first nil)
  (setq notmuch-search-result-format
        '(("date" . "%12s  ")
          ("count" . "%-7s  ")
          ("authors" . "%-20s  ")
          ("subject" . "%-80s  ")
          ("tags" . "(%s)")))
  (setq notmuch-tree-result-format
        '(("date" . "%12s  ")
          ("authors" . "%-20s  ")
          ((("tree" . "%s")
            ("subject" . "%s"))
           . " %-80s  ")
          ("tags" . "(%s)")))
  (setq notmuch-saved-searches
        `((:name "inbox"
                 :query "tag:inbox"
                 :sort-order newest-first
                 :key ,(kbd "i"))
          (:name "unread (inbox)"
                 :query "tag:unread and tag:inbox"
                 :sort-order newest-first
                 :key ,(kbd "u"))
          (:name "unread all"
                 :query "tag:unread not tag:archived"
                 :sort-order newest-first
                 :key ,(kbd "U"))
          (:name "todo"
                 :query "tag:todo not tag:archived"
                 :sort-order newest-first
                 :key ,(kbd "t"))
          (:name "mailing lists"
                 :query "tag:list not tag:archived"
                 :sort-order newest-first
                 :key ,(kbd "m"))))

  (setq notmuch-archive-tags '("-inbox" "+archived"))
  (setq notmuch-message-replied-tags '("+replied"))
  (setq notmuch-message-forwarded-tags '("+forwarded"))
  (setq notmuch-show-mark-read-tags '("-unread"))
  (setq notmuch-draft-tags '("+draft"))
  (setq notmuch-draft-folder "drafts")
  (setq notmuch-draft-save-plaintext 'ask)

  (defun index/notmuch-poll-and-refresh-this-buffer ()
    "Invoke `mbsync -a' to syncronise all new mails."
    (interactive)
    (shell-command "mbsync -a")
    (notmuch-poll)
    (notmuch-refresh-this-buffer))

  :bind (("C-c m" . notmuch)
         (notmuch-hello-mode-map
          ("G" . index/notmuch-poll-and-refresh-this-buffer))
         (notmuch-search-mode-map
          ("G" . index/notmuch-poll-and-refresh-this-buffer))))

9.19. Calculations in Emacs

Calculations in Emacs are quite simple to do, and you have multiple tools at hand for that specific job:

eval-expression
With this function bound to M-:, you can evaluate anything and, thus, arithmetic expressions: (1+ 10) ;=> 11.
calc
An inbuild tool by Emacs to be meant to do very complex calculations. Always look up the documentation with C-h i u m calc RET.
quick-calc
A simple prompt to the user for a single arithmetic expression. This is useful when in need for a quick calculation—thus the name.

When met with complex equations or expressions, I suggest the usage of calc. As it is a fully featured calculator. Let's show an example:

Start calc via M-x. You are greeted with two windows, the left one being the stack, and the right one being the history. At that point, I suggest to play around a bit; type a few expressions to see how it works.

Following a more complex example: how to solve algebraic formulas. Yes, calc has this functionality, so I can cast my TI-84 Plus CE-T aside. Let's say we have this simple algebraic equation:

x^2 - 22 = 55

When entering this formula into calc with the keybinding ' and its corresponding function calc-algebraic-entry, it already simplefies the equation by putting the 22 to the opposite side; you then see the following equation on the stack:

x^2 = 77

To now solve for x, simply hit a S and enter x. It will solve the equation for x, and print the x-value onto the stack, replacing the old value:

x = 8.77496438739

There are many more examples, but I think this suffices to convince you that calc is an almighty calculation tool for even complex equations. (annex: I'm not a mathematician though!)

But when in need of a quick and easy expression, then quick-calc would be the ideal choice. What is the value of the expression: 2^32? Launch quick-calc with M-x and enter the expession. The result will be printed in the minibuffer's echo area.

This is not what I want. I want the result to be inserted to the point of the current buffer. This is mostly my use-case, and it is easy to simply remove the inserted number. Thus, my function index/calc-dwim does exactly that.

So, the result of the expression 2^32 can be entered easily into quick-calc with H-c 2^32 RET.

(leaf calc
  :straight t
  :config
  (defun index/calc-dwim (&optional arg)
    "Calls `calc' in a Do What I Mean fassion."
    (interactive "P")
    (cond
     ((equal '(4) arg) (calc))
     (:else (quick-calc :insert))))

  :bind (("H-c" . index/calc-dwim)))

10. Utilities

Emacs is known to be more than a text editor. I like to think of Emacs as an Emacs Lisp interpreter with a provided text interface. So applications that can be used in text-only are perfect for Emacs.

Here are those special niches that one would consider not fit to be in a text editor– but those fools haven't truly grasped the beauty of Emacs and its text based interface.

10.1. (News)Feeds

10.1.1. Elfeed

10.1.1.1. Config
(leaf elfeed
  :straight t
  :commands elfeed
  :config
  (setq elfeed-use-curl t)
  (setq elfeed-curl-max-connections 8)
  (setq elfeed-db-directory (concat user-emacs-directory "elfeed"))
  (setq elfeed-enclosure-default-dir "~/www/")
  (setq elfeed-search-filter "@6-months-ago -torrent")
  (setq elfeed-sort-order 'descending)
  (setq elfeed-search-clipboard-type 'CLIPBOARD)
  (setq elfeed-search-title-max-width 72)
  (setq elfeed-search-title-min-width 50)
  (setq elfeed-search-trailing-width 25)
  (setq elfeed-show-truncate-long-urls t)
  (setq elfeed-show-unique-buffers t)

  (defun index/elfeed-load-feeds ()
    "Loads the encryped `feed.el.gpg' file.
Used in combination with `elfeed-search-mode-hook', because it
only gets loaded when it is needed and not on startup."
    (let ((f (expand-file-name "feeds.el.gpg" user-emacs-directory)))
      (when (file-readable-p f)
        (load-file f))))

  (defun index/elfeed-mpv-open-link ()
    "Play entry link with external `mpv' program.
This will launch `mpv' without any buffer."
    (interactive)
    (let* ((entry (if (eq major-mode 'elfeed-show-mode)
                      elfeed-show-entry
                    (elfeed-search-selected :ignore-region)))
           (link (elfeed-entry-link entry)))
      (start-process "mpv" nil "mpv_open" link)
      (save-excursion
        (elfeed-search-untag-all-unread))
      (message
       (concat "Launching MPV for " (propertize link 'face 'success)))))

  (defun index/elfeed-youtube-dl-link ()
    "Download via `youtube-dl'."
    (interactive)
    (let* ((entry (if (eq major-mode 'elfeed-show-mode)
                      elfeed-show-entry
                    (elfeed-search-selected :ignore-region)))
           (link (elfeed-entry-link entry)))
      (start-process "youtube-dl" nil "youtube-dl" link)
      (save-excursion
        (elfeed-search-untag-all-unread))
      (message
       (concat "Launching YOUTUBE-DL for " (propertize link 'face 'success)))))

  (defun index/elfeed-toggle-read-unread ()
    "Used to toggle between read and unread."
    (interactive)
    (let* ((entry (elfeed-search-selected :ignore-region))
           (tags (elfeed-entry-tags entry)))
      (if (member 'unread tags)
          (elfeed-search-untag-all-unread)
        (elfeed-search-tag-all-unread))))

  (defun index/elfeed-toggle-read-unread-all ()
    "Used to toggle all entrys between read and unread."
    (interactive)
    (let* ((entry (elfeed-search-selected :ignore-region))
           (tags (elfeed-entry-tags entry)))
      (if (member 'unread tags)
          (progn
            (save-excursion
              (mark-whole-buffer)
              (elfeed-search-untag-all-unread)))
        (save-excursion
          (mark-whole-buffer)
          (elfeed-search-tag-all-unread)))))

  ;;   (defun index/elfeed-new-entry-notification (entry)
  ;;     "Spawns a notify-send shell process after `elfeed-new-entry'
  ;; Meant to be used in combination with `add-hook' and
  ;; `elfeed-new-entry-hook'"
  ;;     (let* ((title (elfeed-entry-title entry))
  ;;            (name (elfeed-feed-title (elfeed-entry-feed entry)))
  ;;            (link (elfeed-entry-link entry)))
  ;;       (unless (or (string-match "^magnet:" link)
  ;;                   (string-match "\.torrent$" link))
  ;;         (index/call-process
  ;;          `("notify-send" "RSS"
  ;;            ,(concat "Title: " title "\nAuthor: " name "\nLink: " link))))))

  ;; (lexical-let ((state 0)
  ;;               (filter elfeed-search-filter))
  ;;
  ;;   (advice-add 'elfeed-search-clear-filter
  ;;               :after (lambda () (setq state 0)))

  (defun index/elfeed-toggle-between-predef-tags ()
    "Toggle between predefined tags.
This cycles through the following iterations:

0. \"\"
1. \"+unread\"
2. \"+video\"
3. \"-video\"
4. \"+torrent\"

After hitting the last iteration, it will wrap around to the
first."
    (interactive)
    (if (= 4 state)
        (setq state 0)
      (incf state))
    (pcase state
      (0 (elfeed-search-set-filter nil))
      (1 (setq elfeed-search-filter (concat filter " +unread"))
         (elfeed-search-update :force))
      (2 (setq elfeed-search-filter (concat filter " +video"))
         (elfeed-search-update :force))
      (3 (setq elfeed-search-filter (concat filter " -video"))
         (elfeed-search-update :force))
      (4 (setq elfeed-search-filter (s-join
                                     " "
                                     `(,(car (s-split " " filter))
                                       "+torrent")))
         (elfeed-search-update :force))))

  (defun index/elfeed-format-content (entry)
    (let* ((original (elfeed-deref (elfeed-entry-content entry)))
           (replace (with-temp-buffer
                      (insert original)
                      (mark-whole-buffer)
                      (fill-paragraph)
                      (buffer-string))))
      (setf (elfeed-entry-content entry) (elfeed-ref replace))))

  (defun index/elfeed-toggle-star ()
    "Toggle between '*' on entry.
This will add a '*' tag on the current entry."
    (interactive)
    (save-excursion
      (elfeed-search-toggle-all '*)))

  (defun index/elfeed-copy-link-address ()
    "Copy the link address of entry at point."
    (interactive)
    (let* ((entry (if (eq major-mode 'elfeed-show-mode)
                      elfeed-show-entry
                    (elfeed-search-selected :ignore-region)))
           (link (elfeed-entry-link entry)))
      (message (s-concat
                "Copied entry link: "
                (propertize (kill-new link) 'face 'success)))))

  (add-hook 'elfeed-new-entry-hook
            (elfeed-make-tagger :before "2 weeks ago"
                                :remove 'unread))

  :hook ((elfeed-search-mode-hook . index/elfeed-load-feeds)
         ;; (elfeed-new-entry-hook . index/elfeed-new-entry-notification)
         (elfeed-show-mode-hook . olivetti-mode)
         (elfeed-new-entry-hook . index/elfeed-format-content))

  :bind (("C-c f" . elfeed)
         (elfeed-show-mode-map
          ("o" . index/elfeed-mpv-open-link)
          ("TAB" . forward-button))
         (elfeed-search-mode-map
          ("o" . index/elfeed-mpv-open-link)
          ("y" . index/elfeed-youtube-dl-link)
          ("a" . index/elfeed-toggle-read-unread)
          ("A" . index/elfeed-toggle-read-unread-all)
          ("t" . index/elfeed-toggle-between-predef-tags)
          ("m" . index/elfeed-toggle-star)
          ("w" . index/elfeed-copy-link-address))))
10.1.1.2. Update

Since elfeed updates are rather slow by default, I tweaked with them more or less. Well actually I stole some code from Reddit User github-alphapapa.

(leaf elfeed
  :config
  (defvar tributi/elfeed-update-complete-hook nil
    "Functions called with no arguments when `elfeed-update' is
    finished.")

  (defvar tributi/elfeed-updates-in-progress 0
    "Number of feed updates in-progress.")

  (defvar tributi/elfeed-search-update-filter nil
    "The filter when `elfeed-update' is called.")

  (defun tributi/elfeed-update-complete-hook (&rest _)
    "When update queue is empty, run
`tributi/elfeed-update-complete-hook' functions."
    (when (= 0 tributi/elfeed-updates-in-progress)
      (run-hooks 'tributi/elfeed-update-complete-hook)))

  (add-hook 'elfeed-update-hooks
            #'tributi/elfeed-update-complete-hook)

  (defun tributi/elfeed-search-update-restore-filter (&rest _)
    "Restore filter after feeds update."
    (when tributi/elfeed-search-update-filter
      (elfeed-search-set-filter tributi/elfeed-search-update-filter)
      (setq tributi/elfeed-search-update-filter nil)))

  (add-hook 'tributi/elfeed-update-complete-hook
            #'tributi/elfeed-search-update-restore-filter)

  (defun tributi/elfeed-search-update-save-filter (&rest _)
    "Save and change the filter while updating."
    (setq tributi/elfeed-search-update-filter elfeed-search-filter)
    (setq elfeed-search-filter "#0"))

  ;; NOTE: It would be better if this hook were run before starting the
  ;; feed updates, but in `elfeed-update', it happens afterward.
  (add-hook 'elfeed-update-init-hooks
            #'tributi/elfeed-search-update-save-filter)

  (defun tributi/elfeed-update-counter-inc (&rest _)
    (cl-incf tributi/elfeed-updates-in-progress))

  (advice-add
   #'elfeed-update-feed
   :before
   #'tributi/elfeed-update-counter-inc)

  (defun tributi/elfeed-update-counter-dec (&rest _)
    (cl-decf tributi/elfeed-updates-in-progress)
    (when (< tributi/elfeed-updates-in-progress 0)
      ;; Just in case
      (setq tributi/elfeed-updates-in-progress 0)))

  (add-hook 'elfeed-update-hooks
            #'tributi/elfeed-update-counter-dec)

  ;; (run-at-time nil (* 10 60) #'elfeed-update)
  )

10.1.2. ERC

(leaf erc
  :config
  (setq erc-echo-notices-in-minibuffer-flag t)
  (setq erc-rename-buffers t)
  (setq erc-interpret-mirc-color t)
  (setq erc-server-auto-reconnect t)
  (setq erc-server-reconnect-timeout 15)
  (setq erc-prompt-for-nickserv-password nil)
  (setq erc-fill-function 'erc-fill-static)
  (setq erc-fill-static-center 12)
  (setq erc-track-switch-direction 'newest)

  (defun index/erc-dwim ()
    "Switch to `erc-mode' buffer(s) or log into IRC."
    (interactive)
    (let ((irc "irc.freenode.net")
          (nick "xinjyz")
          (pass (shell-command-to-string "pass irc.freenode.net/xinjyz"))
          (buffers (erc-buffer-list)))
      (cond ((eq 1 (length buffers))    ; switch to the only buffer
             (switch-to-buffer
              (nth
               (cl-position
                'erc-mode
                (--map (with-current-buffer it
                         major-mode)
                       (buffer-list)))
               (buffer-list))))
            (buffers
             (erc-switch-to-buffer))
            (t
             (erc :server irc
                  :nick nick
                  :password pass
                  :full-name nick)))))
  :hook ((erc-mode-hook . erc-fill-enable)
         (erc-mode-hook . erc-track-mode))
  :bind (("C-c i" . index/erc-dwim)))

10.2. Eshell

Eshell gives you everything from a traditional shell. It integrates with elisp very nicely. You can do something like echo (propertize "Hello, EShell" 'face 'success). Of course there is way more powerful ways you can utilize this functionality. One thing that's different is how you write do blocks in eshell: for i in one two three { echo $i }. And $(...) is not like in bash as a subshell execution, but it is an elisp evaluation. If you want this functionality you can use ${...} as shell evaluation. There is a great manual for eshell in the info buffer. Just go there by typing C-h i C-s eshell RET or M-x info-display-manual eshell RET.

(leaf eshell
  :straight t
  :require esh-mode
  :commands index/eshell-dwim
  :config
  (defun tributi/call-process (command &rest args)
    "Execute COMMAND with ARGS synchronously.
Returns (STATUS . OUTPUT) when it is done, where STATUS is the returned error
code of the process and OUTPUT is its stdout output."
    (with-temp-buffer
      (cons (or (apply #'call-process command nil t nil (remq nil args))
                -1)
            (string-trim (buffer-string)))))

  (defun tributi/eshell--current-git-branch ()
    (cl-destructuring-bind (status . output)
        (tributi/call-process "git" "symbolic-ref" "-q" "--short" "HEAD")
      (if (equal status 0)
          (format " [%s]" output)
        (cl-destructuring-bind (status . output)
            (tributi/call-process "git" "describe" "--all" "--always" "HEAD")
          (if (equal status 0)
              (format " [%s]" output)
            "")))))

  (defvar index/eshell-time-command nil
    "Stores the starting time of the command.")

  (defun index/eshell-time-command (&optional end)
    (if end
        (if index/eshell-time-command
            (format " %s" (->> index/eshell-time-command
                               time-since
                               float-time
                               seconds-to-string))
          " 0s")
      (setq index/eshell-time-command (current-time))))

  (setq eshell-prompt-function
        (lambda ()
          (concat
           " "
           (let ((s (->> default-directory
                         (s-split "/")
                         reverse
                         cadr)))
             (if (string= user-login-name s) "~" s))
           (propertize
            (tributi/eshell--current-git-branch)
            'face
            'shadow)
           (propertize
            (index/eshell-time-command 'end)
            'face
            'shadow)
           (propertize
            " λ"
            'face
            (if (zerop eshell-last-command-status)
                'success
              'error))
           " ")))

  (setq eshell-prompt-regexp (rx bol (* (not "λ")) "λ "))

  ;; Print to stdout instead, thus we can use Emacs commands and
  ;; notions.
  (setenv "PAGER" "cat")

  (defun index/eshell-smartparens-mode ()
    "Calls `smartparens-mode' in eshell.
  Intended as a hook call, since eshell doesn't seem to respect
  `smartparens-global-mode'."
    (smartparens-mode))

  (defun index/eshell-dwim ()
    "Do what I mean function for `eshell'.
When in a buffer with a valid filename, change to that
directory (plus list directly) and run `eshell'.  Otherwise just
run `eshell'."
    (interactive)
    (unless (get-buffer "*eshell*")
      (with-current-buffer (get-buffer-create eshell-buffer-name)
        (eshell-mode)))

;;; TODO: This code snippet is very unclean and, dare I say, brute
;;; forcing into the whished possibility.  It was from my earlier
;;; times with Emacs Lisp.
    ;; (let ((buf (buffer-file-name (current-buffer))))
    ;;   (if buf
    ;;       (with-current-buffer eshell-buffer-name
    ;;         (end-of-buffer)
    ;;         (eshell-kill-input)
    ;;         (insert
    ;;          (concat
    ;;           "cd \""
    ;;           (file-name-directory buf)
    ;;           "\"; ls"))
    ;;         (eshell-send-input)
    ;;         (eshell))
    ;;     (eshell)))
    (eshell))

  (defmacro index/eshell-ffap (name doc &rest body)
    (declare (indent 1))
    `(defun ,(intern (concat "index/eshell-ffap-"
                             (symbol-name name)))
         nil ,doc (interactive)
         (if-let ((file (ffap-file-at-point)))
             (progn ,@body)
           (user-error "No file at point"))))

  (index/eshell-ffap find-file
    "Find file at point and go to it."
    (find-file file))

  (index/eshell-ffap dired-jump
    "Open dired from file at point."
    (dired (file-name-directory file)))

  (index/eshell-ffap kill-ring-save
    "Save the absolute file path to the kill ring."
    (kill-new (expand-file-name file))
    (message (concat "Stored "
                     (propertize file 'face 'success)
                     " inside the kill ring.")))

  (index/eshell-ffap cd
    "Change directory to FILE."
    (if (not (file-directory-p file))
        (user-error "Not a directory at point.")
      (delete-region eshell-last-output-end (point-max))
      (when (> eshell-last-output-end (point))
        (goto-char eshell-last-output-end))
      (insert-and-inherit "cd " (eshell-quote-argument file))
      (eshell-send-input)))

  (index/eshell-ffap mpv
    "Open FILE with `mpv_open'."
    (if (file-directory-p file)
        (user-error "Not a file at point.")
      (delete-region eshell-last-output-end (point-max))
      (when (> eshell-last-output-end (point))
        (goto-char eshell-last-output-end))
      (insert-and-inherit "mpv_open " (eshell-quote-argument file))
      (eshell-send-input)))

  :hook ((eshell-mode-hook . index/eshell-smartparens-mode)
         (eshell-pre-command-hook . index/eshell-time-command))
  :bind (("C-c s" . index/eshell-dwim)
         (eshell-mode-map
          ("C-c C-f" . index/eshell-ffap-find-file)
          ("C-c C-j" . index/eshell-ffap-dired-jump)
          ("C-c C-w" . index/eshell-ffap-kill-ring-save)
          ("C-c C-m" . index/eshell-ffap-mpv))
         (eshell-proc-mode-map
          ("C-c C-d" . index/eshell-ffap-cd))))

10.2.1. Aliases

Aliases in Eshell are a bit different in comparison to bash. Yes, you can still define aliases in the prompt the same way: alias name 'function $n'. This will store and enable the alias. Where it will be stored will be printed in the minibuffer.

But I like to keep my configuration in one place; here. Eshell has a great feature: if you define a function with the namespace eshell/ it will be autoloaded as a command in eshell with the name after the /, thus you can define custom made functions easily.

(leaf eshell
  :config
  (defun eshell/f (file)
    (find-file file))

  (defun eshell/ll (&optional file)
    (eshell/ls "-Al" file))

  (defun eshell/c ()
    (eshell/clear :all)
    (setq index/eshell-time-command nil)
    (eshell/echo "Cleared the Eshell buffer.  No more going back!\n\n"))

  (advice-add 'eshell/cd :after (lambda (&optional _) (eshell/ls)))

  (defun eshell/tcd (&optional directory)
    "Like regular 'cd' but don't jump out of a tramp directory."
    (if (file-remote-p default-directory)
        (with-parsed-tramp-file-name default-directory nil
          (eshell/cd
           (tramp-make-tramp-file-name
            method user nil host nil (or directory "") hop)))
      (eshell/cd directory)))

  (defun eshell/root ()
    "Upgrade to root."
    (let* ((pwd-lst (s-split "\\(:\\||\\)" (eshell/pwd)))
           (host (cadr pwd-lst))
           (pwd (car (last pwd-lst))))
      (if host
          (eshell/cd (concat "/ssh:" host "|sudo:" host ":" pwd))
        (eshell/cd (concat "/sudo:root@localhost:" pwd))))))

10.3. Git - The Stupid Content Tracker

10.3.1. Magit

World's most powerful git interface.

(leaf magit
  :straight t
  ;; `index/magit-status-emacs' calls `magit-status' internally, so it
  ;; should be fine to omit it here.
  :commands (magit-status magit-edit-line-commit index/find-config-file)
  :config
  (setq vc-follow-symlinks t)
  (setq git-commit-summary-max-length 50)

  (defvar index/magit-repository-directories
    '("~/web/" "~/lin/" "~/wtn/" "~/rel/")
    "Directories with a repository.")

  (defun index/magit-status-emacs (&optional arg)
    "Finds the Emacs repository.
If called with the universal argument prompt for a repository
from the variable `index/magit-repository-directories'."
    (interactive "P")
    (if arg
        (magit-status
         (completing-read
          "Repository: "
          index/magit-repository-directories))
      (magit-status "~/dot/emacs/")))

  ;; TODO : Move it outside this src block.
  ;; This function is super convenient.  Have your point be on a line
  ;; you want to change, then invoke this function, and then magit will
  ;; then rebase to the commit that introduced that line.  Form there
  ;; you can change the file and stage [S] them and extend the commit [c
  ;; e] or amend the commit [c a].  After that you can continue the
  ;; rebase and revert to master with the changes taking effect.  If you
  ;; changed your mind you can always cancel the rebase [r c].
  (put 'magit-edit-line-commit 'disabled nil)

  :hook ((after-save-hook . magit-after-save-refresh-status))
  :bind (("C-c g" . magit-status)
         ("H-g" . index/magit-status-emacs)
         ("C-M-g" . magit-edit-line-commit)))

(leaf ediff
  :commands magit-ediff-dwim
  :config
  (setq ediff-keep-variants nil)
  (setq ediff-make-buffers-readonly-at-startup nil)
  (setq ediff-merge-revisions-with-ancestor t)
  (setq ediff-show-clashes-only t)
  (setq ediff-split-window-function 'split-window-horizontally)
  (setq ediff-window-setup-function 'ediff-setup-windows-plain)

  :hook ((ediff-prepare-buffer-hook . org-show-all)))

(leaf magit-todos
  :straight t
  :commands magit-status
  :after magit
  :config
  (magit-todos-mode 1))

10.3.2. Diff-hl - A Git Diff Gutter

Simple way of displaying the changes in a git directory.

(leaf diff-hl
  :straight t
  ;; The following will allow this block to load when the command is
  ;; issued; `find-file' is used in `index/find-config-file' for
  ;; example.
  :commands find-file
  :config
  (global-diff-hl-mode 1)
  :hook ((magit-pre-refresh-hook . diff-hl-magit-pre-refresh)
         (magit-post-refresh-hook . diff-hl-magit-post-refresh)))

10.3.3. Dired Version Root Jump

Refer to this section for the regular dired jump accessed via H-j.

(leaf emacs
  :config
  (defun index/dired-vc-root-jump ()
    "Jump to root version control directory."
    (interactive)
    (let ((dir (vc-root-dir)))
      (if dir
          (dired (vc-root-dir))
        (progn
          (message "No VC root dir found.  You can use j to invoke `dired-jump'.")
          (set-transient-map
           (let ((map (make-sparse-keymap)))
             (define-key map (kbd "j") #'dired-jump)
             map))))))
  :bind (("H-M-j" . index/dired-vc-root-jump)))

10.4. TODO Bongo

(leaf bongo
  :straight t
  :config
  (setq bongo-insert-whole-directory-trees t)
  (setq bongo-default-directory (expand-file-name "~/snd"))
  (setq bongo-enabled-backends '(mpv))
  (setq bongo-mpv-extra-arguments '("--no-config" "--vo=null"))
  (setq bongo-header-line-mode nil)

  (leaf bongo
    :config
    (defun index/bongo-add-dired-files ()
      "Add marked files to Bongo library."
      (interactive)
      (let (file-point file (files nil))
        (dired-map-over-marks
         (setq file-point (dired-move-to-filename)
               file (dired-get-filename)
               files (append files (list file)))
         nil t)
        (save-excursion
          ;; Is this always safe or can there be more than
          ;; one Bongo buffer?
          (set-buffer bongo-default-library-buffer-name)
          (mapc 'bongo-insert-file files))
        (if file
            (message "File added: %s" file)
          (message "Files added: %s" files))))
    :bind (("<f8>" . index/bongo-add-dired-files))))

10.5. TODO Transmission

This is a substitute for the program tremc.

(leaf transmission
  :straight t
  :config
  (defun index/transmission-mpv-open ()
    "Open a torrent file via `mpv_open'"
    (interactive)
    (cond
     ((eq major-mode 'transmission-mode)
      (save-excursion
        (transmission-files)
        (index/call-process
         `("mpv_open"
           ,(expand-file-name
             (transmission-files-file-at-point))))
        (quit-window)))
     ((eq major-mode 'transmission-files-mode)
      (index/call-process
       `("mpv_open"
         ,(or (and (file-exists-p (expand-file-name
                                   (transmission-files-file-at-point)))
                   (expand-file-name (transmission-files-file-at-point)))
              (and (file-exists-p (expand-file-name
                                   (s-replace
                                    "/done/" "/working/"
                                    (transmission-files-file-at-point))))
                   (expand-file-name
                    (s-replace "/done/" "/working/"
                               (transmission-files-file-at-point))))))))
     (t (error "Not in `transmission-mode'"))))

  (defun index/transmission-reannounce-ask (ids)
    "Ask if `transmission-reannounce' should be executed."
    (transmission-interactive (list ids))
    (and (yes-or-no-p "Reannounce torrents?")
         (transmission-reannounce ids)))

  :bind ((transmission-mode-map
          ("o" . index/transmission-mpv-open)
          ("R" . index/transmission-reannounce-ask)
          ("fd" . transmission-set-torrent-download)
          ("fu" . transmission-set-torrent-upload))
         (transmission-files-mode-map
          ("o" . index/transmission-mpv-open))))

11. EXWM - Emacs X Window Manager

The EXWM– Emacs X Window Manager– is a cool idea. Emacs everywhere, now, even as our window manger. This seems too good to be true, and, sadly, it is for me:

This section has been deprecated. Although it will stay here indefenetly, as I believe it can be helpful to some. Below you will find a list where I've written some interesting functions that I deem helpful and, possibly, unique. It would be too sad to see them vanish. Maybe it can be helpful to you.

index/exwm-modifier-passthrough
Parse the beginning of a modifier key to this function, lets say the hyper key "h", and it will tell EXWM when inside an X window, to parse it to Emacs instead of the X window. So you can use all the keys beginning with hyper to be used “normally”.
index/exwm-launch
Little run launcher like rofi and dmenu. Nothing major, but still neat.
index/exwm-launch-mpv
Function for mpv usage. The cool thing is, when the function is called with C-u C-u. It'll call index/exwm-floating-window-mode on it; 'course refer to this function. And the doc string of index/exwm-launch-mpv.
index/exwm-raise-or-spawn
Raise or spawn command based on its class. So when called with the argument "qutebrowser" spawn it, or when it already exists, then simply raise it. Refer to the doc string.

These are the functions I deem quite useful. Of course you're free to roam around.

(leaf exwm
  :straight t
  :require exwm exwm-randr
  :config

  (setq exwm-workspace-show-all-buffers t)
  (setq exwm-workspace-switch-create-limit 2)

  (setq exwm-workspace-number 2)
  (setq exwm-randr-workspace-output-plist '(0 "VGA1" 1 "LVDS-1"))

  (exwm-randr-enable)
  (exwm-enable)

  (defvar index/exwm-polybar-mode-process nil
    "The process of polybar")

  (define-minor-mode index/exwm-polybar-mode
    "Toggle between polybar enabled and disabled."
    :global t
    :init-value t
    :lighter ""
    (or (process-live-p index/exwm-polybar-mode-process)
        (setq index/exwm-polybar-mode-process
              (start-process-shell-command
               "polybar"
               nil
               "polybar exwm")))
    (if index/exwm-polybar-mode
        (index/call-process "polybar-msg cmd show")
      (index/call-process "polybar-msg cmd hide")))

  (defun index/exwm-switch-transparency ()
    "Toggle between transparency.
    Available are 92, 30, and 100.  If toggling to 100, kill picom.
    If toggling to 92, start picom"
    (interactive)
    (pcase (if (< 1 (frame-parameter nil 'alpha))
               (frame-parameter nil 'alpha)
             (round (* 100 (frame-parameter nil 'alpha))))
      (92
       (set-frame-parameter (selected-frame) 'alpha 30)
       (add-to-list 'default-frame-alist '(alpha . 30)))
      (30
       (index/call-process "pkill picom")
       (set-frame-parameter (selected-frame) 'alpha 100)
       (add-to-list 'default-frame-alist '(alpha . 100)))
      (100
       (index/call-process "picom")
       (set-frame-parameter (selected-frame) 'alpha 92)
       (add-to-list 'default-frame-alist '(alpha . 92)))))

  (defun tributi/exwm-update-class ()
    (unless (string-prefix-p "sun-awt-X11-" exwm-instance-name)
      (exwm-workspace-rename-buffer exwm-class-name)))

  (defun index/exwm-modifier-passthrough (modifier)
    "Passthrough all the keys starting with MODIFIER.
Add all the keybindings starting with a modifier key e.g. hyper
'H' to the variable `exwm-input-prefix-keys'.  This will have the
effect that when inside a buffer with the major mode `exwm-mode'
pass the keybindings to Emacs instead of to the X window."
    (dolist (key (with-temp-buffer
                   (describe-buffer-bindings (current-buffer))
                   (let ((tmp nil))
                     (--map (when (s-match (concat "^" modifier "-") it)
                              (setq tmp (cons (car (s-split " " it)) tmp)))
                            (s-split "\n" (buffer-string)))
                     (reverse tmp))))
      (add-to-list 'exwm-input-prefix-keys (car (append (kbd key) nil)))))

  (defun index/exwm-launch (&optional arg)
    "Launch a process in $PATH.
With optional ARG prompt for additional argument to the selected
process."
    (interactive "P")
    (index/call-process
     (concat
      (completing-read
       "Launch in $PATH: "
       (flatten-tree
        (--map (ignore-errors (directory-files-recursively it "."))
               (s-split ":" (getenv "PATH")))))
      (when arg
        (concat
         " "
         (read-string "Arguments: "))))))

  (defvar index/exwm-launch-mpv-dirs
    '("~/vid/" "~/ytb/")
    "Directories where to look at.")

  (defconst index/exwm-launch-mpv-file-ending-regex
    (rx "." (or "mkv" "webm" "mp4" "opus" "flac" "wav") eol))

  (defun index/exwm-launch-mpv (&optional arg)
    "Launch mpv via `mpv_open'.
Raise the mpv buffer.  If called with the universal ARG prompt
for file completion from the directories defined in
`index/exwm-launch-mpv-dirs'.  Will focus the `mpv' buffer, too,
and the selected file will be played.  Called with the universal
ARG twice, toggle `index/exwm-floating-window-mode'.  To specify
where the window will be placed once in floating window mode, set
the mpv flag `--geometry'-- for documentation about this look
into the well documented man page of mpv."
    (interactive "P")
    (cond
     ((equal arg '(4))
      (index/call-process
       `("mpv_open"
         ,(->> index/exwm-launch-mpv-dirs
               (--map (directory-files-recursively
                       it index/exwm-launch-mpv-file-ending-regex
                       nil nil t))
               flatten-tree
               (completing-read "Launch MPV: ")
               expand-file-name)))
      (--when-let (get-buffer "mpv")
        (shell-command
         (s-join
          " "
          '("sleep .1"
            "&&"
            "echo script-binding uosc/next"
            "|"
            "socat - $MPV_SOCKET")))
        (switch-to-buffer it)))
     ((equal arg '(16))
      (with-current-buffer (get-buffer "mpv")
        (index/exwm-floating-window-mode 'toggle))
      (switch-to-buffer (current-buffer)))
     (:else
      (let ((mpv-buf (get-buffer "mpv")))
        (if (string= (buffer-name (current-buffer)) "mpv")
            (if (progn
                  (set-buffer mpv-buf)
                  exwm--floating-frame)
                ;; BUG: does not work with 2 monitors.
                (other-frame 1)
              (exwm-layout-unset-fullscreen)
              (switch-to-buffer (other-buffer (current-buffer) t)))
          (if (progn
                (or mpv-buf (user-error "MPV buffer does not exist"))
                (set-buffer mpv-buf)
                exwm--floating-frame)
              (switch-to-buffer-other-frame mpv-buf)
            (switch-to-buffer mpv-buf)
            ;; (exwm-layout-set-fullscreen)
            ))))))

  (define-minor-mode index/exwm-floating-window-mode
    "Enables EXWM floating window mode.
    This adds removing the mode line locally."
    :init-value nil
    :global nil
    :lighter " EXWM: Floating"
    (when (derived-mode-p 'exwm-mode)
      (if index/exwm-floating-window-mode
          (progn
            (exwm-floating--set-floating exwm--id)
            ;; FIXME : grays out x windows; transparency?
            ;; (exwm-workspace--hide-minibuffer)
            (setq-local mode-line-format nil))
        ;; FIXME : grays out x windows; transparency?
        ;; (exwm-workspace--show-minibuffer)
        (exwm-floating--unset-floating exwm--id)
        (kill-local-variable 'mode-line-format))))

  ;; `index/last-created-exwm-buffer' holds the last created EXWM-Window.
  (prog1 t
    (defvar index/last-created-exwm-buffer nil)
    (defun index/exwm-finish-hook-last-buffer ()
      (setq index/last-created-exwm-buffer (current-buffer)))
    (add-hook 'exwm-manage-finish-hook #'index/exwm-finish-hook-last-buffer))

  (defmacro index/exwm-raise-or-spawn (command keybinding)
    "Raise or spawn COMMAND with KEYBINDING.
This macro has three dwim operations:
1. If window of command does not exist, spawn command.
2. If window of command exist, switch to it.
3. If current buffer is window, switch to last buffer. "
    (let* ((buf-name-fn (intern
                         (concat
                          "+index/exwm-raise-or-spawn-"
                          command
                          "-update-buffer-name")))
           (fn (intern (concat "+index/exwm-raise-or-spawn-" command))))
      `(prog1 t
         (defvar ,fn "")

         (defun ,buf-name-fn ()
           (sleep-for 0.1)
           (setq ,fn (buffer-name index/last-created-exwm-buffer))
           (remove-hook 'exwm-manage-finish-hook #',buf-name-fn))

         (defun ,fn ()
           (interactive)
           ;; Ensure the current window is not in fullscreen, otherwise
           ;; it would not work and be overall messy.
           (exwm-layout-unset-fullscreen)
           (cond
            ((string= (buffer-name (current-buffer)) ,fn)
             (switch-to-buffer (other-buffer (current-buffer) t)))
            ((ignore-errors (get-buffer ,fn))
             (switch-to-buffer ,fn))
            ;; A safety measure, because when this command is evoked
            ;; more than once, it would launch multiple COMMAND.
            ((member ',buf-name-fn exwm-manage-finish-hook)
             (message (concat ,command ": currently launching...")))
            (:else
             (index/call-process ,command)
             (add-hook 'exwm-manage-finish-hook #',buf-name-fn))))
         (global-set-key (kbd ,keybinding) #',fn))))

  (defun index/ewxm-clear-gpg-password ()
    "Clear the GPG password and prompt for entering."
    (interactive)
    (index/call-process "clear-gpg-password"))

  (defun index/exwm-volume-up ()
    (interactive)
    (index/call-process "pamixer -i 5"))
  (add-to-list
   'exwm-input-global-keys
   `(,(kbd "<XF86AudioRaiseVolume>") . index/exwm-volume-up))

  (defun index/exwm-volume-mute ()
    (interactive)
    (index/call-process "pamixer -t"))
  (add-to-list
   'exwm-input-global-keys
   `(,(kbd "<XF86AudioLowerVolume>") . index/exwm-volume-down))

  (defun index/exwm-volume-down ()
    (interactive)
    (index/call-process "pamixer -d 5"))
  (add-to-list
   'exwm-input-global-keys
   `(,(kbd "<XF86AudioMute>") . index/exwm-volume-mute))

  (defun index/exwm-wallpaper (&optional arg)
    (interactive "P")
    ;; TODO: select via dired
    (if arg
        (index/call-process "bwp -wr")
      (index/call-process
       (concat
        "bwp -w "
        (completing-read
         "Wallpaper: "
         (-map
          #'file-name-nondirectory
          (directory-files-recursively
           "~/.cache/wallpapers/walls"
           ".")))))))

  ;; Multimedia -- from i3
  (leaf emacs
    :config
    (global-set-key
     (kbd "s-/")
     #'(lambda ()
         (interactive)
         (index/call-process "mediacontrol toggle")))
    (global-set-key
     (kbd "s-.")
     #'(lambda ()
         (interactive)
         (index/call-process "mediacontrol seek +5")))
    (global-set-key
     (kbd "s-,")
     #'(lambda ()
         (interactive)
         (index/call-process "mediacontrol seek -5")))
    (global-set-key
     (kbd "s->")
     #'(lambda ()
         (interactive)
         (index/call-process "mediacontrol next")))
    (global-set-key
     (kbd "s-<")
     #'(lambda ()
         (interactive)
         (index/call-process "mediacontrol prev"))))

  (defun index/exwm-scratchpad ()
    "An Eshell scratch buffer."
    (interactive)
    (let ((eshell-buffer-name "*eshell scratchpad*"))
      (if (string= (buffer-name (current-buffer)) eshell-buffer-name)
          (switch-to-buffer (other-buffer (current-buffer) t))
        (eshell))))

  (defun index/exwm-display-switch ()
    "Switch between the two displays, if both exist."
    (interactive)
    (and (string= (cdaar (x-display-monitor-attributes-list)) "VGA-1")
         (exwm-workspace-switch
          (if (= exwm-workspace-current-index 0) 1 0))))

  (defun index/exwm-display-swap ()
    "Swap between the two displays, if both exist."
    (interactive)
    (when (string= (cdaar (x-display-monitor-attributes-list)) "VGA-1")
      (let ((buffer-1 (progn (other-frame 1) (current-buffer)))
            (buffer-2 (progn (other-frame 1) (current-buffer))))
        (switch-to-buffer buffer-1)
        (other-frame 1)
        (switch-to-buffer buffer-2))))

  (defun index/exwm-call-on-mpv-fullscreen ()
    "Kill/Spawn picom based on MPV fullscreen."
    (when (and (derived-mode-p 'exwm-mode)
               ;; (exwm-layout--fullscreen-p)
               (string= (buffer-name (current-buffer)) "mpv"))
      ;; (index/call-process "polybar-msg cmd hide")
      (index/call-process "pkill picom")

      (remove-hook 'buffer-list-update-hook
                   #'index/exwm-call-on-mpv-fullscreen)

      (defun +index/exwm-call-on-mpv-fullscreen-toggle ()
        (unless (and (derived-mode-p 'exwm-mode)
                     (string= (buffer-name (current-buffer)) "mpv"))
          (index/call-process "picom")
          (remove-hook 'buffer-list-update-hook
                       #'+index/exwm-call-on-mpv-fullscreen-toggle)
          (add-hook 'buffer-list-update-hook
                    #'index/exwm-call-on-mpv-fullscreen)))

      (add-hook 'buffer-list-update-hook
                #'+index/exwm-call-on-mpv-fullscreen-toggle)))

  (defun index/exwm-init-functions ()
    "Functions to be called with `exwm-init-hook'."
    (prog1 t
      (index/call-process "picom")
      ;; (index/call-process "bwp")
      (index/call-process "xsetroot -cursor_name left_ptr")

      (index/exwm-modifier-passthrough "H")
      (index/exwm-modifier-passthrough "s")))

  (index/exwm-raise-or-spawn "qutebrowser" "s-g")
  (index/exwm-raise-or-spawn "arandr" "s-a")
  (index/exwm-raise-or-spawn "nyxt" "s-n")
  (index/exwm-raise-or-spawn "tor-browser" "s-p")
  (index/exwm-raise-or-spawn "aseprite" "s-A")

  (set-frame-parameter (selected-frame) 'alpha 92)
  (add-to-list 'default-frame-alist '(alpha . 92))

  ;; Whenever EXWM enters into char-mode, my keybindings do not get send
  ;; to the X window (E.g. mpv).  I rather stay in line-mode all the
  ;; time---thus disabling it.  I set it to the function
  ;; `exwm-input--grab-keyboard', instead of the function `ignore', so I
  ;; can use the hook `exwm-input-input-mode-change-hook' for entering
  ;; and exiting fullscreen.
  (fset #'exwm-input--release-keyboard #'exwm-input--grab-keyboard)

  (add-to-list 'exwm-input-global-keys `(,(kbd "s-<f11>") . exwm-reset))
  (add-to-list 'exwm-input-global-keys `(,(kbd "<k-1>") . index/exwm-volume-up))
  (add-to-list 'exwm-input-global-keys `(,(kbd "<k-2>") . index/exwm-volume-down))
  (add-to-list 'exwm-input-global-keys `(,(kbd "<k-3>") . index/exwm-volume-mute))

  :hook ((exwm-init-hook . index/exwm-init-functions)
         (exwm-update-class-hook . tributi/exwm-update-class)
         (buffer-list-update-hook . index/exwm-call-on-mpv-fullscreen))
  :bind (("s-b" . index/exwm-polybar-mode)
         ("s-t" . index/exwm-switch-transparency)
         ("s-d" . index/exwm-launch)
         ("s-m" . index/exwm-launch-mpv)
         ("s-q" . index/ewxm-clear-gpg-password)
         ("s-w" . index/exwm-wallpaper)
         ("s-s" . index/exwm-scratchpad)
         ("s-o" . index/exwm-display-switch)
         ("s-O" . index/exwm-display-swap)
         ("s-<f11>" . exwm-reset)
         ("<XF86AudioLowerVolume>" . index/exwm-volume-down)
         ("<kp-1>" . index/exwm-volume-down)
         ("<XF86AudioRaiseVolume>" . index/exwm-volume-up)
         ("<kp-2>" . index/exwm-volume-up)
         ("<XF86AudioMute>" . index/exwm-volume-mute)
         ("<kp-3>" . index/exwm-volume-mute)))

(leaf exwm-outer-gaps
  :straight (exwm-outer-gaps :host github :repo "lucasgruss/exwm-outer-gaps")
  :after exwm
  :config
  (add-to-list 'exwm-input-global-keys `(,(kbd "s-0") . exwm-outer-gaps-mode))
  (add-to-list 'exwm-input-global-keys `(,(kbd "s-=") . exwm-outer-gaps-increment))
  (add-to-list 'exwm-input-global-keys `(,(kbd "s-+") . exwm-outer-gaps-increment))
  (add-to-list 'exwm-input-global-keys `(,(kbd "s--") . exwm-outer-gaps-decrement))

  (defun index/exwm-outer-gaps-increment ()
    "Increment outer gaps with left, right twofold."
    (interactive)
    (unless (bound-and-true-p index/exwm-outer-gaps-increment)
      (setq index/exwm-outer-gaps-increment 1))
    (setq index/exwm-outer-gaps-increment
          (mod (1+ index/exwm-outer-gaps-increment) 2))
    (when (= index/exwm-outer-gaps-increment 0)
      (exwm-outer-gaps-increment nil))
    (exwm-outer-gaps-increment 0)
    (exwm-outer-gaps-increment 1))

  (defun index/exwm-outer-gaps-decrement ()
    "Decrement outer gaps with left, right twofold."
    (interactive)
    (unless (bound-and-true-p index/exwm-outer-gaps-decrement)
      (setq index/exwm-outer-gaps-decrement 1))
    (setq index/exwm-outer-gaps-decrement
          (mod (1+ index/exwm-outer-gaps-decrement) 2))
    (when (= index/exwm-outer-gaps-decrement 0)
      (exwm-outer-gaps-decrement nil))
    (exwm-outer-gaps-decrement 0)
    (exwm-outer-gaps-decrement 1))

  :bind (("s-0" . exwm-outer-gaps-mode)
         ("s-=" . index/exwm-outer-gaps-increment)
         ("s-+" . index/exwm-outer-gaps-increment)
         ("s--" . index/exwm-outer-gaps-decrement)))

12. Software Development

12.1. Web Development

I do minor web-development that just includes HTML/CSS and no other resources such as JAVASCRIPT. As I only use those two, the packages are rather simple.

12.1.1. Emmet

HTML is a pain to write by hand; and I used LaTeX for a long time. Since writing HTML is so cumbersome, let's fix it. Emmet is popular for editing HTML files: you just write the HTML-Tags in a rather intuitive way, like ol>li.class$$*4 C-j, and it expands to this:

<ol>
  <li class="class01"></li>
  <li class="class02"></li>
  <li class="class03"></li>
  <li class="class04"></li>
</ol>

This is a very quick and less annoying way to write HTML.

(leaf emmet-mode
  :straight t
  :hook ((html-mode-hook . emmet-mode)))

12.1.2. Rainbow

This package shows coloring definition as actual color. So something like #454545 will be shown with its corresponding background.

(leaf rainbow-mode
  :straight t
  :hook (((html-mode-hook css-mode-hook) . rainbow-mode)))

12.1.3. Website

12.1.3.1. Dynamically Update the Emacs Document

I don't want to think about updating the html of this document to be included in my website. It should happen automatically! Well, this is not a new task to us Emacs users. Each time, when I save the config.org buffer, I wan't it to update the html. This, of course, should only happen when it actually changed its content to save on unnecessary computing.

First retrieve the config.org's sha256 and later on compare it to the current loaded buffer. When a change occured, build the html file, put it under the web/layouts/partials directory and store the new sha into the variable.

One hook is required to make it work: index/org-save-buffer-hook. The relevent section: Custom Id.

(leaf org
  :config
  (defvar index/website-config-sha256
    (secure-hash
     'sha256
     (file->string
      (expand-file-name
       "config.org"
       user-emacs-directory)))
    "SHA-256 of `config.org'.")

  ;; TODO: implement async call; so Emacs isn't blocked.
  (defun index/website-config-htmlize ()
    "Export `config.org' into the website.
Only export when the file was modified."
    (interactive)
    (let ((o (expand-file-name "config.org" user-emacs-directory))
          (h (expand-file-name "~/web/layouts/partials/dotemacs.html")))
      (unless (string=
               (secure-hash 'sha256 (with-current-buffer (find-file-noselect o)
                                      (buffer-substring-no-properties
                                       (point-min) (point-max))))
               index/website-config-sha256)
        (with-current-buffer (find-file-noselect o t)
          (org-export-to-file 'html h nil nil nil t nil))
        (setq index/website-config-sha256
              (secure-hash 'sha256 (with-current-buffer (find-file-noselect o)
                                     (buffer-substring-no-properties
                                      (point-min) (point-max))))))))

  :hook ((index/org-save-buffer-hook . index/website-config-htmlize)))
12.1.3.2. Org Mode meets Hugo
(leaf ox-hugo
  :straight t
  :require t
  :commands (org-capture index/org-hugo-auto-export-mode)
  :init
  (setq org-hugo-front-matter-format 'yaml)
  (setq org-log-done-with-time t)
  (setq org-tag-alist
        '(("emacs")
          ("org")))

  (defun index/org-capture-website ()
    "Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."
    (let* ((title (read-from-minibuffer "Post Title: "))
           (fname (org-hugo-slug title)))
      (s-join
       "\n"
       `("\n"
         ,(concat "* TODO " title " %^g")
         ":PROPERTIES:"
         ,(concat ":EXPORT_FILE_NAME: " fname)
         ":END:\n"
         "%i%?\n"))))

  ;; (add-to-list 'org-capture-templates
  ;;              '("b"
  ;;                "Blog post"
  ;;                entry
  ;;                (file+olp "~/web/website.org" "NO-HUGO Blog Posts")
  ;;                (function index/org-capture-website)))

  (defvar index/org-hugo-auto-export-mode-sections '("About")
    "Sections for `index/org-hugo-export-sections' and
  `index/org-hugo-auto-export-mode'")

  (defun index/org-hugo-export-sections ()
    "Export all sections in
  `index/org-hugo-auto-export-mode-sections'"

    (with-current-buffer (find-file-noselect
                          (expand-file-name
                           "~/web/website.org")
                          t)
      (dolist (str index/org-hugo-auto-export-mode-sections)
        (save-excursion
          (beginning-of-buffer)
          (search-forward-regexp
           (concat "^" outline-regexp str "$"))
          (org-hugo-export-wim-to-md)))))

  (define-minor-mode index/org-hugo-auto-export-mode
    "Same as `org-hugo-auto-export-mode' but always export sections
  in `index/org-hugo-auto-export-mode-sections'"
    :global nil
    :lighter ""
    (if index/org-hugo-auto-export-mode
        (progn
          (add-hook 'after-save-hook #'index/org-hugo-export-sections :append :local)
          (org-hugo-auto-export-mode 1))
      (org-hugo-auto-export-mode -1)
      (remove-hook 'after-save-hook #'index/org-hugo-export-sections :local)))

  :bind (("C-c M-e M-b" . org-capture)))

12.2. REVIEW Lisp

As of 2021-02-27 this section is under REVIEW: I feel like this section is out of place and should be integrated elsewhere.

Working with LISP inside Emacs is not hard at all. It only requires slime for Common Lisp and geiser for Scheme. Now we can use C-x C-e and have it be evaluated in its dedicated compiler/interpreter. This makes for a smooth REPL– read-eval-print loop– experience, which supersedes LISP above other programming languages: easily act on the code.

12.2.1. Elisp

12.2.1.1. TODO Lispy - Tree-like Lisp

http://danmidwood.com/content/2014/11/21/animated-paredit.html

(leaf lispy
  :straight t
  :hook ((lisp-interaction-mode-hook . lispy-mode)
         (emacs-lisp-mode-hook . lispy-mode)
         (common-lisp-mode-hook . lispy-mode)
         (scheme-mode-hook . lispy-mode)))
;; (leaf paredit
;;   :straight t
;;   :hook ((lisp-interaction-mode-hook . paredit-mode)
;;          (emacs-lisp-mode-hook . paredit-mode)
;;          (common-lisp-mode-hook . paredit-mode)
;;          (scheme-mode-hook . paredit-mode)
;;          (clojure-mode-hook . paredit-mode)))

12.2.2. Scheme

(leaf geiser
  :straight t
  :config
  (setq geiser-active-implementations '(guile)))

12.2.3. Clojure

(leaf cider
  :straight t)

(leaf clj-refactor
  :straight t
  :hook ((clojure-mode-hook . clj-refactor-mode)))

13. Keyboard and Movement Enhancements

13.1. Extend the Basic Functionality

13.1.1. Move to the Beginning of the Line

I always want a key to be purposeful: in my window manager I have a keybinding to spawn Emacs with s-e. But when Emasc is already spawned, it will be purposeless; an unused key. This should not be the case, so when Emasc is spawned, then instead of spawning it again, focus it. You can see this functionality in the section EXWM.

The same is true in Emacs: when (in this example) already at the beginning, the key C-a becomes useless—you are already at the beginning of the line. So, there must be a way to utilise C-a.

Thus, when the point is at the beginning of the line, move to the first non-whitespace character on the current line– effectively jumping indentation. Now, not being at the beginning of the line, the keybinding can be used “normally”.

Such a simple modification will make your life easier, trust me.

(leaf emacs
  :config
  (defun index/move-beginning-of-line ()
    "Modification of `move-beginning-of-line'.
If the cursor is at the beginning of the line-- column 0-- then
goto the first char in the line, otherwise jump to the next
non-whitespace character.

\\{move-beginning-of-line}"
    (interactive)
    (if (or (not (bolp)) (and (derived-mode-p 'org-mode) (bolp) (eolp)))
        (move-beginning-of-line 1)
      (back-to-indentation)))

  :bind ((index/keys-minor-mode-map
          ("C-a" . index/move-beginning-of-line))))

13.1.2. Move to the End of the Line

As already descibed in this section, when the point is already at the end of the line, moving to the end of the line seems redundant: instead jump to a possible beginning of a comment instead. I've found this to be the most useful “second” command, but if you have any other ideas, then please contact me!

(leaf emacs
  :config
  (defun index/move-end-of-line ()
    "Modification of `move-end-of-line'.
Will move to the end of the line.  When invoked at the end of the
line move to the front of a comment.  Otherwise do nothing."
    (interactive)
    (if (eolp)
        (--when-let (save-excursion
                      (beginning-of-line 1)
                      (comment-search-forward (line-end-position) t))
          (goto-char it)
          (skip-syntax-backward " "))
      (move-end-of-line 1)))
  :bind ((index/keys-minor-mode-map
          ("C-e" . index/move-end-of-line))))

13.1.3. Aligning Multiple Lines

In this document, there are a lot of tools that I use very frequently. This is one of them. Aligning multiple lines depending on the column. The function align-regex already exists, thus, it's just a wrapper around it and has three different callings:

No universal argument
Align columns on the first whitespace that is not the indentation.
One universal argument C-u
Prompt to what to align to. The awnser is stored in a variable for later use.
Two universal arguments C-u C-u
Calls the last stored prompt from the said variable.

This function comes really handy when scripting in bash:

curl -A "${_user_agent}" -s "${_url}" \
    | sed -n "s/.*value='\(http:.*\)'.*/\1/p" \
    | xargs wget -w 60 --random-wait -O - \
    | gunzip - \
    | egrep -v '^(#|$)' \
        > "${_file}"

Now we can call the function index/align-column-dwim with one universal argument while marking the code snippet in a region. When prompted enter the following regular expression: "\\$" and see the magic happen:

curl -A "${_user_agent}" -s "${_url}"         \
    | sed -n "s/.*value='\(http:.*\)'.*/\1/p" \
    | xargs wget -w 60 --random-wait -O -     \
    | gunzip -                                \
    | egrep -v '^(#|$)'                       \
        > "${_file}"
(leaf emacs
  :config
  (defvar index/align-column-dwim "\\(\\s-*\\) "
    "Stores the last used Regular Expression inside the
    `index/align-column-dwim' function.")

  (defun index/align-column-dwim (&optional arg)
    "Aligns the column in a do-what-i-mean fashion.
When called with an active region, then act on it.  With no
region selected, then act on the paragraph.  Called with a
prefix, then prompt for a regexp, otherwise assume space.  If C-u
C-u is the prefix, repeat the last called Regular Expression.
This does not store the space regex.  This only makes sense with
the prefix option.

\\{align-regexp}"
    (interactive "P")
    (let ((regexp (cond
                   ((equal arg '(16))
                    index/align-column-dwim)
                   ((equal arg '(4))
                    (concat
                     "\\(\\s-*\\)"
                     (read-string "Align regexp: ")))
                   (t
                    "\\(\\s-*\\) ")))
          (indent-tabs-mode nil))
      (unless (string= "\\(\\s-*\\) " regexp)
        (setq index/align-column-dwim regexp))
      (save-excursion
        (unless (use-region-p)
          (mark-paragraph))
        (align-regexp (region-beginning) (region-end) regexp))))
  :bind (("C-M-h" . index/align-column-dwim)))

13.1.4. Join Line

I missed the vim J since it was a super easy way to join lines as I used it all the time. Well, this implements that feature.

(leaf emacs
  :config
  (defun index/join-line (&optional arg)
    "Concatenate the next line with the previous one.
This emulates vim behavior."
    (interactive "p")
    (if (region-active-p)
        (join-line nil (region-beginning) (region-end))
      (dotimes (i arg)
        (join-line -1))))
  :bind (index/keys-minor-mode-map
         ("M-j" . index/join-line)))

13.1.5. Common Shortcuts

(leaf emacs
  :bind (("H-f" . find-file)
         ("H-F" . find-file-other-window)
         ("H-d" . dired)
         ("H-D" . dired-other-window)))

13.1.6. Transpose

(leaf emacs
  :config
  (defmacro index/transpose (name textobj)
    "This macro will generate new functions with transpose and
TEXTOBJ.  With these new function you can use the region to
transpose TEXTOBJ at `region-beginning' and `region-end'."
    (declare (indent 2) (doc-string 3))
    `(defun ,name (&optional arg)
       ,(concat "Transpose "
                textobj
                " or swap over active region.")
       (interactive "p")
       (let ((func (intern (format "%s-%s" "transpose" ,textobj))))
         (if (use-region-p)
             (funcall func 0)
           (funcall func arg)))))

  (index/transpose index/transpose-chars "chars")
  (index/transpose index/transpose-words "words")
  (index/transpose index/transpose-lines "lines")
  (index/transpose index/transpose-sentences "sentences")
  (index/transpose index/transpose-paragraphs "paragraphs")
  (index/transpose index/transpose-sexps "sexps")

  :bind (("C-t" . index/transpose-chars)
         ("M-t" . index/transpose-words)
         ("C-M-t" . index/transpose-sexps)
         ("C-x C-t" . index/transpose-lines)
         ("C-x M-t" . index/transpose-sentences)
         ("C-x C-M-t" . index/transpose-paragraphs)))

13.1.7. Insert Primary Selection

When you press <S-insert> you would expect it to insert the primary selection into the Emacs Buffer, but it only invokes the yank command. This little section changes that.

(leaf emacs
  :config
  (defun index/insert-primary (&optional count)
    (interactive "P")
    (dotimes (_ (or (unless (listp count) count) 1))
      (insert-for-yank (gui-get-primary-selection))))
  :bind ((index/keys-minor-mode-map
          ("<S-insert>" . index/insert-primary))))

13.1.8. Kill/Save Line or Region

Same reasoning like in this section. No lazy keybindings!!!

This modifies the basic utility of C-w and M-w. When the region is not active, act on the current line instead of an invisible region.

(leaf emacs
  :config
  (defun index/kill-region (&optional arg)
    "When region is not active, kill the current line."
    (interactive "p")
    (if (region-active-p)
        (kill-region (region-beginning) (region-end))
      (kill-whole-line arg)))

  (defun index/kill-ring-save ()
    "When region is not active, save the current line."
    (interactive)
    (if (region-active-p)
        (kill-ring-save (region-beginning) (region-end))
      (kill-ring-save
       (save-excursion
         (move-beginning-of-line 1)
         (point))
       (save-excursion
         (move-end-of-line 1)
         (point)))))

  :bind ((index/keys-minor-mode-map
          ("C-w" . index/kill-region)
          ("M-w" . index/kill-ring-save))))

13.1.9. Change Numbers on the Fly

To be able to change the number at the point is something I was missing from Vim. To quickly change them with a keybinding can be useful sometimes. Thus, I've implemented this feature into Emacs.

(leaf emacs
  :config
  (defmacro +index/number-increment-or-decrement (sign)
    `(let* ((n (or (thing-at-point 'number :no-properties)
                   (user-error "Not a Number at Point")))
            (s (save-excursion
                 (search-forward-regexp (rx (or " " "\n" "\t" eol)))
                 (search-backward (number-to-string n))))
            (e (save-excursion
                 (goto-char s)
                 (search-forward (number-to-string n)))))
       (save-excursion
         (delete-region s e)
         (goto-char s)
         (insert
          (number-to-string
           (if arg
               (,sign n arg)
             (,sign n 1)))))))

  (defun index/number-increment (&optional arg)
    (interactive "P")
    (+index/number-increment-or-decrement +))

  (defun index/number-decrement (&optional arg)
    (interactive "P")
    (+index/number-increment-or-decrement -))

  :bind (("H-a" . index/number-increment)
         ("H-x" . index/number-decrement)))

13.1.10. Narrow Do What I Mean

To always remember which key I need to press seems kinda obscure to me. I just want to press one key to do the functionality that would be provided by the keybinding prefix C-x n.

(leaf emacs
  :config
  (defun index/narrow-dwim (beg end)
    "Narrow do-what-I-mean function.
Either narrow on the active region BEG and END or on the domain
of the window.  When the buffer is narrowed, then widen it
instead---thus being able to cycle this function."
    (interactive "r")
    (cond
     ((buffer-narrowed-p) (widen))
     ((region-active-p) (narrow-to-region beg end))
     (:else (narrow-to-region (window-start) (window-end)))))

  :bind (("H-n" . index/narrow-dwim)))

13.2. Universal Argument

To be able to use s-u or H-u as a universal argument is great, since if for example a keybinding is mapped to H-i and that function can be called with a universal argument to change some aspects of it, it's hard to press C-u H-i. With H-u H-i you don't have to move your finger away from the modifier key.

(leaf emacs
  :bind (("H-u" . universal-argument)
         ("s-u" . universal-argument)
         (universal-argument-map
          ("H-u" . universal-argument-more)
          ("s-u" . universal-argument-more))))

13.3. Expand Region

(leaf expand-region
  :straight t
  :bind (("M-C-SPC" . er/expand-region)))

13.4. Interactive Edit – iedit

TODO [2022-04-15 Fri]: This function seems overloaded with all the (bol) and (eol) function calls.

In the beginning of my Emacs journey I used multiple cursors. This seemed rather to much for by taste: I didn't need all the features that multiple cursors provided, I only used multiple cursors to edit multiple occurences throughout the buffer.

I found iedit by mere coincedence. I started using lispy as a way of Lisp editing—not disturbing the state of parentheses and many more.

At that point I knew its exitance, but I didn't investigate any further—why should I, I had multiple cursors.

But when I found myself playing around with it, it made more and more sense to me. But there was lacking a proper intergration such as multiple cursor's counterpart: mc/mark-all-dwim. A do what I mean function.

This function has 7 conditions or or conditions—as you could say.

  1. When the point is at the beginning of the line and the region is active, then act on the beginning of the line. Useful when you want to prepend something onto multiple lines.
  2. Same as Number 1, but act on the whole buffer when the region is not active.
  3. Same as Number 1, but act on the end of the line instead of the beginning of the line.
  4. Same as Number 2, but– again– act on the end of the line.
  5. When the region is active, prompt the user for a regular expression to match inside the region.
  6. When the last command was `isearch', then do `iedit' with the last seached term.
  7. When none of the above apply, select the s-expr at point.

Although this function being not as powerful as multiple cursors, it needs some other functions to work well: query-replace does most of the things that iedit cannot do.

'(leaf iedit
  :straight t
  :config
  ;; TODO: Change (bolp) and (eolp) clauses to only work on the
  ;; paragraph.
  (defun index/iedit-dwim (beg end)
    "Execute `iedit' in a Do-What-I-Mean fassion.
BEG and END are the active regions endpoints.

These are the conditions that are checked from top to the bottom:

(1) When the point is at the beginning of the line and the region
is active, then act on the beginning of the line.  Useful when
you want to prepend something onto multiple lines.
(2) Same as Number 1, but act on the whole buffer when the region
is not active.
(3) Same as Number 1, but act on the end of the line instead of
the beginning of the line.
(4) Same as Number 2, but-- again-- act on the end of the line.
(5) When the region is active, prompt the user for a regular
expression to match inside the region.
(6) When the last command was `isearch', then do `iedit' with the
last seached term.
(7) When none of the above apply, select the s-expr at point."
    (interactive "r")
    (cond
     ;; ((and (bolp) (region-active-p))
     ;;  (iedit-start (rx bol (or any "\n")) beg (1+ end))
     ;;  (deactivate-mark))
     ((region-active-p)
      (let ((string (read-string "Edit Regexp in Region: ")))
        (cond
         ((string= string "^")
          (iedit-start (rx bol (or any "\n")) beg (1+ end)))
         ((string= string "$")
          (iedit-start (rx eol "\n") beg (1+ end)))
         (:else (iedit-start string beg end))))
      (deactivate-mark)
      (iedit-next-occurrence 1))
     ((bolp)
      (iedit-start (rx bol (or any "\n")) (point-min) (point-max)))
     ;; ((and (eolp) (region-active-p))
     ;;  (iedit-start (rx eol "\n") beg (1+ end))
     ;;  (deactivate-mark))
     ((eolp)
      (iedit-start (rx eol "\n") (point-min) (point-max)))
     ((and (region-active-p) (= (count-lines beg end) 1))
      (iedit-start (buffer-substring-no-properties beg end)
                   (point-min) (point-max)))
     ((eq last-command 'isearch-exit)
      (iedit-start isearch-string (point-min) (point-max)))
     ((thing-at-point 'sexp)
      (iedit-start (if current-prefix-arg
                       (read-string "Edit Regexp in Buffer: ")
                     (rx symbol-start
                         (eval (thing-at-point 'sexp t))
                         symbol-end))
                   (point-min)
                   (point-max))
      (when (= 0 iedit-occurrence-index)
        (iedit-start (thing-at-point 'sexp t)
                     (point-min)
                     (point-max))))))

  ;; :bind (("H-SPC" . index/iedit-dwim))
  )

(leaf multiple-cursors
  :straight t
  :config (setq mc/always-run-for-all t)
  :bind (("H-SPC" . mc/mark-all-like-this-dwim)))

13.5. Insert an Empty Line

(leaf emacs
  :config
  (defun index/insert-line-below (&optional arg)
    "Insert an empty line below the current line.
If called with a prefix indent, otherwise don't indent."
    (interactive "P")
    (end-of-line)
    (open-line 1)
    (next-line)
    (when arg
      (indent-for-tab-command)))

  (defun index/insert-line-above (&optional arg)
    "Insert an empty line above the current line.
If called with a prefix indent, otherwise don't indent."
    (interactive "P")
    (end-of-line 0)
    (open-line 1)
    (next-line)
    (when arg
      (indent-for-tab-command)))

  :bind ((index/keys-minor-mode-map
          ("C-M-m" . index/insert-line-below)
          ("C-M-o" . index/insert-line-above)
          ("M-RET" . nil)
          )))

13.6. Big Font

(leaf emacs
  :config
  (defcustom index/big-font-value 3
    "Amount to raise the font size by proportionately.  Set in pt."
    :type 'number)

  (defmacro +index/big-font-builder (font sign)
    `(->> (* 10 index/big-font-value)
          (,sign (face-attribute (quote ,font) :height))
          (set-face-attribute (quote ,font) nil :height)))

  (define-minor-mode index/big-font-mode
    "Enlarge the global font by `index/big-font-value'."
    :init-value nil
    :global t
    :lighter " BigFont"
    (when current-prefix-arg
      (setq index/big-font-value
            (string-to-number
             (read-string "New Big Font Value: "))))
    (cond
     (index/big-font-mode
      (+index/big-font-builder default +)
      (+index/big-font-builder variable-pitch +)
      (+index/big-font-builder fixed-pitch +))
     (t
      (+index/big-font-builder default -)
      (+index/big-font-builder variable-pitch -)
      (+index/big-font-builder fixed-pitch -)))
    (when doom-modeline-mode
      (doom-modeline-refresh-font-width-cache)))

  :bind (("C-c C-=" . index/big-font-mode)))

13.7. Buffer

(leaf buffer
  :bind (("H-p" . bury-buffer)
         ("H-b" . consult-buffer)
         ("H-B" . consult-buffer-other-window)))

13.7.1. Ibuffer

(leaf ibuffer
  :bind (("C-x C-b" . ibuffer)))

14. This Document

Here cometh additional information.

14.1. Namespace

In this document I use a strict naming convention.

14.1.1. Index index/

Every function, macro, minor-mode, et cetera start with the index/ namespace. Following the slash / I give them their respective name. This is so I have a clear distinction between my code and Emacs' code. There is never a name collision.

Technically I could query them all and print them out. Or look up the documentation with ^index/. These extra keystrokes when defining some code have a lot of benefits.

This name might get changed to indicis, since this would be more correctly with the Latin use of it. Indicis is the genitive form of index and a genitive shows possession like the English's possessive 's and possessive its.

14.1.2. Tributi tributi/

This has the same method as the above mentioned namespace. The only difference is that these functions, macros, minor-modes, et cetera are imported from other people; the code that I took without changing anything.

Once I gave it just a minor tweak, I convert them into the index/ prefix.

As for now– 2020-12-29– I try to have a comment for every code that I imported pointing to the author/creator or where I got it from.

14.2. Keybindings Table

The way I represent some keybindings are in a table format. In the table I only specify the keybinding and the name of the function and the description are auto filled with a table formula (TBLFM [point to future blog post]).

In the source code of this document you can see the formula below the table. But since this is meant to be read in a formatted way, I specify it here for completion sake:

$2='(key-binding (kbd $1))::$3='(ignore-errors (car (s-split "\n" (documentation (key-binding (kbd $1))))))

You put this– as I stated above– under the table:

#+caption: The function name and description where automatically generated;
#+caption: see [[#h:80d39c8d-709d-4a27-9008-89cfed7d87e7][how]].
| Keybinding | Function Name | Description |
|------------+---------------+-------------|
| C-M-q      |               |             |
#+tblfm: $2='(key-binding (kbd $1))::$3='(ignore-errors (car (s-lines (documentation (key-binding (kbd $1))))))

And then execute the formula via C-c C-c while the point is above the formula:

#+caption: The function name and description where automatically generated;
#+caption: see [[#h:80d39c8d-709d-4a27-9008-89cfed7d87e7][how]].
| Keybinding | Function Name        | Description                      |
|------------+----------------------+----------------------------------|
| C-M-q      | index/auto-fill-mode | Wrapper around ‘auto-fill-mode’. |
#+tblfm: $2='(key-binding (kbd $1))::$3='(ignore-errors (car (s-lines (documentation (key-binding (kbd $1))))))

This is very easy and I highly recommend to look up the table documentation especially the spreadsheet section.