Skip to content

lmintmate/emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

Repository files navigation

My literate Emacs configuration

Org-mode

Table of Contents

Introduction

Here begins my literate emacs configuration. I tended to comment my init file a whole lot, so I figured I preferred a better interface for the comments.

Notes on the evolution of my emacs initialisation

I first started off with the .emacs file in my home directory. I had to look for it though amongst all the other hidden files in that directory. So, especially after I found out I could version-control my emacs dotfiles, I switched to .emacs.d/init.el. so that I could have my config in one place. That setup still wasn’t satisfactory enough though. So when I found out I could have my config in an org file, which would allow my comments to truly stand out, I was intrigued and wanted to accomplish that. I initially thought however it’d be too hard to do (I do think that a lot when trying new ways of doing things…). I took the plunge however, and it turned out to be way easier than I thought. For a while I kept most of my config in README.org and used init.el only for the preliminary configs that needed to be loaded before loading README.org with org-babel-load-file. If I wanted however to change a preliminary setting, I had to remember to do this on both files and when I changed README.org, emacs took a little longer to load because it tangled README.el. I thus ended up deciding to put everything in README.org and tangle init.el from it.

On tangling init.el from the org file

I tangle the init.el file by writing on the top of the README.org file the following:

#+property: header-args :tangle init.el

Note that when changing the property headings, we need to use C-c C-c with the cursor on it to refresh its setting (see here).
This configures that the file will be tangled to a file called init.el. This can be triggered manually with C-c C-v t, but can also be done automatically upon saving (see here and here). This uses file local variables set at the end of the org file and said variables need to be declared as safe so that emacs won’t warn about them being unsafe (see here) - the implementation of this can thus be found at the end of this file.

Peculiarities of this configuration

My configuration has some specific traits that differentiate it from others. They are the following:

Doesn’t use use-package by choice

