General Config

General settings of my personal Emacs Config, 2021-07-05T11:00:00+02:00

Package System

The idea behind package.el is to be able to download packages and install them. Packages are versioned and have versioned dependencies. Furthermore, this supports built-in packages which may or may not be newer than user-specified packages. This makes it possible to upgrade Emacs and automatically disable packages which have moved from external to core.

(require 'package)
(package-initialize)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(put 'denote-mode 'safe-local-variable (lambda (_) t))

(require 'hideshow)

CUA Mode

Cua-mode is part of GnuEmacs versions 22.1.1 and later (at least).

Cua-mode allows one to use ‘C-v’, ‘C-c’, and ‘C-x’ to paste, copy, and cut the region. Since this conflicts with very important keybindings in Emacs, these CUA bindings are only active when the mark is active. The package does a whole lot more, too: ‘C-z’ to undo, Shift-movement to select, and it includes support for rectangular regions (‘C-RET’ and arrow keys instead of using the `C-x r …’ keys) and registers (instead of using the register commands), and it uses `<tab>’ and `S-<tab>’ to indent and outdent the region. As you can see, it is very powerful!

Note: If region is not active – no visible selection – then ‘C-x’ works as it as it does normally in Emacs (without cua-mode).

On the other hand if the region is active you can use C-S-x (or two rapid C-x C-x) instead C-x to do what C-x normally does in Emacs. The same goes for the other CUA keys.

(cua-mode t)
(setq cua-auto-tabify-rectangles nil) ;; Don't tabify after rectangle commands
(transient-mark-mode 1) ;; No region when it is not highlighted
(setq cua-keep-region-after-copy nil) ;; Standard Windows behaviour

Window-Buffer handling

Read: Demystifying Emacs’s Window Manager Watch: control where buffers are displayed (the display-buffer-alist ) (2024-02-08).

The display-buffer-alist is a powerful user option and somewhat hard to get started with. The reason for its difficulty comes from the knowledge required to understand the underlying display-buffer mechanism.

Here is the gist of what we do with it:

  • The alist is a list of lists.
  • Each element of the alist (i.e. one of the lists) is of the following form:

    (BUFFER-MATCHER FUNCTIONS-TO-DISPLAY-BUFFER OTHER-PARAMETERS)

  • The BUFFER-MATCHER is either a regular expression to match the buffer by its name or a method to get the buffer whose major mode is the one specified. In the latter case, you will see the use of cons cells (like (one . two)) involving the derived-mode symbol (remember that I build Emacs from source, so derived-mode may not exist in your version of Emacs).
  • The FUNCTIONS-TO-DISPLAY-BUFFER is a list of display-buffer functions that are tried in the order they appear in until one works. The list can be of one element, as you will notice with some of my entries.
  • The OTHER-PARAMETERS are enumerated in the Emacs Lisp Reference Manual. Evaluate:
(info "(elisp) Buffer Display Action Alists")

Switching Buffers

There is a subtle but important distinction between displaying a buffer and switching to it. Switching is done with C-x b (or its sibling commands, like C-x 4 b) and it is the default user-facing key binding for switching a window’s buffer.

By default Emacs distinguishes between automatic and manual window switching. If you effect a window switch yourself with C-x b, it’s manual — and exempt from any display action rules you create yourself.

You probably don’t want that. I recommend you set this:

;; Requires Emacs 27+
(setq switch-to-buffer-obey-display-actions t)
(setq switch-to-buffer-in-dedicated-window 'pop)

Now Emacs treats manual buffer switching the same as programmatic switching.

However, it also guards against (some) misbehaving commands you may encounter: those that call out to switch-to-buffer programmatically. That is “against the rules”, as switch-to-buffer is a user-facing command.

(defun my-window-select (window &rest _)
  "Select WINDOW.

        Use this as the `body-function' in a `display-buffer-alist' entry."
  (select-window window)
  )

;; NOTE 2023-03-17: Remember that I am using development versions of
;; Emacs.  Some of my `display-buffer-alist' contents are for Emacs
;; 29+.
;;  (advice-add 'quit-window :around
;;            (lambda (orig &rest args)
;;              (apply orig t args)))
(setq display-buffer-alist
      `(;; no window
        ("\\`\\*Async Shell Command\\*\\'"
         (display-buffer-no-window))
        ("\\`\\*\\(Warnings\\|Compile-Log\\|Org Links\\)\\*\\'"
         (display-buffer-no-window)
         (allow-no-window . t))
        ;; bottom side window
        ("\\*Org \\(Select\\|Note\\)\\*" ; the `org-capture' key selection and `org-add-log-note'
         (display-buffer-in-side-window)
         (dedicated . t)
         (side . bottom)
         (slot . 0)
         (window-parameters . ((mode-line-format . none))))


        ;; bottom buffer (NOT side window)
        ((or . ((derived-mode . flymake-diagnostics-buffer-mode)
                (derived-mode . flymake-project-diagnostics-mode)
                (derived-mode . messages-buffer-mode)
                (derived-mode . backtrace-mode)))

         (display-buffer-reuse-mode-window
          display-buffer-at-bottom)

         (window-height . 0.3)
         (dedicated . t)
         (preserve-size . (t . t)))

        ("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
         nil
         (window-parameters (mode-line-format . none)))


        ("\\*Embark Actions\\*"
         (display-buffer-reuse-mode-window display-buffer-in-side-window display-buffer-below-selected)
         (window-height . fit-window-to-buffer)
         (window-parameters . ((no-other-window . t)
                               (mode-line-format . none))))

        ("\\*vterm\\*"
         (display-buffer-reuse-mode-window
          display-buffer-below-selected)
         (window-height . 0.3)
         (window-parameters . ((no-other-window . t)
                               (mode-line-format . none))))

        ("\\*\\(Output\\|Register Preview\\).*"
         (display-buffer-reuse-mode-window display-buffer-at-bottom))

        ;; below current window
        ("\\(\\*Capture\\*\\|CAPTURE-.*\\)"
         (display-buffer-reuse-mode-window display-buffer-below-selected))

        ("\\*\\vc-\\(incoming\\|outgoing\\|git : \\).*"
         (display-buffer-reuse-mode-window display-buffer-below-selected)
         (window-height . 0.1)
         (dedicated . t)
         (preserve-size . (t . t)))

        ((derived-mode . reb-mode) ; M-x re-builder
         (display-buffer-reuse-mode-window display-buffer-below-selected)
         (window-height . 4) ; note this is literal lines, not relative
         (dedicated . t)
         (preserve-size . (t . t)))
        ((or . ((derived-mode . occur-mode)
                (derived-mode . grep-mode)
                (derived-mode . Buffer-menu-mode)
                (derived-mode . log-view-mode)
                (derived-mode . help-mode) ; See the hooks for `visual-line-mode'
                "\\*\\(|Buffer List\\|Occur\\|vc-change-log\\).*"
                ))
         (display-buffer-reuse-mode-window display-buffer-pop-up-window )
         (dedicated . t)
         (body-function . my-window-select))

        ("\\*\\(Calendar\\|Bookmark Annotation\\|ert\\).*"
         (display-buffer-reuse-mode-window display-buffer-below-selected)
         (dedicated . t)
         (window-height . fit-window-to-buffer))


        ;; NOTE 2023-02-17: `man' does not fully obey the
        ;; `display-buffer-alist'.  It works for new frames and for
        ;; `display-buffer-below-selected', but otherwise is
        ;; unpredictable.  See `Man-notify-method'.
        ((or . ((derived-mode . Man-mode)
                (derived-mode . woman-mode)
                "\\*\\(Man\\|woman\\).*"))
         (display-buffer-same-window))))

We match against one buffer name, Compilation; with one ACTION, display-buffer-reuse-window; and no ALIST settings, so it’s not listed.

(add-to-list 'display-buffer-alist
             '("\\*Compilation\\*"
               display-buffer-reuse-window))

I want info windows in a side bar window; it must be on the right-hand side and in slot 0; the window-width must be 80; and Emacs must set the window no-delete-other-windows window parameter to t.

(add-to-list 'display-buffer-alist
             '("\\*info\\*"
               (display-buffer-in-side-window)
               (side . right)
               (slot . 0)
               (window-width . 100)
               (window-parameters
                (no-delete-other-windows . t))))
(add-to-list 'display-buffer-alist
         '(  (major-mode . dired-mode)
           (display-buffer-reuse-window)
               (side . right)
               (slot . 0)
               (window-width . 120)
               (window-parameters
                (no-delete-other-windows . t))))

Here I insist that Help buffers reuse any existing Help window if such a window exists. And if that is not possible, it must pop up a new window. Furthermore, Emacs cannot use the same (selected) window, and it must use another.

(add-to-list 'display-buffer-alist
             '("\\*Help\\*"
               (display-buffer-reuse-window
                display-buffer-pop-up-window)
               (inhibit-same-window . t)
               )
             )

Printing

lpr is an older command, stemming from the times when printers were often directly connected to Unix systems. However, it came to pass that the "Common Unix Printing System" (CUPS) was developed, providing modern print management on Unix systems. And a little web search revealed that it comes with its own command line command, lp.

The syntax of the two commands varies slightly. lpr usually simply accepts the filename I want to print, while lp accepts a range of options that allow me to control various aspects of the print job, such as the number of copies, the printer, the paper format, etc.

Emacs kindly provides the option to configure the command it uses under the hood. So, instead of lpr, we say we'd rather use lp:

(setq lpr-command "lp")

Since lp and lpr pass different parameters on the command line, it also doesn't make sense to continue using the lpr standard switches for lp. So, we turn them off:

(setq lpr-add-switches nil)

Folding

Outline-indent

The outline-indent package provides code folding based on indentation levels. It is recommended for Python, Haskell, and YAML because it supports an unlimited number of folding levels. For instance, it allows folding an entire function or specific nested blocks within that function, such as if statements inside while loops.

(require 'outline-indent)
(setq outline-indent-ellipsis " ▼")

(add-hook 'python-mode-hook #'outline-indent-minor-mode)
(add-hook 'python-ts-mode-hook #'outline-indent-minor-mode)

Treesit-fold

The treesit-fold package provides Intelligent code folding by using the structural understanding of the built-in tree-sitter parser. Unlike traditional folding methods that rely on regular expressions or indentation, treesit-fold uses the actual syntax tree of the code to accurately identify foldable regions such as functions, classes, comments, and documentation strings.

(require 'treesit-fold)
(setq treesit-fold-line-count-show t)
(setq treesit-fold-line-count-format " ▼")
(set-face-attribute 'treesit-fold-replacement-face nil
                    :foreground "#B0B000"
                    :box nil
                    :weight 'bold)

Kirigami

The kirigami Emacs package provides a unified method to fold and unfold text in Emacs across a diverse set of Emacs modes.

Supported modes include: outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, gfm-mode, outli-mode, embark-collect-mode, vdiff-mode, vdiff-3way-mode, hide-ifdef-mode, vimish-fold-mode, TeX-fold-mode (AUCTeX), fold-this-mode, origami-mode, yafolding-mode, folding-mode, ts-fold-mode, treesit-fold-mode, hs-minor-mode (hideshow), ibuffer-mode ( M-x ibuffer), and profiler-report-mode ( M-x profile-report ).

(require 'kirigami)
(setq kirigami-show-menu-bar t)
(setq kirigami-show-context-menu t)
(kirigami-global-mode 1)

OpenWith

(require 'openwith)
(openwith-mode t)
;;(add-to-list  'mm-inhibit-file-name-handlers 'openwith-file-handler)
(setq openwith-associations
      (list (list (openwith-make-extension-regexp '("pdf"))
                  "evince" '(file))
            (list (openwith-make-extension-regexp '("maff" "mht" "mhtml"))
                  "firefox" '(file))
            (list (openwith-make-extension-regexp '("m4a" "flac" "mp3" "wav"))
                  "vlc" '(file))
            (list (openwith-make-extension-regexp '("avi" "flv" "mov" "mp4"
                                                    "mpeg" "mpg" "ogg" "wmv"))
                  "vlc" '(file))
            (list (openwith-make-extension-regexp '("doc" "docx" "odt"))
                  "libreoffice" '("--writer" file))
            (list (openwith-make-extension-regexp '("ods" "xls" "xlsx"))
                  "libreoffice" '("--calc" file))
            (list (openwith-make-extension-regexp '("odp" "pps" "ppt" "pptx"))
                  "libreoffice" '("--impress" file))
            ))

Use ‘read-key’

Use ‘read-key’ when reading answers to "y or n" questions by ‘y-or-n-p’. Otherwise, use the ‘read-from-minibuffer’ to read the answers.

When reading via the minibuffer, you can use the normal commands available in the minibuffer, and can, for instance, temporarily switch to another buffer, do things there, and then switch back to the minibuffer before entering the character. This is not possible when using ‘read-key’, but using ‘read-key’ may be less confusing to some users.

(setq y-or-n-p-use-read-key t)

JSON

JSON (JavaScript Object Notation) is a subset of JavaScript useful as a format for transferring data from program to program, much like XML.

Read all about it at json.org!

You can decode and encode JSON from Emacs Lisp using json.el. It is part of GNU Emacs since 23.1 (2008).

(require 'json)

Elpy

Elpy is the Emacs Python Development Environment. It aims to provide an easy to install, fully-featured environment for Python development.

Elpy documentation: http://elpy.readthedocs.org/en/latest/index.html Elpy wiki: https://github.com/jorgenschaefer/elpy/wiki

(add-to-list 'auto-mode-alist '("\\.org.txt\\'" . org-mode))
(add-to-list 'auto-mode-alist '("\\.ino\\'" . c-mode))
(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")

Deciding on which system we work

Emacs config switch depending on hostname or operating system: Idea found here: Single dot emacs file and per-computer configuration | SIGQUIT

This is so cool: with those functions, I am able to maintain one single Emacs configuration for all of my hosts. If there is something I want to do or do not on a specific platform or host, those functions allow me to express my restrictions easily:

Usage: (when (my-system-type-is-windows) (do-something) )

;; Check if system is Microsoft Windows
(defun my-system-type-is-windows ()
  "Return true if system is Windows-based (at least up to Win7)"
  (string-equal system-type "windows-nt")
  )

;; Check if system is GNU/Linux
(defun my-system-type-is-linux ()
  "Return true if system is GNU/Linux-based"
  (string-equal system-type "gnu/linux")
  )

Window splitting

The existing split-window-sensibly function always prefers to end up with a horizontal stack of windows (which, rather confusingly, it calls a vertical "split", though the split is horizontal …) over a side-by-side arrangement. It's easy enough to create a function which has the opposite preference, which is essentially just a copy of split-window-sensibly with the preferences reversed:

  (defun split-window-really-sensibly (&optional window)
    (let ((window (or window (selected-window))))
      (or (and (window-splittable-p window t)
               ;; Split window vertically.
               (with-selected-window window
                 (split-window-right))) 
          (and (window-splittable-p window)
               ;; Split window horizontally.
               (with-selected-window window
                 (split-window-below)))
          (and (eq window (frame-root-window (window-frame window)))
               (not (window-minibuffer-p window))
               ;; If WINDOW is the only window on its frame and is not the
               ;; minibuffer window, try to split it vertically disregarding
               ;; the value of `split-height-threshold'.
               (let ((split-height-threshold 0))
                 (when (window-splittable-p window)
                   (with-selected-window window
                     (split-window-right))))))))
  (setq split-window-preferred-function 'split-window-really-sensibly)

RecentF

Recentf is a minor mode that builds a list of recently opened files. This list is automatically saved across sessions on exiting Emacs - you can then access this list through a command or the menu.

(require 'recentf)
(recentf-mode 1)
(setq recentf-max-saved-items 40)
(setq recentf-max-menu-items 40)
(setq recentf-menu-append-commands-flag t)
(setq recentf-menu-filter 'recentf-arrange-by-dir)

https://www.emacswiki.org/emacs/RecentFiles

This mode has been part of GNU Emacs since version 21.

Other

;;(add-hook 'org-mode-hook 'read-only-mode)

(setq safe-local-variable-values
      '((org-image-actual-width . 50)
        ))

By default, Emacs sometimes asks you to type full “yes” or “no” in confirmation prompts. To make all of these accept just “y” or “n”, you can add this line to your Emacs config:

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