A lot of people use use-package to manage their packages and package settings, especially as it’s supposed to make loading emacs faster. I tried to convert my existing config, which uses the “regular” way, a couple of times, and ended up realizing that use-package isn’t compatible with my way of thinking about my configuration. For one thing, use-package places every setting of a particular package under its config, even if said setting is configuring said package to be used by another package. For example, adding helpful-mode to ibuffer-help-buffer-modes (a setting that sets which buffers are going to be colored with the comment color, as help buffers are) would be placed under the ibuffer config with use-package, but I feel it belongs more under the Helpful config instead, because I see it as configuring Helpful in order to be detected by ibuffer instead of configuring ibuffer to detect Helpful (small but important in terms of classification difference). use-package also thinks of built-in “packages” the same way as external packages, thus (potentially) requiring a construction such as (use-package dired [...] in order to configure dired for example, instead of just setting the various desired options without such a preamble. In short, for these and other reasons, use-package just doesn’t feel all that simple or intuitive for me to use. As for the decreasing loading time aspect of it, I’m not too bothered by the loading time, as long as I open emacs once per day, and keep it open afterwards (I try, but sometimes close it absentmindedly, since I do the same with other editors after I’m done using them).

Doesn’t use alternative package managers by choice

Many people use package management solutions other than the default package.el to install and update packages e.g. quelpa, el-get, straight. All these however, as far as I know, require git, and some of them also require additional programs such as tar, ssl libraries (quelpa), install-info (el-get). This makes emacs harder (if not impossible) to deploy on computers either without git and the additional utilities installed or, if installed, in a location not in the PATH, and thus not detectable by emacs and other applications that read the path (Cmder, for example, includes git for windows in its full version, but it doesn’t seem to be detected by other applications). For this reason I stick to using package.el for my package management ; for the single-file packages I use that aren’t on MELPA, I use some custom code that leverages url-copy-file to download each file and place said files in a folder that gets added to the load-path (see below).

Main configuration

First, let’s make sure the init file will be lexically bound, since all the cool kids do it.

;; -*- lexical-binding: t -*-

Also make this config incompatible with emacs versions lower than 25.1, so that I won’t have to bother with too many backwards compatiblity measures. Tbh, I initially had some reservations about doing this, since the default emacs version on Ubuntu 16.04 LTS and the Linux Mint 18 series (which are supported until 2021) is 24.3 iirc, but I’ve been using a PPA for a while now and on Windows I can download and use the latest stable release just fine, since my current Windows machine is 64bit. Note: I used to use at some point a 32bit Windows pc where for some reason only the i386 release could run, and not the i686 one, and thus the latest version supported there was 24.3, but since I don’t use it anymore, I have even less reason to support versions below 25 in my config.

(when (version< emacs-version "25.1")
  (error "This configuration requires GNU Emacs 25.1 or newer, but you're running %s" emacs-version))

Preliminaries: package management settings

Add MELPA and org repositories

First I add the MELPA and org repositories. The code I use for this is taken from MELPA’s Getting Started section and it accounts for the incompatibility of Windows with Emacs’s https support system, GnuTLS, by using http on Windows if GnuTLS isn’t available. This article might be helpful in case I absolutely have to install GnuTLS on Windows.

(require 'package)
(let* ((no-ssl (and (memq system-type '(windows-nt ms-dos))
                    (not (gnutls-available-p))))
       (proto (if no-ssl "http" "https")))
  (add-to-list 'package-archives (cons "melpa" (concat proto "://melpa.org/packages/")) t)
  (add-to-list 'package-archives (cons "org" (concat proto "://orgmode.org/elpa/")) t))
(package-initialize)

Auto-install packages

Ideas from Aaron Bedra’s Emacs 24 Configuration and here.
The first part of the code defines a variable containing the core packages to be installed ; from this list are absent the packages installed manually, present in the lisp folder, and packages that are only installed under certain conditions (see below). Afterwards, the value of package-selected-packages is set to the value of lmintmate/packages, so that we can benefit from the package-selected-packages feature and its benefits (e.g. autoremoving packages not on the list, clearly setting dependencies as opposed to packages installed by the user e.t.c).

(defvar lmintmate/packages '(color-theme-modern
counsel
dired-icon
dired-recent
free-keys
jump-char
no-littering
parent-mode
rainbow-mode
toc-org
transpose-frame
try
undo-fu
vimrc-mode
;; emacs 24.4 and above
auto-minor-mode
elisp-demos
ivy-rich
markdown-mode
org-cliplink
;; emacs 25.1 and above
el-patch
ryo-modal
helpful
ivy-prescient)
  "Core packages")

(setq package-selected-packages lmintmate/packages)

Here I conditionally add to the value of package-selected-packages some packages that are compatible with only certain OS or emacs versions.

;; Packages to be installed only when a certain executable is on the path

(when (executable-find "git")
  (add-to-list 'package-selected-packages 'magit))

;; Packages for use only on my Linux system

(when (eq system-type 'gnu/linux)
  (add-to-list 'package-selected-packages 'trashed))

;; Packages that require emacs versions above 25.1

(unless (version< emacs-version "25.2")
  (add-to-list 'package-selected-packages 'minions))

;; Packages that require emacs versions 26.1 and above

(unless (version< emacs-version "26.1")
  (add-to-list 'package-selected-packages 'org-superstar))

;; GNU ELPA keyring package for versions below 26.3
(when (version< emacs-version "26.3" )
  (add-to-list 'package-selected-packages 'gnu-elpa-keyring-update))

Finally, populate the package-archive-contents with package-refresh-contents, so that the installation will take place properly with a fresh setup, and install all packages in the package-selected-packages list with package-install-selected-packages, if said command exists.

(unless package-archive-contents
  (message "%s" "Refreshing package database...")
  (package-refresh-contents))

(when (fboundp 'package-install-selected-packages)
  (package-install-selected-packages))

Force package.el to install the latest version of org-mode

Because org-mode is already builtin, it doesn’t get reinstalled automatically from the org-mode repository, as it should. Thus, if I want the latest org-mode version, I would normally have to install it manually from package-list-packages, and only then it would overtake the builtin version (see also here). However trying to start emacs without the latest version of org-mode installed causes emacs to error out saying that org-tempo wasn’t detected, because I require that later in the config and it apparently wasn’t included in the built in emacs version. I thus found here a solution that looks for the latest version of org with a regex and, if it doesn’t find it, proceeds to install it from the org repository. I also add org to the list of selected packages, so that it won’t propose to autoremove it.

;; enforce installing the latest version of org mode
(unless (file-expand-wildcards (concat package-user-dir "/org-[0-9]*"))
(if (y-or-n-p "Do you want to install the latest version of org-mode?")
  (package-install (elt (cdr (assoc 'org package-archive-contents)) 0))
(message "The latest version of org-mode wasn't installed.")))

(add-to-list 'package-selected-packages 'org)

Function to update all packages at once without list-packages

From Noninteractively upgrade all packages - Emacs Stack Exchange.

(defun package-upgrade-all ()
  "Upgrade all packages automatically without showing *Packages* buffer."
  (interactive)
  (package-refresh-contents)
  (let (upgrades)
    (cl-flet ((get-version (name where)
                (let ((pkg (cadr (assq name where))))
                  (when pkg
                    (package-desc-version pkg)))))
      (dolist (package (mapcar #'car package-alist))
        (let ((in-archive (get-version package package-archive-contents)))
          (when (and in-archive
                     (version-list-< (get-version package package-alist)
                                     in-archive))
            (push (cadr (assq package package-archive-contents))
                  upgrades)))))
    (if upgrades
        (when (yes-or-no-p
               (message "Upgrade %d package%s (%s)? "
                        (length upgrades)
                        (if (= (length upgrades) 1) "" "s")
                        (mapconcat #'package-desc-full-name upgrades ", ")))
          (save-window-excursion
            (dolist (package-desc upgrades)
              (let ((old-package (cadr (assq (package-desc-name package-desc)
                                             package-alist))))
                (package-install package-desc)
                (package-delete  old-package)))))
      (message "All packages are up to date"))))

Locale and encoding settings

Set the coding system to utf-8. Needed for Windows.

(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)

No littering

(setq no-littering-etc-directory
      (expand-file-name "config/" user-emacs-directory))
(setq no-littering-var-directory
      (expand-file-name "data/" user-emacs-directory))
(require 'no-littering)

Setting the default frame parameters on Windows

On Linux, Emacs places its frame just fine, but on Windows it places it on the left side and in such a manner that the mode line was hidden below the Windows toolbar, which meant I had to manually resize the frame every single time (needless to say, this got old very quickly). Here is thus some config to place the default frame near the center of the screen and above the Windows toolbar.

;; set frame
(when (eq system-type 'windows-nt)
(setq default-frame-alist '((top . 5) (left . 220) (width . 80) (height . 30))))

Disabling the tool bar

The snippet below disables tool-bar-mode. I placed it this early in the config so that the toolbar won’t be loaded and disabled afterwards, but be disabled from the get-go (I had some glitches with the title screen when I had it further down).

(tool-bar-mode -1)

Setting the font

Here, I’m setting the font and the font size. The default font emacs by itself used on my machine appeals a lot to me, so when I found out it was DejaVu Sans Mono, I decided to put it in the config, in case I move to another computer where the font might suddenly be something else entirely I won’t like. I also set the font size to 14. The default size seems way too small for me, as if I were trying to watch a bunch of ants… On systems different from the one I’m currently on (which is Linux Mint MATE), the fonts might look thicker than they should, to an unappealing degree. This can be solved (on Linux systems at least) by going to Appearance > Fonts, and setting hinting to light instead of full. On Windows, where DejaVu Sans Mono is less likely to be preinstalled, Consolas will be used as a fallback.

(if (eq system-type 'windows-nt)
(if (member "DejaVu Sans Mono" (font-family-list))
(set-face-attribute 'default nil :family "DejaVu Sans Mono" :height 140)
(set-face-attribute 'default nil :family "Consolas" :height 140))
(set-face-attribute 'default nil :family "DejaVu Sans Mono" :height 140))

Also the default fixed pitch font on Windows is really ugly, so set it to Consolas instead there.

(when (eq system-type 'windows-nt)
(set-face-attribute 'fixed-pitch nil :family "Consolas" :height 140))

Setting up my must use theme - Blue Mood theme.

I wasn’t satisfied with the default Adawaita theme (but then who is?). I tried to find another theme, but most of them (even the popular ones) didn’t satisfy my tastes. But when I found Blue Mood, I knew it was the one!
In case you haven’t encountered it (not too unlikely), it’s because it’s a part of the color-theme-modern package, which apparently recreates older themes for Emacs 24+. In the repository I linked just now, you can see all the other included themes too, complete with screenshots, and, of course, the way to apply them to your init file.\ I also modified the fringe color to the same background color as the rest of the theme, as its original color was black, and didn’t fit in too well with the rest of the colorscheme for me, and changed the highlight color, as it had the same color as the one of the region so that I couldn’t distinguish a highlighted region when hl-line-mode was turned on. I also made the secondary selection have that same color, as I didn’t like its default color (this affects the color of the region that appears as selected when it is being edited by invoking org-edit-special). In addition I modified the color of the edited state of a version-controlled file vc-edited-state so that it is more apparent. Finally, I modified the colors of package-status-installed and package-status-dependency, now that they matter more, and also changed package-status-built-in, since I wanted to use its color for the dependencies.

(load-theme 'blue-mood t t)
(enable-theme 'blue-mood)

(set-face-attribute 'fringe nil :background "DodgerBlue4")
(set-face-attribute 'font-lock-negation-char-face nil :foreground "tomato")
(set-face-attribute 'font-lock-variable-name-face nil :foreground "tomato")
(set-face-attribute 'font-lock-doc-face nil :foreground "cyan" :inherit 'unspecified)
(set-face-attribute 'highlight nil :background "#235c94")
(set-face-attribute 'secondary-selection nil :background "#235c94" :foreground nil :inherit 'unspecified)
(set-face-attribute 'package-status-built-in nil :inherit font-lock-comment-face)
(set-face-attribute 'package-status-dependency nil :inherit font-lock-builtin-face)
(set-face-attribute 'package-status-installed nil :inherit font-lock-function-name-face)
(set-face-attribute 'vc-edited-state nil :background "tomato1" :foreground "black" :box '(:line-width 2 :color "tomato1"))
;; setting so that hl-line-mode won't affect syntax coloring
(set-face-foreground 'highlight nil)

Mode line customisation

I give a flat look to the mode line, to make it look more modern. I also make it look thicker, by putting a box with line-width 2 and color same the foreground around it (trick borrowed from Beautifying the Mode Line - Elsa Gonsiorowski). In order to keep that look uniform I also styled this way not only both active and inactive mode lines, but also the vc-edited-state (see above).

(set-face-attribute 'mode-line nil :background "grey75" :foreground "black" :box '(:line-width 2 :color "grey75"))
(set-face-attribute 'mode-line-inactive nil :background "grey30" :foreground "grey80" :box '(:line-width 2 :color "grey30"))
(set-face-attribute 'mode-line-highlight nil :box '(:line-width 1 :color "grey20"))
(set-face-attribute 'mode-line-buffer-id nil :weight 'normal :foreground nil :background nil)

Hide line between fringes

Remove the strange white line between two fringes, which appears when the scrollbar is hidden (from Dennis Ogbe’s Emacs configuration file).

(set-face-attribute 'vertical-border nil :foreground (face-attribute 'fringe :background))

Manually loading external files

I use a couple external lisp files, and I thus need to load my personal lisp directory.

Where I bootstrap the elisp files

In this section I have some code in emacs lisp that downloads the lisp files I use and places them in the correct place. Specifically, it checks whether the files exist, and if they don’t, first creates the containing directory, if it doesn’t exist, and then proceeds to download and store the files.

(setq lisp-directory (concat user-emacs-directory "lisp"))

(unless (file-directory-p lisp-directory) (make-directory lisp-directory))

;; in addition to greek.el, also download the byte-compiled greek.elc
(unless (file-exists-p (expand-file-name "greek.el" lisp-directory))
    (url-copy-file "http://myria.math.aegean.gr/~atsol/emacs-unicode/greek.el" (expand-file-name "greek.el" lisp-directory)))
(unless (file-exists-p (expand-file-name "greek.elc" lisp-directory))
    (url-copy-file "http://myria.math.aegean.gr/~atsol/emacs-unicode/greek.elc" (expand-file-name "greek.elc" lisp-directory)))

;; byte-compile .el files after downloading them
(unless (file-exists-p (expand-file-name "lacarte.el" lisp-directory))
    (url-copy-file "https://www.emacswiki.org/emacs/download/lacarte.el" (expand-file-name "lacarte.el" lisp-directory)))
(unless (file-exists-p (expand-file-name "lacarte.elc" lisp-directory))
(byte-compile-file (expand-file-name "lacarte.el" lisp-directory)))

(unless (file-exists-p (expand-file-name "elispfl.el" lisp-directory))
   (url-copy-file "https://github.com/raw/lmintmate/elispfl/master/elispfl.el" (expand-file-name "elispfl.el" lisp-directory)))
(unless (file-exists-p (expand-file-name "elispfl.elc" lisp-directory))
(byte-compile-file (expand-file-name "elispfl.el" lisp-directory)))

Where I tell Emacs where is my personal lisp directory.

(add-to-list 'load-path lisp-directory)

Where I load the specific packages

In this section, I load the lisp files previously downloaded.

Loading the package for polytonic greek

I’m used to writing Greek with the modern Greek layout, which is quite different from the greek-babel polytonic layout in a way jarring to me. I tried to find a way to solve my problem, and found out with relief that I didn’t need to reinvent the wheel, as someone had already made a package for what I wanted ; a layout that would provide polytonic Greek while also keeping the regular keyboard layout I was used to.
This solution to my problem can be found here (look under the compiled greek.elc link for the greek.el source).

;; load elisp file, use byte compiled version (.elc) if it exists
(load "greek")

Loading La Carte (for executing menu commands from the keyboard)

La Carte is a package that allows searching and executing menu commands from the keyboard, in a way similar to ivy (in fact, when ivy is installed, this package also benefits from ivy integration, which makes its autocompletion so much better). There is also a builtin in emacs command tmm-menubar, but that one goes literally from menu to submenu, just with keyboard shortcuts instead of mouse clicks, and is thus much less discoverable. I also set up here a keybinding for lacarte (specifically for lacarte-execute-menu-command, because lacarte-execute-command also includes regular commands, and counsel-M-x already takes care of that).

(require 'lacarte)
(global-set-key (kbd "\C-c.") 'lacarte-execute-menu-command)

Loading elispfl (extra font-lock for emacs lisp)

elispfl is a package with additional syntax highlighting for emacs lisp mode (which notably also fontifies the contents of org src blocks for some reason), not on MELPA. Comparing it with other similar packages, like lisp-extra-font-lock and morlock, I think I prefer the stylistic choices of this one more. I use my own fork of it, because I set it to work on lisp-interaction-mode (the mode of the scratch buffer) as well, and also added an option to propertize the face names based on the faces themselves.

(require 'elispfl)

(with-eval-after-load 'elisp-mode
  (elispfl-mode))
;; Highlight face name by the face itself
(setq elispfl-face-use-itself t)

Startup screen and scratch buffer options

Inhibit startup screen

At this point I only use the quick link to the Customize interface, so I thought I’d hide it altogether.

(setq inhibit-startup-screen t)

Scratch buffer message

If the fortune executable can be found, supply a random fortune cookie as the scratch message (found from EmacsWiki: Fortune). Otherwise, use the builtin-in lisp library cookie1. This library can retrieve random phrases from fortune cookie style files. The type of phrase files it accepts is similar to the default form of fortune files, with the variation that it accepts either % or %% as the delimiter between cookies and needs an empty final line after the final delimiter, while the fortune program doesn’t require this and works fine without said empty final line (see here). The function normally used to insert cookies is cookie-insert. I don’t like however that this function by default adds new lines unnecessarily, so I redefine it without the new lines as lmintmate/cookie-insert. Both the cookie-insert function and my customised variant have to have the phrase file to be used (here named apofthegmata.txt, and located in the user-emacs-directory) as an argument. In case that file isn’t present (e.g. if the code for downloading it fails for whatever reason), show a custom fortune-style message to avoid erroring out.
Worth noting: The cookie1 method is very satisfactory, but its only problem is that it doesn’t respect the lines of the phrase file (that is, it puts some stuff that is on separate lines on the same line). Until I figure out how to solve this, I’m keeping the shell-command "fortune" method around, even though I would rather not depend on an external program for this, since there is a builtin library that does (almost) the same.

(unless (executable-find "fortune")
(unless (file-exists-p (concat user-emacs-directory "apofthegmata.txt"))
(url-copy-file "https://gitlab.com/snippets/1870200/raw" (concat user-emacs-directory "apofthegmata.txt")))
(require 'cookie1)
(defun lmintmate/cookie-insert (phrase-file &optional count startmsg endmsg)
  (setq phrase-file (cookie-check-file phrase-file))
  (let ((cookie-vector (cookie-snarf phrase-file startmsg endmsg)))
    (cookie-shuffle-vector cookie-vector)
    (let ((start (point)))
      (cookie1 (min (- (length cookie-vector) 1) (or count 1)) cookie-vector)
      (fill-region-as-paragraph start (point) nil)))))

(if (executable-find "fortune")
   (setq initial-scratch-message
         (with-temp-buffer
           (shell-command "fortune" t)
           (let ((comment-start ";;"))
             (comment-region (point-min) (point-max)))
           (concat (buffer-string))))
(if (file-exists-p (concat user-emacs-directory "apofthegmata.txt"))
(setq initial-scratch-message
(with-temp-buffer
           (lmintmate/cookie-insert
(concat user-emacs-directory "apofthegmata.txt"))
           (let ((comment-start ";;"))
             (comment-region (point-min) (point-max)))
           (concat (buffer-string) "\n")))
(setq initial-scratch-message (concat ";; Είς οιωνός άριστος, αμύνεσθαι περί πάτρης." "\n"))))

I decided not to change the scratch buffer’s major mode however, because, as weird as it may sound, I like prefix notation a lot (and think that the Reverse Polish notation is overrated in comparison), and want to keep having it as a nifty little prefix calculation mode.

New message for the startup echo area

(defun display-startup-echo-area-message ()
  (message "Καλωσήλθες!"))

Nationality parameters

Setting the calendar up in Greek.

See also EmacsWiki: Calendar Localization.

(setq calendar-week-start-day 1
          calendar-day-name-array ["Κυριακή" "Δευτέρα" "Τρίτη" "Τετάρτη"
                                   "Πέμπτη" "Παρασκευή" "Σάββατο"]
          calendar-month-name-array ["Ιανουάριος" "Φεβρουάριος" "Μάρτιος"
                                     "Απρίλιος" "Μάιος" "Ιούνιος"
                                     "Ιούλιος" "Αύγουστος" "Σεπτέμβριος"
                                     "Οκτώβριος" "Νοέμβριος" "Δεκέμβριος"])

Set input method to greek

In order to be able to write greek with the keyboard set to English (useful for those pesky Latin C- and M- shortcuts). set-input-method makes the set input method the default when emacs starts up, which usually isn’t desired. Contrarily setq default-input-method sets the input method as available with toggle-input-method, but doesn’t make it the default when emacs starts up.

(setq default-input-method "el_GR")
(global-set-key (kbd "C-/") 'toggle-input-method)

Redirect commands with Greek letters to the correct commands

Sometimes I forget to switch the keyboard language from Greek to English (especially when I’m using emacs in tandem with other applications that require the keyboard be set to Greek in order to write in that language) and, as a result, I get something like «M-χ is undefined». I used to use this method from Stack Overflow, which added bindings with Greek letters and told emacs to consider them equivalent to the ones with latin letters. This however didn’t work for commands which had not only a modifier and a letter, but also additional letters (e.g. C-c u). Since the last time I checked this Stack Overflow thread however, a new method has been added that also works for the commands the other method didn’t. I modified it accordingly and will use that one from here on out.

(defun reverse-input-method (input-method)
  "Build the reverse mapping of single letters from INPUT-METHOD."
  (interactive
   (list (read-input-method-name "Use input method (default current): ")))
  (if (and input-method (symbolp input-method))
      (setq input-method (symbol-name input-method)))
  (let ((current current-input-method)
        (modifiers '(nil (control) (meta) (control meta))))
    (when input-method
      (activate-input-method input-method))
    (when (and current-input-method quail-keyboard-layout)
      (dolist (map (cdr (quail-map)))
        (let* ((to (car map))
               (from (quail-get-translation
                      (cadr map) (char-to-string to) 1)))
          (when (and (characterp from) (characterp to))
            (dolist (mod modifiers)
              (define-key local-function-key-map
                (vector (append mod (list from)))
                (vector (append mod (list to)))))))))
    (when input-method
      (activate-input-method current))))

(reverse-input-method 'el_GR)

Custom modal config with ryo-modal

I rather like modal editing, because it allows me to do the same stuff with less effort. At first I used evil, as anyone newly introduced to modal editing is wont to do, however after a while I realized I was using a really small subset of it (I don’t even use text objects all that much), and therefore it’d be a good idea to replace it with something more suited to my workflow. Here thus I set up an evilesque config (minus all the features I don’t need) using ryo-modal.

Preliminary custom functions

First I must set up some functions that will create commands that are closer to what I’m used to from evil.
First, some functions that will be used to open new lines below and above (equivalents of o and O in evil) and also a command to be used when exiting ryo-modal to edit text (equivalent of i in evil) so that any selected region will be killed (adapted from here).

(defun start-from-new-line ()
    (interactive)
    (move-end-of-line nil)
    (newline)
    (indent-for-tab-command))

  (defun start-from-new-top-line ()
    (interactive)
    (previous-line)
    (start-from-new-line))

  (defun lmintmate/insert ()
    "Kill active region if active"
    (interactive)
    (if mark-active (kill-region (region-beginning) (region-end))))

Custom self-created command to prevent a from going to the next line when at the end of the current line.

(defun lmintmate/append ()
  "Do not go to the next line when at the end of the current line"
  (interactive)
  (unless (eolp) (forward-char)))

Command to deactivate the mark if activated - used later as part of the command for toggling ryo-modal-mode (from here).

(defun sk/remove-mark ()
  "Deactivate the region"
  (interactive)
  (if (use-region-p)
	  (deactivate-mark)))

From here. When using arrows afterwards, it extends the mark. Also executing it multiple times marks multiple lines. Bound to V.

(defun lmintmate/mark-line (&optional arg)
  (interactive "p")
  (if (not mark-active)
      (progn
        (beginning-of-line)
        (push-mark)
        (setq mark-active t)))
  (forward-line arg))

Command to copy entire line to kill-ring, bound to Y (adapted from here).

(defun lmintmate/kill-ring-save-line-trim ()
  "Copy current line in kill-ring, trimming begining spaces and tabs"
  (interactive)
	(beginning-of-line)
	(kill-ring-save (progn (skip-chars-forward " \t") (point))
			(line-beginning-position 2))
	(beginning-of-line))

Command to copy the selected region to kill-ring, and show message if there isn’t an active region (adapted from here). Bound to y.

(defun selection/kill-ring-save ()
  "kill-ring-save the active region but don't do anything if there's no active region"
  (interactive)
  (if (use-region-p)
      (kill-ring-save (region-beginning) (region-end))
    (message "Used selection/kill-ring-save while no active region")))

Command to kill the selected region, and show message if there’s no active region, bound to d (adapted from here).

(defun selection/kill-region ()
  "Kill the active region but don't do anything if there's no active region"
  (interactive)
  (if (use-region-p)
      (kill-region (region-beginning) (region-end))
    (message "Used selection/kill-region while no active region")))

Command for toggling the case of letter under point, like ~ in evil (but unlike evil, cannot be repeated with numeric argument). Here bound to ` instead as that’s easier to press (adapted from emacs - Toggle case of next letter in elisp - Stack Overflow).

(defun lmintmate/toggle-case-char-under-point ()
  "Toggle the case of char under point. Afterwards moves to the next char."
  (interactive)
  (let ((char (following-char)))
    (if (eq char (upcase char))
        (insert-char (downcase char) 1 t)
      (insert-char (upcase char) 1 t)))
  (delete-char 1 nil))

Commands to toggle ryo-modal and overwrite modes.

(defun lmintmate/toggle-ryo-modes ()
  "Deactivate a potentially marked region, toggle input method if enabled and disable overwrite-mode if enabled before toggling ryo-modal-mode"
  (interactive)
  (sk/remove-mark)
  (unless (eq current-input-method nil)
  (toggle-input-method))
  (overwrite-mode -1)
  (ryo-modal-mode 'toggle))

(defun lmintmate/silently-toggle-overwrite-mode ()
  "Silently toggle overwrite-mode"
  (interactive)
  (overwrite-mode 'toggle))

Keybindings and mode toggles

Here are the keys bound in ryo-modal-mode. Notably, I bind backspace to backward-char, as I don’t want backspace to be able to delete stuff while in ryo mode.

(ryo-modal-keys
   ;; Movement
   ("i" lmintmate/insert :name "Insert" :exit t)
   ("a" lmintmate/append :first (lmintmate/insert) :name "Append" :exit t)
   ("b" backward-word)
   ("w" forward-word)
   ("f" jump-char-forward)
   ("F" jump-char-backward)
   ("j" next-line)
   ("k" previous-line)
   ("h" backward-char)
   ("l" forward-char)
   ("<backspace>" backward-char)
   ("A" move-end-of-line :name "Append at end of line" :exit t)
   ("I" move-beginning-of-line :name "Insert at beginning of line" :exit t)
   ("g g" beginning-of-buffer)
   ("G" end-of-buffer)
   ("e" mark-whole-buffer)
   ("^" back-to-indentation)
   ("0" move-beginning-of-line)
   ("$" move-end-of-line)
   ;; Editing
   ("." ryo-modal-repeat)
   ("y" selection/kill-ring-save)
   ("Y" lmintmate/kill-ring-save-line-trim)
   ("p" yank)
   ("`" lmintmate/toggle-case-char-under-point)
   ("<S-return>" start-from-new-top-line)
   ("<return>" start-from-new-line)
   ("o" start-from-new-line :name "Open new line below and insert" :exit t)
   ("O" start-from-new-top-line :name "Open new line above and insert" :exit t)
   ("u" undo-fu-only-undo :norepeat t)
   ("C-r" undo-fu-only-redo :norepeat t)
   ("r" lmintmate/silently-toggle-overwrite-mode :exit t)
   ("d" selection/kill-region)
   ("D" kill-whole-line)
   ("c w" kill-word :name "Kill word" :exit t)
   ("c $" kill-line :name "Kill from point until end of line" :exit t)
   ("S" kill-line :first '(move-beginning-of-line) :name "Substitute line" :exit t)
   ("c f" zap-to-char)
   ("c t" zap-up-to-char)
   ("v" set-mark-command :name "Begin marking a region" :norepeat t)
   ("V" lmintmate/mark-line :name "Begin marking whole lines")
   ("C" comment-dwim :norepeat t)
   ("J" join-line)
   ;; Other
   ("s" save-buffer :norepeat t))

Keys to use for numeric arguments.

(ryo-modal-keys
 (:norepeat t)
 ("1" "M-1")
 ("2" "M-2")
 ("3" "M-3")
 ("4" "M-4")
 ("5" "M-5")
 ("6" "M-6")
 ("7" "M-7")
 ("8" "M-8")
 ("9" "M-9"))

Here I hook ryo-modal-mode to text and prog mode and bind keys to enable/disable and toggle it. I also add a hook to disable ryo-modal-mode when the input method is activated.

(add-hook 'text-mode-hook 'ryo-modal-mode)
(add-hook 'prog-mode-hook 'ryo-modal-mode)
(global-set-key (kbd "C-'") 'ryo-modal-mode)
(global-set-key (kbd "<escape>") 'lmintmate/toggle-ryo-modes)

(add-hook 'input-method-activate-hook (lambda () (ryo-modal-mode -1)))

Beautification: Cursors and modeline indicators

Here I set custom cursor types for each ryo “state”, so that I can distinguish visually when I’m editing text with regular emacs editing, when modal editing is enabled, and when overwrite mode is being used. The regular cursor is used when ryo-modal is on, a bar cursor when it’s off, and an hbar cursor when overwrite-mode is on. When the buffer is neither text nor prog mode (only modes where ryo is enabled by default anyway) the normal cursor is used (adapted from emacs-fu: changing the cursor color and shape dynamically).

(setq djcb-modal-cursor-type 'box)
(setq djcb-overwrite-cursor-type 'hbar)
(setq lmintmate/emacs-editing-cursor-type 'bar)
(setq djcb-emacs-other-cursor-type 'box)

(defun djcb-set-cursor-according-to-mode ()
  "change cursor type according to modes."
  (cond
   (ryo-modal-mode
      (setq cursor-type djcb-modal-cursor-type))
    (overwrite-mode
      (setq cursor-type djcb-overwrite-cursor-type))
    ((or (derived-mode-p 'prog-mode) (derived-mode-p 'text-mode))
     (setq cursor-type lmintmate/emacs-editing-cursor-type))
    (t
      (setq cursor-type djcb-emacs-other-cursor-type))))

(add-hook 'ryo-modal-mode-hook 'djcb-set-cursor-according-to-mode)

Remove the color from ryo cursor, as it is red by default and I don’t want it to be so.

(setq ryo-modal-cursor-color nil)

Here I set up modeline indicators to distinguish ryo from emacs from overwrite “states” (adapted from here). The idea to create faces for the modeline tags is inspired from here, but see however Elisp: How to Define Face for the correct method of defining faces.

(defface modal-state-tag
  '((t :foreground "black"
       :background "#4f94cd"
       :box (:line-width 2 :color "#4f94cd")))
  "Face for the Modal state tag"
  :group 'my-state-tags)

(defface overwrite-state-tag
  '((t :foreground "black"
       :background "tomato"
       :box (:line-width 2 :color "tomato")))
  "Face for the Overwrite state tag"
  :group 'my-state-tags)

(defface emacs-state-tag
  '((t :foreground "black"
       :background "MediumPurple2"
       :box (:line-width 2 :color "MediumPurple2")))
  "Face for the Emacs state tag"
  :group 'my-state-tags)

  (defun add-ryo-modeline-status (&rest _)
    (interactive)
    (let ((win (frame-selected-window)))
      (unless (minibuffer-window-active-p win)
        (add-to-list 'mode-line-format '(:eval (cond (ryo-modal-mode
                                                     (propertize " MODAL " 'face 'modal-state-tag))
						  (overwrite-mode
                                                     (propertize " OVWRT " 'face 'overwrite-state-tag))
						 (t
                                                   (propertize " EMACS " 'face 'emacs-state-tag))))))))

(add-hook 'window-configuration-change-hook 'add-ryo-modeline-status)
;; Needed so that it'll show up on all major modes, including help buffers and ibuffer
(add-hook 'after-change-major-mode-hook 'add-ryo-modeline-status)

Misc configuration parameters

The Bell

That infamous bell… I only found out about its «charms» because it turns out my system sounds were disabled for some reason and I hadn’t even realised this was the case. This is my way to exterminate those annoying sounds everytime anything out of the norm happens (that’s why we have text messages in the first place after all!). I disabled alarms completely as even the visual indication (which is a nice wheat color in my colortheme) can be distracting…

(setq ring-bell-function 'ignore)

Disable dialog boxes

(setq use-dialog-box nil)

Separate custom file

Put it in the no-littering config folder. Idea from here - also see here.

(setq custom-file (no-littering-expand-etc-file-name "custom.el"))

Set the format of the frame title

It shows relative file path if a file is opened, whether the buffer is modified or not, and the emacs version (adapted from EmacsWiki: Frame Title).

(setq frame-title-format
    '((:eval (if (buffer-file-name)
                  (abbreviate-file-name (buffer-file-name))
                    "%b"))
      (:eval (if (buffer-modified-p)
                 " [+]"))
      " - Emacs " emacs-version))

Do not autosave nor make any backup files.

All they do is litter the place and trigger a nagging prompt whenever I leave Emacs without having saved.

(setq auto-save-default nil)
(setq make-backup-files nil)

Do not create lockfiles

The only thing they do is being annoying, and I’m not going to find myself in a situation where I’ll be writing on the exact same file as someone else.

(setq create-lockfiles nil)

Delete by moving to the trash

(the default behavior being completely delete from the system)

(setq delete-by-moving-to-trash t)

Delete selection mode.

I used to think that this enabled deleting selected text with the Delete key, but it turns out that one is the work of the delete-active-region parameter, which is enabled by default. What this does is allow the replacing of selected text with other inserted (e.g. pasted/yanked) text, thus bringing Emacs more in line with other text editors.
I initially set this one from the Customization buffer, and got (setq delete-selection-mode t) as the resulting code snippet, so I assumed it would work even when outside the custom-set-variables, but it didn’t - and then I was wondering why pasting text didn’t replace the selected text… Now I replaced that wrong parameter with the correct one.

(delete-selection-mode 1)

P.S. Just so you know, here’s precisely why the other wording hadn’t worked:

Setting this variable directly does not take effect; either customize it (see the info node `Easy Customization’) or call the function `delete-selection-mode’

That goes into showing that RT(F)M is valid advice…

Disable double space as end of sentence

I once tried M-e to go to the end of a long sentence I wrote, and was surprised when I went to the end of the paragraph instead. I searched a little about it and found out there are people that actually use two spaces to start a new sentence. I personally use only one space though (and when writing on paper zero), so I disable this setting.

(setq sentence-end-double-space nil)

Disabling menu bar when emacs is run in a terminal.

Since it can’t be clicked anyways, it takes up space without reason… (I use display-graphic-p instead of window-system because the latter is now deprecated:)

>From the doc string of `window-system’:

“Use of this function as a predicate is deprecated. Instead, use `display-graphic-p’ or any of the other `display-*-p’ predicates which report frame’s specific UI-related capabilities.”

(unless (display-graphic-p)
  (menu-bar-mode -1))

Toggling the menu bar with a keyboard shortcut.

(global-set-key [f8] 'toggle-menu-bar-mode-from-frame)

Toggle scroll bar and window divider mode

I might not always want the scroll bar enabled (though I prefer it to be, so that I can quickly see where I am on a buffer), but when I have it disabled, there is no divider between vertical splits. Window divider mode fixes that. I want however the window divider mode and scroll bar mode to be mutually exclusive, as, when the scroll bar mode is enabled, the scroll bar by itself is a sufficient divider. Thus I create a hook on window divider mode that toggles the scroll bar mode (for the 'toggle property see the docstring of define-minor-mode), and add a keybinding to enable and disable window-divider-mode. I also customise the look of the divider to fit in more with my theme.

(set-face-attribute 'window-divider nil :foreground "gray75")
(set-face-attribute 'window-divider-first-pixel nil :foreground "gray95")
(set-face-attribute 'window-divider-last-pixel nil :foreground "gray55")

(add-hook 'window-divider-mode-hook (lambda () (scroll-bar-mode 'toggle)))
(global-set-key [f10] 'window-divider-mode)

Toggle maximizing the frame

Useful for newsticker

(global-set-key [f9] 'toggle-frame-maximized)

Do not resize frame when setting frame elements

From the help string of frame-inhibit-implied-resize:

Whether frames should be resized implicitly.

If this option is nil, setting font, menu bar, tool bar, internal
borders, fringes or scroll bars of a specific frame may resize the frame
in order to preserve the number of columns or lines it displays.  If
this option is t, no such resizing is done.
[...]
The value of this option can be also be a list of frame parameters. [...]
[For more see the help string]
(setq frame-inhibit-implied-resize t)

Visual line mode only for text mode.

Visual line wraps lines instead of cutting them as default.

(add-hook 'text-mode-hook 'visual-line-mode)

Associate .txt files with the goto-address-mode.

This mode highlights urls and makes them clickable.

(add-to-list 'auto-minor-mode-alist '("\\.te?xt\\'" . goto-address-mode))

Bind command to kill current buffer

Oftentimes, I just want to kill the current buffer, and C-x k showing me a list slows me down, since I thus have to do 2 actions: first use C-x k and then press enter to confirm killing the (highlighted) current buffer. Thus here I bind C-c k to kill-current-buffer. This function was added on 26.1 however (see here), so I have to use a custom function to achieve the same in emacs versions under 26 (idea from here).

(when (version< emacs-version "26.0.50" )
(defun my-kill-buffer ()
    "Kill current buffer without prompting"
    (interactive)
    (kill-buffer (current-buffer))))

(if (version<= "26.0.50" emacs-version )
(global-set-key "\C-ck" 'kill-current-buffer)
(global-set-key "\C-ck" 'my-kill-buffer))

Bind M-o to other-window

C-x o is too long a binding for this simple action (idea drawn from My Emacs keybindings - Mastering Emacs).

(define-key global-map "\M-o" 'other-window)

Adds shift + arrows for changing between visible buffers, in addition to M-o.

(when (fboundp 'windmove-default-keybindings)
  (windmove-default-keybindings))

The windmove-wrap-around setting allows for windmove movement off the edge of a frame to wrap around.

(setq windmove-wrap-around t)

Drag selected text with the mouse in emacs

Sometimes I just want to cop out and use the mouse when trying to move text. I found out via Stack Exchange that this is possible in emacs, and one just needs to set the function mouse-drag-and-drop-region to t. This feature was added in version 26.1 (see here), and I thus wrap it in a conditional so that it won’t error out on versions lower than 26.

(when (version<= "26.0.50" emacs-version )
(setq mouse-drag-and-drop-region t))

Bind zap-up-to-char command in emacs state

I found the commands M-x zap-to-char and zap-up-to-char, which roughly correspond to vim’s df and dt. The former is bound to M-z, but the latter isn’t bound to anything. I bind it here to C-c z, since that isn’t bound to anything.

;; (define-key evil-emacs-state-map "\C-cz" 'zap-up-to-char)

Enable display-line-numbers-mode

This minor mode first appeared in 26.1 and makes line rendering faster than the previous system, linum-mode. I have text-mode and prog-mode display relative line numbers, for help with line movements (adapted from here). I enable it conditionally, so that it won’t error out on a version lower than 26.1, where the display-line-numbers feature was first added.

(when (fboundp 'display-line-numbers-mode)
(setq-default display-line-numbers nil)
(defun noct:relative ()
  (setq-local display-line-numbers 'relative))
(defun noct:line-number-relative ()
  (setq-local display-line-numbers-current-absolute nil)))

(when (fboundp 'display-line-numbers-mode)
(add-hook 'text-mode-hook #'noct:relative)
(add-hook 'text-mode-hook #'noct:line-number-relative)
(add-hook 'prog-mode-hook #'noct:relative)
(add-hook 'prog-mode-hook #'noct:line-number-relative))

Disable it however for lisp-interaction-mode

I don’t want line numbers to display however for lisp-interaction-mode, which is the mode used in the *scratch* buffer (and, to my knowledge, only there). I set this conditionally so that it won’t error out on versions lower than 26.1.

(when (fboundp 'display-line-numbers-mode)
(add-hook 'lisp-interaction-mode-hook (lambda () (display-line-numbers-mode -1))))

Make the current line bold

I set it conditionally so that it won’t error out on versions lower than 26.1. I set it to be loaded with with-eval-after-load because set-face-attribute needs for the package being configured to be loaded, unlike custom-set-faces.

(when (fboundp 'display-line-numbers-mode)
(with-eval-after-load 'display-line-numbers
(set-face-attribute 'line-number-current-line nil :inherit font-lock-comment-face)))

Make help windows active by default

When I call a help window, I want to immediately scroll through it, and having to either click to it or use the other-window command to make it active was somewhat annoying. This parameter makes such windows active by default (found from Why doesn’t emacs set the focus to information windows? : emacs subreddit).

(setq-default help-window-select t)

Enable all disabled commands

Here I enable all advanced commands that are by default disabled (for more information see EmacsWiki: Disabled Commands). Most interesting for me is erase-buffer, which I can use when I want to clear the scratch buffer after some experimenting in order to have a clean slate.

(setq disabled-command-function nil)

Theme settings

Treat all themes as safe

(setq custom-safe-themes t)

Load only one theme at a time

Found from here.

(defadvice load-theme (before clear-previous-themes activate)
    "Clear existing theme settings instead of layering them"
    (mapc #'disable-theme custom-enabled-themes))

Custom mode settings

This the mode used for the Customization interface buffers.
Here I have the names of the entries to be customised shown in their raw lisp form.

(setq custom-unlispify-tag-names nil)

Bookmark settings

In order to remember where I last left reading a particular info manual I can use the bookmarks feature of emacs. In particular, I can add a bookmark with the command bookmark-set (C-x r m), access bookmarks with the command bookmark-jump (C-x r b), view a more detailed list of bookmarks with bookmark-bmenu-list (C-x r l) and delete a bookmark with the command bookmark-delete. I set the latter command to C-x r d, overriding it default setting delete-rectangle, because I thought I wanted to keep the C-x r line of commands for all bookmark related commands.

(global-set-key (kbd "C-x r d") 'bookmark-delete)

Custom functions

Function to download a file from url to specified path

Found from here.

(defun d/download-file (&optional url name)
  "Download a file from url to specified path."
  (interactive)
  (let* ((file-url (or url (read-from-minibuffer "URL: ")))
         (file-name
          (or name
              (counsel-find-file
               (file-name-nondirectory file-url)))))
    (url-copy-file file-url file-name)))

Function to quickly switch to the *scratch* buffer

Useful when I want to experiment with some elisp code, but have also opened a gazillion help buffers and thus have to actually type (gasp, the horror!) the name of the scratch buffer in order to get back there. Also add a keybinding to make the switching even faster. Found from here.

(defun d/switch-to-scratch ()
  "Switch to scratch buffer."
  (interactive)
  (switch-to-buffer "*scratch*"))
(global-set-key (kbd "\C-cs") 'd/switch-to-scratch)

Functions to change file formats

Found from here.

(defun spacemacs/dos2unix ()
  "Converts the current buffer to UNIX file format."
  (interactive)
  (set-buffer-file-coding-system 'undecided-unix nil))

(defun spacemacs/unix2dos ()
  "Converts the current buffer to DOS file format."
  (interactive)
  (set-buffer-file-coding-system 'undecided-dos nil))

Functions to rename files

Adapted from here.

(defun lmintmate/rename-file (filename &optional new-filename)
  "Rename FILENAME to NEW-FILENAME.
When NEW-FILENAME is not specified, asks user for a new name.
Also renames associated buffers (if any exists) and updates recentf list."
  (interactive "f")
  (when (and filename (file-exists-p filename))
    (let* ((is-dir (file-directory-p filename))
           (short-name
            (if is-dir
                (file-name-base (directory-file-name filename))
              (file-name-nondirectory filename)))
           (new-filename
            (if new-filename new-filename
              (read-file-name
               (format "Rename %s to: " short-name)))))

      ;; Rename filename to new-filename and error if new-filename already
      ;; exists. `dired-rename-file' handles renaming of directories and files.
      ;; It updates the name of all associated buffers.
      (dired-rename-file filename new-filename nil)

      ;; Update recentf list.
      (when (fboundp 'recentf-add-file)
        (seq-map
         (lambda (fp)
           (recentf-add-file
            (concat new-filename (string-remove-prefix filename fp)))
           (recentf-remove-if-non-kept fp))
         (seq-filter
          (lambda (fp)
            (string-prefix-p filename fp))
          recentf-list)))

      ;; Inform user about tremendous success.
      (message "%s '%s' successfully renamed to '%s'"
               (if is-dir "Directory" "File")
               short-name
               (file-name-nondirectory new-filename)))))

(defun lmintmate/rename-current-buffer-file (&optional arg)
  "Rename the current buffer and the file it is visiting.
If the buffer isn't visiting a file, ask if it should
be saved to a file, or just renamed.
If called without a prefix argument, the prompt is
initialized with the current directory instead of filename."
  (interactive "P")
  (let* ((name (buffer-name))
         (filename (buffer-file-name)))
    (if (and filename (file-exists-p filename))
        ;; the buffer is visiting a file
        (let* ((dir (file-name-directory filename))
               (new-name (read-file-name "New name: " (if arg dir filename))))
          (cond ((get-buffer new-name)
                 (error "A buffer named '%s' already exists!" new-name))
                (t
                 (let ((dir (file-name-directory new-name)))
                   (when (and (not (file-exists-p dir))
                              (yes-or-no-p
                               (format "Create directory '%s'?" dir)))
                     (make-directory dir t)))
                 (rename-file filename new-name 1)
                 (rename-buffer new-name)
                 (set-visited-file-name new-name)
                 (set-buffer-modified-p nil)
                 (when (fboundp 'recentf-add-file)
                   (recentf-add-file new-name)
                   (recentf-remove-if-non-kept filename))
                 (message "File '%s' successfully renamed to '%s'"
                          name (file-name-nondirectory new-name)))))
      ;; the buffer is not visiting a file
      (let ((key))
        (while (not (memq key '(?s ?r)))
          (setq key (read-key (propertize
                               (format
                                (concat "Buffer '%s' is not visiting a file: "
                                        "[s]ave to file or [r]ename buffer?")
                                name)
                               'face 'minibuffer-prompt)))
          (cond ((eq key ?s)            ; save to file
                 ;; this allows for saving a new empty (unmodified) buffer
                 (unless (buffer-modified-p) (set-buffer-modified-p t))
                 (save-buffer))
                ((eq key ?r)            ; rename buffer
                 (let ((new-name (read-string "New buffer name: ")))
                   (while (get-buffer new-name)
                     ;; ask to rename again, if the new buffer name exists
                     (if (yes-or-no-p
                          (format (concat "A buffer named '%s' already exists: "
                                          "Rename again?")
                                  new-name))
                         (setq new-name (read-string "New buffer name: "))
                       (keyboard-quit)))
                   (rename-buffer new-name)
                   (message "Buffer '%s' successfully renamed to '%s'"
                            name new-name)))
                ;; ?\a = C-g, ?\e = Esc and C-[
                ((memq key '(?\a ?\e)) (keyboard-quit))))))))

Function to show parent modes of current buffer’s major mode

Found from here. Depends on parent-mode.

(require 'parent-mode)
(defun parent-mode-display ()
  "Display this buffer's mode hierarchy."
  (interactive)
  (let ((ls (parent-mode-list major-mode)))
    (princ ls)))

Dabbrev completion

The default command used for completion by evil, evil-complete-next (bound by default to C-n), only completes one possible candidate, and thus, if it isn’t the correct one, one has to erase the wrong characters and try again with more of the prefix string. Looking at the function definition, it used dabbrev (dabbrev-expand specifically) under the hood. Dabbrev also includes dabbrev-completion, which has the following behavior (as found at its docstring):

Like [dabbrev-expand] but finds all expansions in the current buffer
and presents suggestions for completion.

With a prefix argument ARG, it searches all buffers accepted by the
function pointed out by dabbrev-friend-buffer-function to find the
completions.

If the prefix argument is 16 (which comes from C-u C-u),
then it searches *all* buffers.

In short, this normally looks only at the current buffer, but when using a specific prefix, it can look at all buffers. Here thus I define a function to run it with the prefix (see here) and bind this function to emacs and insert states at the modes I want to use this with. Note that dabbrev-completion uses a popup when ivy is installed, otherwise it uses a more inconvenient Completions buffer.

(defun dabbrev-completion-all-buffers ()
    (interactive)
  (setq current-prefix-arg '(16))
(call-interactively 'dabbrev-completion))

(define-key text-mode-map (kbd "C-n") 'dabbrev-completion-all-buffers)
(define-key prog-mode-map (kbd "C-n") 'dabbrev-completion-all-buffers)

Setting so that completion in org mode won’t change when the character for literal code ~ is in front of the completion candidate. What this does is tell dabbrev to ignore the presence of said symbol. Without this setting when attempting to complete a string preceded by ~ , the first letter of each subword is capitalised e.g. Package-Install-Selected-Packages instead of the normal package-install-selected-packages (idea to use this setting from here).

(setq dabbrev-abbrev-skip-leading-regexp "~")

Recent files

A quick way to access my most recently opened files (as I didn’t want to have to go all the way through the directory structure).

(require 'recentf)
(recentf-mode 1)

Don’t add files from the elpa folder, autoloads or bookmarks in the recentf list (adapted from here).

(setq recentf-exclude '(".*-autoloads\\.el\\'"
                        "[/\\]\\elpa/"
                        "bookmark"
                        ))

Note that the command recentf-edit-list provides a way to remove undesired files from the recentf list without having to edit the file by hand. I should have found about this earlier!

Settings for multiple buffer management

I wanted to be able to change the layout of the buffers from horizontal to vertical, as well as be able to flip frames, so that left goes right, and up goes down. I used to use some custom functions found at What the .emacs.d!? (here and here, specifically), but then found the package transpose-frame (available at MELPA), and decided to use that instead, as to make the README.org file less lengthy.

(define-key global-map "\M-[" 'transpose-frame)
(define-key global-map "\M-]" 'rotate-frame)

Ibuffer

A better way to list buffers than buffer-menu(link). Config influenced from Using Emacs - 34 - ibuffer and emmet | C’est la Z, some dude’s .emacs, and some other dotfiles from github.

Replace buffer-menu.

(require 'ibuffer)
 (global-set-key (kbd "C-x C-b") 'ibuffer)
    (autoload 'ibuffer "ibuffer" "List buffers." t)

Set buffer groups.

(setq ibuffer-saved-filter-groups
      '(("default"
	       ("Dired" (mode . dired-mode))
	       ("Org" (derived-mode . org-mode))
               ("Text" (name . "^.*txt$"))
               ("Markdown" (derived-mode . markdown-mode))

	       ("Emacs Lisp" (mode . emacs-lisp-mode))
               ("Help" (or (derived-mode . help-mode)
                       (derived-mode . helpful-mode)
                       (derived-mode . elisp-refs-mode)
                       (derived-mode . apropos-mode)))
               ("Info" (derived-mode . Info-mode))
               ("Custom" (derived-mode . Custom-mode))
               ("Scratch" (name . "*scratch*"))
               ("Git" (derived-mode . magit-mode))
	       ("Other"
                  (or
                   (name . "^\\*")))
	       )))
(add-hook 'ibuffer-mode-hook
	  (lambda ()
	    (ibuffer-auto-mode 1)
	    (ibuffer-switch-to-saved-filter-groups "default")))

Don’t show filter groups if there are no buffers in that group.

(setq ibuffer-show-empty-filter-groups nil)

Use human readable size column.

;; Use human readable Size column instead of original one
(define-ibuffer-column size-h
  (:name "Size" :inline t)
  (cond
   ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0)))
   ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0)))
   ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0)))
   (t (format "%8d" (buffer-size)))))

;; Modify the default ibuffer-formats
  (setq ibuffer-formats
	'((mark modified read-only " "
		(name 18 18 :left :elide)
		" "
		(size-h 9 -1 :right)
		" "
		(mode 16 16 :left :elide)
		" "
		filename-and-process)))

Dired Mode Configurations

Enable dired icon mode.

This functionality, coming from the dired-icon package, shows icons from the currently used icon theme next to the filenames, and thus makes for a better dired experience.

(add-hook 'dired-mode-hook 'dired-icon-mode)

Setting to make the image size of the dired-icon icons bigger.

(setq dired-icon-image-size 32)

Dired recent

This package remembers directories recently visited through dired.

(dired-recent-mode 1)

Auto-revert dired buffers

Also don’t show “Reverting…” message.

(add-hook 'dired-mode-hook 'auto-revert-mode)
(setq auto-revert-verbose nil)

Dired listing

Listing switches.

Group directories first and make sizes human-readable.

(setq dired-listing-switches "-alh --group-directories-first")

Sort files by modified date.

(add-hook 'dired-mode-hook 'dired-sort-toggle-or-edit)

Hide the details on dired mode, for a cleaner appearance.

(add-hook 'dired-mode-hook 'dired-hide-details-mode)

Dired dwim target

(setq dired-dwim-target t)

Org Mode configuration

First off, require org.

(require 'org)

Hook toc-org to org-mode

toc-org is a package that creates Table of Contents for org-mode files without exporting, which can thus give the rendered in github/lab README.org a table of contents, convenient for those who might want to browse said files. Here I add a snippet given in said repo to hook it to org-mode.

(if (require 'toc-org nil t)
    (add-hook 'org-mode-hook 'toc-org-mode)
  (warn "toc-org not found"))

Org-mouse

This is an org-mode subpackage that allows control of various things with the mouse. I enable it because I almost never remember which is the shortcut to tick checkboxes in org-mode.

(require 'org-mouse)

Workaround so that mouse-drag-and-drop-region will work. Specifically, when org-mouse is enabled, the mouse-drag-region command (the command used when org-mouse is disabled) for some reason is replaced with org-mouse-down-mouse which doesn’t have the drag-n-drop capabilities of the other command. The only way to override the default org-mouse behavior of <down-mouse-1>, in order for it to respect mouse-drag-and-drop-region, is to remap the command to mouse-drag-region with the remap parameter of define-key. Mapping the key doesn’t work.

(define-key org-mode-map [remap org-mouse-down-mouse] 'mouse-drag-region)

Define C-c l as the keybinding to org-cliplink.

I used to have it as the shortcut to org-store-link, but it turns out I hardly used that one…

(when (package-installed-p 'org-cliplink)
(define-key org-mode-map (kbd "\C-cl") 'org-cliplink))

Define C-c d as the keybinding to org-toggle-link-display

This command toggles between descriptive and literal links, and I need it so that I can edit on the literal links the text that will show up on the descriptive links (and it was too much of a hassle to go to the Org > Hyperlinks submenu just for that…).

(define-key org-mode-map (kbd "\C-cd") 'org-toggle-link-display)

Additional org link types

For a while I’ve wanted to have links to help buffers in org mode, however the default help link type defaulted to the help-mode buffers, while I wanted it to point to helpful-mode instead. With this code (found from Luis-Henriquez-Perez’s emacs config), when calling org-insert-link (reminder: has the kbd shortcut C-c C-l), I get the additional options of helpfn and helpvar which are for functions and variables respectively, and these will redirect to regular help buffers if helpful isn’t installed.

   ;; for functions
   (defun +org-link--helpfn:face (link)
	(let ((fn (intern link)))
	  (if (fboundp fn)
	      'org-link
	    'error)))

     (defun +org-link--helpfn:follow (link)
	(let ((fn (intern link)))
	  (if (require 'helpful nil :no-error)
	      (helpful-callable fn)
	    (describe-function fn))))

     (org-link-set-parameters
      "helpfn"
      :face
      #'+org-link--helpfn:face
      :follow
      #'+org-link--helpfn:follow)

 ;; for variables
 (defun +org-link--helpvar:face (link)
     (if (boundp (intern link)) 'org-link 'error))

   (defun +org-link--helpvar:follow (link)
     (let ((var (intern link)))
	(if (require 'helpful nil :noerror)
	    (helpful-variable var)
	  (describe-variable var))))

   (org-link-set-parameters
    "helpvar"
    :face
    #'+org-link--helpvar:face
    :follow
    #'+org-link--helpvar:follow)

Define C-c e as the keybinding to org-emphasize

This one helps to switch quickly between different text formattings (bold, italic e.t.c).

(define-key org-mode-map (kbd "\C-ce") 'org-emphasize)

Unbind C-c . so that Lacarte can function properly

In org mode, C-c . is by default bound to org-time-stamp, which I don’t use but which overrides the binding that I want to use for Lacarte (see above). Here I unbind this key from org-mode-map so that Lacarte can function properly (idea from How to override a keybinding in Emacs org-mode - Super User).

(define-key org-mode-map (kbd "\C-c.") nil)

Beautification settings

Fancy bullets with org-superstar

The stars used by the DejaVu Sans Mono font are rather plain, so when I noticed that the stars used by Hack (a DejaVu Sans Mono derivative that lacks many glyphs) were prettier, I got into my head the idea of beautifying org-mode. I first looked here, but after I decided to use a different symbol for each headline, I thought I’d try something a little more tried and true. I thus used at first org-bullets, though I didn’t really like the fact that such a widely-used emacs package was basically abandoned, being in the emacs orphanage. At some point, I used a fork I made that added the ability to completely hide the leading stars, instead of just masking them with a different color (which also removes the indentation effect and puts all bullets at the same level screenwise), found from a pull request against the original repository. However, I have now switched to org-superstar, which is package with the same aims as org-bullets, but is actually maintained and thanks to that, has more features, including the one I wanted and for which I had made the org-bullets fork (in fact, I was the one who requested that said feature be added).// Because I’m not the type to distinguish the level of the org headline by color or indentation, and used to use the number of stars to do so before, I decided to use some unicode symbols (found via counsel-unicode-char) that would somehow denote this hierarchy. I settled on Negative Circled Latin Capital letters A, B and C, but because these symbols don’t work under Windows, I use there the Inverse Circled Sans Serif digits one, two and three. Note that performance issues can be witnessed on Windows computers when org-superstar is installed. This is a fault ingrained to emacs for Windows, and thus not the fault of org-superstar (or org-bullets, which had that same problem). This can be fixed by setting inhibit-compacting-font-caches to t (see here and also the readme of org-superstar).

(require 'org-superstar)
(add-hook 'org-mode-hook 'org-superstar-mode)

(when (eq system-type 'windows-nt)
(setq inhibit-compacting-font-caches t))

(if (eq system-type 'windows-nt)
(setq org-superstar-headline-bullets-list
      '("" "" ""))
(setq org-superstar-headline-bullets-list
      '("🅐" "🅑" "🅒")))

;; completely hide the leading stars
(setq org-superstar-remove-leading-stars t)

Set org ellipsis symbol and color

See Changing the org-mode ellipsis · Endless Parentheses.

(setq org-ellipsis "")
(set-face-attribute 'org-ellipsis nil :foreground "cyan3" :underline 'unspecified)

Org progress states settings

These are, apart from TODO, DONE and CANCELLED, also CURRENTLY and SOMEDAY.

(setq org-todo-keywords
   '((sequence "TODO(t)" "CURRENTLY(c)" "SOMEDAY(s)" "CANCELLED(x)" "DONE(d)")))

Also have them use special bullets, courtesy of org-superstar

(setq org-superstar-special-todo-items t)
(setq org-superstar-todo-bullet-alist '(("TODO" . ?➽)
					("CURRENTLY" . ?⌛)
                                        ("SOMEDAY" . ?⏱)
                                        ("CANCELLED" . ?✘)
                                        ("DONE" . ?✓)))

Strikethrough for Done tasks

adapted from Sacha Chua’s blog

(setq org-fontify-done-headline t)
(set-face-attribute 'org-done nil :foreground "PaleGreen" :strike-through t :weight 'bold)
(set-face-attribute 'org-headline-done nil :foreground "LightSalmon" :strike-through t)

Special Ctrl-a/e behavior in org mode

From the Customize section of the parameter:

Non-nil means `C-a’ and `C-e’ behave specially in headlines and items.

When t, `C-a’ will bring back the cursor to the beginning of the headline text, i.e. after the stars and after a possible TODO keyword. In an item, this will be the position after bullet and check-box, if any. When the cursor is already at that position, another `C-a’ will bring it to the beginning of the line.

`C-e’ will jump to the end of the headline, ignoring the presence of tags in the headline. A second `C-e’ will then jump to the true end of the line, after any tags. This also means that, when this variable is non-nil, `C-e’ also will never jump beyond the end of the heading of a folded section, i.e. not after the ellipses.

(setq org-special-ctrl-a/e t)

Ctrl-k behavior in org mode

Use query to confirm killing of hidden subtrees.

(setq org-ctrl-k-protect-subtree t)

Make certain ryo-modal keys work on org-mode

First unbind C-', which triggers some agenda command, so that I can use it to enable/disable ryo-modal-mode from org-mode buffers.

(define-key org-mode-map (kbd "C-'") nil)

The rest of the overrides - the aim is to respect the various org settings that redefine the behavior of some keys such as org-special-ctrl-a/e and org-ctrl-k-protect-subtree, but also other redefinitions of keys that take place in org-mode, such as org-return.

(ryo-modal-major-mode-keys
 'org-mode
 ("<return>" org-return)
 ("c $" org-kill-line :name "Like kill-line but for org" :exit t)
 ("S" org-kill-line :first '(org-beginning-of-line) :name "Substitute line in org mode" :exit t)
 ("0" org-beginning-of-line)
 ("$" org-end-of-line))

Override shift + arrows in certain places in favor of windmove

If you want to make the windmove function active in locations where Org mode does not have special functionality on S-<cursor>, add this to your configuration(from Conflicts - The Org Manual):

;; Make windmove work in org-mode:
          (add-hook 'org-shiftup-final-hook 'windmove-up)
          (add-hook 'org-shiftleft-final-hook 'windmove-left)
          (add-hook 'org-shiftdown-final-hook 'windmove-down)
          (add-hook 'org-shiftright-final-hook 'windmove-right)

Auto adjust footnotes

(setq org-footnote-auto-adjust t)

Follow link in orgmode by pressing Enter key

This adds an alternative way to follow urls in orgmode without reaching out for the mouse.

(setq org-return-follows-link t)

Color only the stars for the org mode levels

This is a nice little setting I found while browsing the Customize interface. It removes the color from the org headline levels, only keeping it on the stars. This makes the buffer way less colorful, but I find it more clean that way. Not to mention that because my color theme isn’t amongst the most popular ones, the coloring was a bit bizzare, in that the first level was green, the second plain white and the third yellow, which was a bit disorienting, since I’d expect the second level to be colored instead of the third. So I’ve now removed the color from the text and can focus on the stars for denoting the hierarchy.

(setq org-level-color-stars-only t)

Custom color for headline levels 2 and 3

In my color theme, headline level 2 used to be plain white, while headline level 3 used to be bold «gold» in color, which was confusing, as I’d expect the inverse. So I took the initiative and customized the colors myself - on the way I decided I preferred level 3 to also be obvious as a headline, and distinguishable from the rest of the text.

(set-face-attribute 'org-level-2 nil :foreground "gold" :weight 'bold :inherit 'unspecified)
(set-face-attribute 'org-level-3 nil :foreground "cyan3" :weight 'bold :inherit 'unspecified)

Custom color for org source code blocks

I didn’t really like the fact that the org source code blocks had the color of the comments (grey), as this made them less visible and prominent. I found, via this reddit post, that I can customize the faces of org-block-begin-line and org-block-end-line. I decided however to leave the default settings for these, as the file became too flashy otherwise. I customized org-block to change the faces of the content of the source blocks and set their foreground to whitesmoke so that I can read them more easily.

(set-face-attribute 'org-block nil :foreground "whitesmoke" :inherit 'unspecified)

Custom postamble in html export

I only want to see the date and not the author nor the created by details on the bottom of the exported html file, and found out the org-html-postamble can be modified to not show these things. I also wanted a custom way to show the date format (because I don’t like the y-m-d format much), so I use here a custom function, adapted from this stackoverflow answer.

(defun my-org-html-postamble (plist)
 (format "Last update : %s" (format-time-string "%a %d/%m/%Y")))
(setq org-html-postamble 'my-org-html-postamble)

I don’t want to print the postamble everywhere however, so I also found out that writing #+OPTIONS: html-postamble:nil on the file where the postamble should be exlcuded does the trick.

Add shortcut templates for emacs lisp, org and title

These templates, e.g. <s, are very practical. Out of them I use most SRC emacs-lisp, org and #+title, so I wished I could create shortcuts for these too. However, the previous shortcut system was changed in Org 9.2 and I had to require org-tempo and modify my template config accordingly in order for the shortcuts to continue working (see here).

(when (version<= "9.2" (org-version))
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("o" . "src org"))
(add-to-list 'org-structure-template-alist '("vim" . "src vim"))
(add-to-list 'org-structure-template-alist '("sh" . "src sh"))
(add-to-list 'org-tempo-keywords-alist '("t" . "title")))

Enable Speed keys

Speed keys are single keystrokes without modifiers that can be used when the cursor is on a headline to e.g. move around the buffer. Here I enable them and set them to be able to be used on any star of the headline (code taken from the explanation string of the M-x customize section).

(setq org-use-speed-commands
        (lambda () (and (looking-at org-outline-regexp) (looking-back "^\**"))))

Change org-show-context-detail

This variable sets how much will be revealed when revealing locations. I’m changing its value because counsel-outline apparently uses this variable to determine how it will show the headings after jumping to a specific one. With the default setting it inherited the (default . ancestors), and thus didn’t show any further children that the heading had. I didn’t like this behavior, since I like to see all the children that are available, and thus set org-goto to tree which shows all the children, but hides any possible introductory text before the children, which is useful since I have long text in some headings as a sort of introduction (basically the reason I didn’t choose the canonical option, since that one shows said text). In order to not have to redefine the entire variable (since I only wanted to change the behavior of org-goto, that is add that element to the list), I use the push function to add the desired element (adapted from here).

(push '(org-goto . tree) org-show-context-detail)

Change org-block indentation setting

Org by default adapts the indentation to the outline node level, which leads to the begin_ and end_ type blocks to be indented when they are created in an outline level below 1, an undesirable for me behavior. This setting disables that, so that now the org blocks have the same alignment as normal text.

(setq org-adapt-indentation nil)

Custom org-related functions

Split org block in a dwim manner

Here I add the function modi/org-split-block (found from here), which splits an org block based on where the point is. For a better explanation, read the docstring below and also here:

# Sensibly split the current Org block at point.
# (1) Point in-between a line
#     #+begin_src emacs-lisp             #+begin_src emacs-lisp
#     (message▮ \"one\")                   (message \"one\")
#     (message \"two\")          -->       #+end_src
#     #+end_src                          ▮
# 				       #+begin_src emacs-lisp
# 				       (message \"two\")
# 				       #+end_src
# (2) Point at EOL
#     #+begin_src emacs-lisp             #+begin_src emacs-lisp
#     (message \"one\")▮                   (message \"one\")
#     (message \"two\")          -->       #+end_src
#     #+end_src                          ▮
# 				       #+begin_src emacs-lisp
# 				       (message \"two\")
# 				       #+end_src
# (3) Point at BOL
#     #+begin_src emacs-lisp             #+begin_src emacs-lisp
#     (message \"one\")                    (message \"one\")
#     ▮(message \"two\")          -->      #+end_src
#     #+end_src                          ▮
# 				       #+begin_src emacs-lisp
# 				       (message \"two\")
# 				       #+end_src

Note: There is also the org-babel-demarcate-block function (bound by default to C-c C-v d or C-c C-v C-d.

This function varies from [org-split-block] in at least two ways:

  • It works only for Org Src blocks.
  • It splits the block exactly at where the point is, whereas I would like to always split only at EOL or BOL.
(defun modi/org-in-any-block-p ()
  "Return non-nil if the point is in any Org block.

The Org block can be *any*: src, example, verse, etc., even any
Org Special block.

This function is heavily adapted from `org-between-regexps-p'."
  (save-match-data
    (let ((pos (point))
          (case-fold-search t)
          (block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$")
          (limit-up (save-excursion (outline-previous-heading)))
          (limit-down (save-excursion (outline-next-heading)))
          beg end)
      (save-excursion
        ;; Point is on a block when on BLOCK-BEGIN-RE or if
        ;; BLOCK-BEGIN-RE can be found before it...
        (and (or (org-in-regexp block-begin-re)
                 (re-search-backward block-begin-re limit-up :noerror))
             (setq beg (match-beginning 0))
             ;; ... and BLOCK-END-RE after it...
             (let ((block-end-re (concat "^[[:blank:]]*#\\+end_"
                                         (match-string-no-properties 1)
                                         "\\( .*\\)*$")))
               (goto-char (match-end 0))
               (re-search-forward block-end-re limit-down :noerror))
             (> (setq end (match-end 0)) pos)
             ;; ... without another BLOCK-BEGIN-RE in-between.
             (goto-char (match-beginning 0))
             (not (re-search-backward block-begin-re (1+ beg) :noerror))
             ;; Return value.
             (cons beg end))))))

(defun modi/org-split-block ()
  "Sensibly split the current Org block at point.

If the point is anywhere on the line, but not at the beginning of the line 
(BOL), go to the end of the line, and then split the block.

Otherwise (if point is at BOL), split the block exactly at that point."
  (interactive)
  (if (modi/org-in-any-block-p)
      (save-match-data
        (save-restriction
          (widen)
          (let ((case-fold-search t)
                (at-bol (bolp))
                block-start
                block-end)
            (save-excursion
              (re-search-backward "^\\(?1:[[:blank:]]*#\\+begin_.+?\\)\\(?: .*\\)*$" nil nil 1)
              (setq block-start (match-string-no-properties 0))
              (setq block-end (replace-regexp-in-string
                               "begin_" "end_" ;Replaces "begin_" with "end_", "BEGIN_" with "END_"
                               (match-string-no-properties 1))))
            ;; Go to the end of current line, if not at the BOL
            (unless at-bol
              (end-of-line 1))
            (insert (concat (if at-bol "" "\n")
                            block-end
                            "\n\n"
                            block-start
                            (if at-bol "\n" "")))
            ;; Go to the line before the inserted "#+begin_ .." line
            (beginning-of-line (if at-bol -1 0)))))
    (message "Point is not in an Org block")))

Ivy-counsel-swiper and ivy-prescient configuration

First, enable ivy and counsel modes.

(ivy-mode 1)
(counsel-mode 1)

Ivy prescient

Ivy prescient is much like smex, but it also remembers the history for not only counsel-M-x, but also many other commands that have a candidate list, and it is also actually maintained.// Enable ivy-prescient-mode.

(ivy-prescient-mode 1)

Enable prescient-persist-mode to keep usage statistics.

(prescient-persist-mode 1)

Don’t sort candidates by length.

(setq prescient-sort-length-enable nil)

Fix so that counsel-unicode-char will be sorted properly: Most other commands were sorted with prescient (except of course those commands that under ivy-read have :sort as either nil or absent - see here), but the output of counsel-unicode-char remained unsorted. And this while said command had :sort t and looking at prescient-save.el showed that the candidates I had chosen were indeed recorded by prescient. I was on the verge of filing an issue, when I saw another, unrelated, issue in the prescient repo mentioning ivy-sort-functions-alist. I took a look at that and saw there a little note to also see ivy-sort-max-size. I took then a look at this one and it said that this determined the max number of candidates after which sorting would be disabled. The default was 30000. Turns out the number of candidates for counsel-unicode-char was 43519, hence it wasn’t sorted. Here I set the max size to 44000 so that sorting with prescient will be enabled there too.

(setq ivy-sort-max-size 44000)

Basic ivy settings

Show current candidate index and count for ivy minibuffer completion.

(setq ivy-count-format "(%d/%d) ")

Keybindings for counsel and swiper functions. swiper-isearch is like regular swiper, only it shows all results corresponding to a search string from the current line, instead of only the first one, and it also doesn’t show the line numbers of the results (see Swiper-isearch - a more isearch-like swiper · (or emacs). counsel-switch-buffer is like ivy-switch-buffer, but it also shows the preview of each buffer. counsel-buffer-or-recentf is like counsel-recentf, but it shows with a different color when a recent file is already opened as a buffer.

(global-set-key (kbd "C-s") 'swiper)
(global-set-key (kbd "C-M-s") 'swiper-isearch)
(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key (kbd "\C-xb") 'counsel-switch-buffer)
(global-set-key (kbd "\C-cu") 'counsel-unicode-char)
(global-set-key (kbd "\C-cr") 'counsel-buffer-or-recentf)
(global-set-key (kbd "\C-h v") 'counsel-describe-variable)
(global-set-key (kbd "\C-h f") 'counsel-describe-function)

Binding to yank symbol in swiper-map. Useful when starting to search for a symbol without writing it in exactly the right manner, and I want to yank the correct form from one of the search results, without selecting said result.

(with-eval-after-load 'swiper
(define-key swiper-map "\C-w" 'ivy-yank-symbol))

Note: To search for the entire word at point with swiper (and not only the part from the cursor onwards, as M-j does), M-n (ivy-next-history-element) can be used to yank the symbol under point into the search.
Inverse use of C-j and tab. This allows me to use tab only once in order to go to the selected directory in counsel-find-file.

(define-key ivy-minibuffer-map (kbd "C-j") #'ivy-partial-or-done)
(define-key ivy-minibuffer-map (kbd "TAB") #'ivy-alt-done)

Wrap around the first and last candidate in the minibuffer.

(setq ivy-wrap t)

Do not add a ^ (beginning of line anchor) when completing (see here).

(setq ivy-initial-inputs-alist nil)

Don’t show parent and current directories in counsel-find-file (found from here).

(setq ivy-extra-directories nil)

Have swiper go to the start of a match, and not to its end (as is the default), when selecting a match.

(setq swiper-goto-start-of-match t)

Have ivy minibuffer selection highlight the entire current line (recommended from the ivy-rich README).

(setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line)

Make counsel-describe-face look like counsel-faces (found from here).

(add-to-list 'ivy-format-functions-alist '(counsel-describe-face . counsel--faces-format-function))

Decrease the width of the left column of counsel-fonts, as with the default width the font preview is cut off when the frame isn’t maximised.

(el-patch-defun counsel--font-with-sample (font-name)
  "Format function for `counsel-fonts'."
  (format (el-patch-swap "%-75s%s" "%-37s%s") font-name
          (propertize "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
                      'face (list :family font-name))))

Ivy/counsel switch buffer configuration

Prerequisite: Modify the ivy-switch-buffer-transformer function to remove the fontification of modified buffers. In versions of ivy after commit 38c90e8, the code that fontifies buffers in the buffer list was moved inside ivy-switch-buffer-transformer itself and therefore the conditionals of that one started overriding the entries of ivy-switch-buffer-faces-alist, if these coincided in some way. This affected the fontification I added to helpful and ivy-occur buffers, as those are considered to be “modified”, and thus the modified buffer fontification took precedence over my own config. Since I don’t need to add fontification to the modified buffers (as I already have an indication of them on the buffer list via ivy-rich: it shows an asterisk in the third column of the ivy/counsel-switch-buffer buffer list), I decided to remove the cond that adds fontification to these buffers altogether. The best way to do this is via el-patch, which permits to modify a function without touching the original function and without having to define a new function (which in my case would mean that I would also have to add a modified ivy-rich declaration for ivy-switch-buffer, only with my custom function instead of the original ivy-switch-buffer-transformer), and it is apparently also able to detect when the upstream code changes, which will be good for keeping my modifications up-to-date. I thus here create a “copy” of the original function with el-patch-defun and remove the undesired part with el-patch-remove.

(el-patch-defun ivy-switch-buffer-transformer (str)
  "Transform candidate STR when switching buffers."
  (let ((buf (get-buffer str)))
    (cond ((not buf) str)
	  ((let ((remote (ivy--remote-buffer-p buf)))
	     (when remote
	(format "%s (%s)" (ivy-append-face str 'ivy-remote) remote))))
	  ((not (verify-visited-file-modtime buf))
	   (ivy-append-face str 'ivy-modified-outside-buffer))
	  (el-patch-remove ((buffer-modified-p buf)
	   (ivy-append-face str 'ivy-modified-buffer)))
	  (t
	   (let* ((mode (buffer-local-value 'major-mode buf))
		  (face (cdr (assq mode ivy-switch-buffer-faces-alist))))
	     (ivy-append-face str face))))))

Fontify more elements in the switch buffer list: Some elements (namely directories and org files) are colored by default in the switch buffer list. This is controlled by the ivy-switch-buffer-faces-alist variable. Here I also add to it some more elements to fontify, namely emacs-lisp, helpful and ivy-occur buffers, and also the scratch buffer.

(add-to-list 'ivy-switch-buffer-faces-alist '(emacs-lisp-mode . font-lock-keyword-face))
(add-to-list 'ivy-switch-buffer-faces-alist '(helpful-mode . font-lock-comment-face))
(add-to-list 'ivy-switch-buffer-faces-alist '(ivy-occur-mode . font-lock-comment-face))
(add-to-list 'ivy-switch-buffer-faces-alist '(lisp-interaction-mode . font-lock-constant-face))

Ivy faces

Change color of ivy-org face: The default one is inherited from org-level-4, which is gray, and thus always confused me when displaying the buffers, because I’m used to the convention that gray=inactive or comment. I changed it thus to chartreuse green, which is the same color that org-level-1 has.

(when (package-installed-p 'ivy)
(set-face-attribute 'ivy-org nil :inherit font-lock-function-name-face))

Change color of ivy-highlight-face: This face is used in counsel-describe-function to highlight the interactive functions and in counsel-describe-variable to highlight the variables defined with defcustom. By default it inherits from highlight, which is a subtle color in my current setup, and thus I change it to bold chartreuse green to make it more obvious.

(when (package-installed-p 'ivy)
(set-face-attribute 'ivy-highlight-face nil :inherit font-lock-function-name-face))

Change color of ivy-minibuffer-match-face-1: This is the color used when a single search string is used in the minibuffer. I change it from the default grey because I didn’t like that one.

(when (package-installed-p 'ivy)
(set-face-attribute 'ivy-minibuffer-match-face-1 nil :foreground "blue" :background "pale turquoise" :weight 'bold :inherit 'unspecified))

Configure counsel-outline

The counsel-outline command allows for easier searching of and landing on org and other headlines. Here I set a keybinding for it on org-mode buffers, and also set it to display todo states, color the headings according to their org colors and customize the separator between the headlines.

(define-key org-mode-map (kbd "\C-co") 'counsel-outline)
(setq counsel-org-headline-display-todo t)
(setq counsel-outline-face-style 'org)
(setq counsel-outline-path-separator "")

Additional ivy actions

Add a “Delete from recentf” action for counsel-buffer-or-recentf. THis feature was added in the regular counsel-recentf with a PR but the author of said PR omitted to also add this feature to counsel-buffer-or-recentf, which is the one I prefer using. So I add it by myself to my own config.

(ivy-set-actions
 'counsel-buffer-or-recentf
 '(("j" find-file-other-window "other window")
   ("f" find-file-other-frame "other frame")
   ("x" counsel-find-file-extern "open externally")
   ("d" (lambda (file) (setq recentf-list (delete file recentf-list)))
    "delete from recentf")))

Ivy rich

This package shows descriptions of the items in ivy-switch-buffer, counsel-M-x, counsel-describe-variable, counsel-describe-function e.t.c.

(require 'ivy-rich)
(ivy-rich-mode 1)

Add custom display transformer for try

Make it the same as package-install. The non-verbose method used here that simply uses the transformer of package-install without having to reproduce it in my init is documented here.

(plist-put ivy-rich-display-transformers-list
             'try
	     'ivy-rich--package-install-transformer)

Add display transformer for package-reinstall

Make it the same as package-install.

(plist-put ivy-rich-display-transformers-list
             'package-reinstall
	     'ivy-rich--package-install-transformer)

Modify display transformer for counsel-describe-variable

Adds a preview of the current value of the variable. Adapted from doom-emacs, specifically from here and here, and found via r/emacs.

(defun +ivy-rich-describe-variable-transformer (cand)
  "Previews the value of the variable in the minibuffer"
  (let* ((sym (intern cand))
         (val (and (boundp sym) (symbol-value sym)))
         (print-level 3))
    (replace-regexp-in-string
     "[\n\t\^[\^M\^@\^G]" " "
     (cond ((booleanp val)
            (propertize (format "%s" val) 'face
                        (if (null val)
                            'font-lock-comment-face
                          'font-lock-function-name-face)))
           ((symbolp val)
            (propertize (format "'%s" val)
                        'face 'font-lock-keyword-face))
           ((keymapp val)
            (propertize "<keymap>" 'face 'font-lock-constant-face))
           ((listp val)
            (prin1-to-string val))
           ((stringp val)
            (propertize (format "%S" val) 'face 'font-lock-string-face))
           ((numberp val)
            (propertize (format "%s" val) 'face 'font-lock-doc-face))
           ((format "%s" val)))
     t)))

        (plist-put ivy-rich-display-transformers-list
                   'counsel-describe-variable
                   '(:columns
                     ((counsel-describe-variable-transformer (:width 40)) ; the original transformer
                      (+ivy-rich-describe-variable-transformer (:width 10))
                      (ivy-rich-counsel-variable-docstring (:face font-lock-doc-face)))))

Modify display transformer for counsel-recentf

Change date format from Y-m-d to d/m/y, and (more importantly) show the candidates in such a way as to shorten the intermediate directories depending on the width of the frame so that the filenames aren’t cut off (adapted from ivy-rich-switch-buffer-path). For this to work properly, that is keep the line adjustment correct, the last modified time was put first and the seconds were removed from its date format.

(el-patch-defun ivy-rich-file-last-modified-time (candidate)
  (let ((candidate (expand-file-name candidate ivy--directory)))
    (if (file-remote-p candidate)
        "?"
      (format-time-string (el-patch-swap "%Y-%m-%d %H:%M:%S" "%d/%m/%y %H:%M") (nth 5 (file-attributes candidate))))))

(plist-put ivy-rich-display-transformers-list
'counsel-recentf
    '(:columns
      ((ivy-rich-file-last-modified-time (:face font-lock-comment-face))
       (ivy-rich-candidate (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.8))))))))

Add display transformer for counsel-buffer-or-recentf

Similar to counsel-recentf, but uses counsel-buffer-or-recentf-transformer instead of ivy-rich-candidate.

(plist-put ivy-rich-display-transformers-list
'counsel-buffer-or-recentf
    '(:columns
      ((ivy-rich-file-last-modified-time (:face font-lock-comment-face))
       (counsel-buffer-or-recentf-transformer (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.8))))))))

Enable display transformers

Since a certain point, it now seems to also require an additonal argument in order to function properly.

(ivy-rich-set-display-transformer nil)

Helpful: Enhanced help buffers

The helpful package enhances the content of the help buffers.

Integrate helpful with counsel

The default helpful commands do not offer integration with ivy-rich. Thankfully, there are variables that define the function to be used to show the result of counsel-describe-function and counsel-describe-variable commands, so that I can have the enhanced help and keep the ivy-rich integration too (found from here). Note: the built-in describe-function includes both functions and macros. helpful-function is functions only, so helpful-callable is provided as a drop-in replacement (see here).

(setq counsel-describe-function-function #'helpful-callable)
(setq counsel-describe-variable-function #'helpful-variable)

Also change the function used for counsel-descbinds (which shows a candidate list of active keybindings and the function they correspond to, going to the function definition upon pressing enter) to helpful-callable, as it was describe-function by default (see here).

(setq counsel-descbinds-function #'helpful-callable)

Also bind helpful-key to the key used by default for describe-key

(global-set-key (kbd "C-h k") #'helpful-key)

Add helpful-mode to ibuffer help buffer modes

This styles it the same way as other help buffers on the ibuffer list.

(add-to-list 'ibuffer-help-buffer-modes 'helpful-mode)

Incorporate elisp-demos into helpful

(advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update)

Terminal configuration

Kill terminal buffer automatically when exiting

From oremacs.

After you close the terminal, you get a useless buffer with no process. It’s probably left there for you to have a history of what you did. I find it not useful, so here’s a way to kill that buffer automatically:

(defun oleh-term-exec-hook ()
  (let* ((buff (current-buffer))
         (proc (get-buffer-process buff)))
    (set-process-sentinel
     proc
     `(lambda (process event)
        (if (string= event "finished\n")
            (kill-buffer ,buff))))))

(add-hook 'term-exec-hook 'oleh-term-exec-hook)

Redefine keys in term modes to their normal behavior

Some key combinations don’t behave as I’d expect in ansi-term. I thus have to redefine them to behave as expected (Syntax from a comment on the emacs subreddit).

(add-hook 'term-mode-hook (lambda ()
      (define-key term-raw-map (kbd "M-x") 'counsel-M-x)
))

Magit

A very good git manager (the reports of its greatness aren’t overrated at all!).
Btw,for those curious (as I was) where the term “porcelain” with which magit is self-described comes from, here’s an answer - the gist of it is that it is a more accessible interface, as opposed to the less user-friendly “plumbing” levels/commands.

Set magit faces

I changed the faces of magit-diff-context-highlight and magit-section-highlight because them being quasi-black didn’t look good on my color theme. I also adjusted the faces of magit-branch-local, magit-branch-remote and magit-section-heading to fit in more with my colorscheme.

(when (package-installed-p 'magit)
(with-eval-after-load 'magit
(set-face-attribute 'magit-diff-context-highlight nil :background "DodgerBlue4" :foreground "grey70")
(set-face-attribute 'magit-section-highlight nil :background "#235c94")
(set-face-attribute 'magit-branch-local nil :foreground "cyan")
(set-face-attribute 'magit-branch-remote nil :foreground "chartreuse")
(set-face-attribute 'magit-section-heading nil :foreground "gold" :weight 'bold)))

Magit-status keybinding

(when (package-installed-p 'magit)
(global-set-key (kbd "C-x g") 'magit-status))

Trashed: trash can management from emacs (Linux only)

This package allows management of the trash can from inside emacs. Here I enable it and set the deletion date format to a form similar to the one it has on my gui file manager, that is localised and shortened day month Year and the time.

(when (package-installed-p 'trashed)
(require 'trashed)
(setq trashed-date-format "%a %d %b %Y %T"))

Emacs-lisp related configuration

Enable show-paren-mode

This highlights matching parentheses. Turns out it is a global minor mode, and it thus has to be activated for everything or for nothing. The show-paren-delay option shows the matching parenthese instantaneously when set to 0. The show-paren-style option set to mixed shows the matching parenthesis when it is visible and highlights the expression when it isn’t.

(setq show-paren-delay 0)
(show-paren-mode 1)
(setq show-paren-style 'mixed)

Enable electric-pair-mode

This autocompletes parentheses. It appears to be a global minor mode as well.

(electric-pair-mode 1)

Here is however a trick to disable it from everywhere but lisp-related modes (from How to make electric-pair-mode buffer local? - Emacs Stack Exchange).

(defvar my-electric-pair-modes '(emacs-lisp-mode lisp-interaction-mode))

(defun my-inhibit-electric-pair-mode (char)
  (not (member major-mode my-electric-pair-modes)))

(setq electric-pair-inhibit-predicate #'my-inhibit-electric-pair-mode)

HideShow mode

Link. A nifty minor mode to fold code the same way org-mode headings are folded.

(add-hook 'prog-mode-hook 'hs-minor-mode)

Keybindings. The <backtab> string means Shift+Tab.

(define-key prog-mode-map (kbd "TAB") 'hs-toggle-hiding)
(define-key prog-mode-map (kbd "<backtab>") 'hs-hide-all)
(define-key prog-mode-map (kbd "<C-tab>") 'hs-show-all)

Hide minor mode indicators from mode line

To hide the various minor modes from the mode line, I was initially using diminish, but after a while I felt like hiding all minor modes, since I was adding more and more modes for diminish to hide. So I looked into alternatives. I knew first about rich-minority, but it looked kinda complicated to configure, what with it using regexes and all. I ended up thus using minions, which hides all minor modes, using a whitelist to show any, but also gives access to them with a menu hidden behind a symbol. I replaced the default symbol ;- with the identical to () math symbol because the default one doesn’t feel like a menu symbol to me. I also have ryo-modal-mode and overwrite-mode show up in the modeline in order to have an extra indication for them.

(minions-mode 1)
(setq minions-mode-line-lighter "")
(setq minions-direct '(ryo-modal-mode overwrite-mode))

Ediff

I’m trying to move myself to emacs as much as possible, so I now decided to replace the functionality of Meld.
This line splits the ediff windows horizontally instead of vertically.

(setq ediff-split-window-function 'split-window-horizontally)

This one puts the ediff help buffer in the same frame as the rest, as I was a bit bothered by the separate small window it had.

(setq ediff-window-setup-function 'ediff-setup-windows-plain)

Operate ediff through dired

Modified from Quickly ediff files from dired · (or emacs

(require 'dired-aux)
(defun ora-ediff-files ()
  (interactive)
  (let ((files (dired-get-marked-files)))
    (if (<= (length files) 2)
        (let ((file1 (car files))
              (file2 (if (cdr files)
                         (cadr files)
                       (read-file-name
                        "file: "
                        (dired-dwim-target-directory)))))
          (if (file-newer-than-file-p file1 file2)
              (ediff-files file2 file1)
            (ediff-files file1 file2)))
      (error "no more than 2 files should be marked"))))

The keybinding

(define-key dired-mode-map "e" 'ora-ediff-files)

Tangle the org file on save

The local variables set here are invisible when weaving. I also set these variables here as safe, so that emacs won’t warn me every time.

(add-to-list 'safe-local-variable-values
             '(eval add-hook 'after-save-hook
	                (lambda () (org-babel-tangle))
	                nil t))

Releases

No releases published

Packages

No packages published