Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1841 lines (1545 sloc) 66.8 KB

Emacs configuration with Org Mode

.screenshot.png

This is my Emacs configuration file written as Org document. The purpose of this file is to provide convenient way to browse Emacs configuration with expressive comments, and references to other people’s work.

Why Emacs?

Emacs by nature is really powerful editor, that can do anything from playing Tetris to Ordering Salads, but as any kind of power, this comes at a cost. In case of Emacs the cost is time. Making Emacs as feature full as most modern editors, like Sublime Text, Visual Studio Code, and Atom, can take huge amount of time, because you need to search over the internet for the packages, that provide this and that.

If Atom or VS Code already have all of this plus more why not just use those editors instead of Emacs? Well, first of all, they are built on top of Electron framework, which has some problems. It constantly improves, but still it is enormously fat for text editor. If I would use windows, I would choose to use VS Code, since there’s no alternative actually. Well, I could use Emacs there, but it is unpleasant experience. What’s actually good about VS Code or Atom is that they have all functionality within themselves. For example, recursive search for a file, I can do it with atom without any extra tool like find, but not in Emacs. This isn’t a problem in Linux or Mac OS though, due it’s Unix nature. It’s even better, since I can replace these tools with more performant tools like fd or ripgrep, which would be hard to achieve in Windows.

I’ve avoided Sublime Text in previous paragraph, because it is actually performant, and features everything that other editors offer, but unfortunately it’s not free. I don’t mind to pay for a good tool, but I don’t think that software has to be closed source in order to get funding. I understand that closed source software gets payments more often, because you can’t use it without paying, and Sublime Text is actually a great editor, but still, I prefer free tools when possible.

OK, what about terminal editors like Vim? It is highly extensible editor, it’s fast, and is available on all systems. That’s true, but there’s a problem I can’t live with. Neovim. At first I was a fan of Vim and Neovim competition, thinking that Neovim can bring long awaited features, and fix major problems with Vim, but after using it for several years I’ve realized that it brought mostly bugs and incompatibilities. Yeah, the features were neat, but for some reason Vim started implementing these new features in its own way, making them incompatible with Neovim’s. Which resulted in plugins having half backed support of both editors. At this point I was struggling with snippets, intelligent completion, certain UI plugins. Maybe currently situation is better, I don’t know. And I don’t want to know, actually.

Given that I don’t want to turn Emacs to Vim copy. I want Emacs to be Emacs. If I wanted to use Vim I’d use Vim. Although there’s also Kakoune, which is great alternative to Vim. If you need terminal editor that is fast and minimalist, yet provides many features via POSIX tools, check it out.

Emacs is hard, it has lame keybindings, you need to do a lot of tinkering in order to get it work how you like, it has bigger footprint compared to Vim, but still it is a great editor with huge community.

About this document

This file is my actual configuration file for Emacs. I do all edits here, then compile it to .el file. It is convenient because Emacs is complex tool, so I can leave comments by myself, but I don’t need to keep track over two files, like in Kakoune or Vim, since this document is my configuration file and a bunch of notes simultaneously.

I try to maintain my Emacs configuration simple and small keeping as much of vanilla Emacs behaviors as I can, but I’m a sane person so some aspects which are actually insane in my opinion are changed. Like for example, why on Earth Emacs scrolls buffer by chunks? It already has C-v and A-v for this. Anyway, I’ll make explicit annotation if I change something so it won’t be hard to notice.

Now to the configuration itself.

Configuration

As I’ve mentioned earlier, I’m using Org Mode function called org-babel-tangle to build my init.el file. This means that you only need this file in order to get my config and compile it with org-babel-tangle function for the first time. After that, every time this file is edited and saved it gets re-tangled automatically.

Since I’m trying to follow Emacs conventions, good Emacs Lisp file starts with commentary:

;;; init.el --- Emacs main configuration file -*- lexical-binding: t; -*-
;;;
;;; Commentary:
;;; Emacs config by Andrey Orst.
;;; This file was automatically generated by `org-babel-tangle'.
;;; Do not change this file.  Main config is located in .emacs.d/config.org
;;;
;;; Code:

After this point configurations will be split in different sections grouped by something in common to them all.

Startup

Emacs starts slow, and I like to close it when I’m not using it. I’m not closing and opening it for every file, of like every 15 minutes, but still, the faster it starts - the better for me.

Once I switch to Emacs 27 most of these settings will be tangled to early-init.el.

Garbage collection and file handler

The main problem with Emacs startup file is garbage collection system. It invoked so many times on startup that it causes quite big impact on startup time. We’re talking seconds. One can raise limit when to trigger garbage collection, but this will end up in unpleasant editing experience. So I’m declaring these variables to store default values for the GC, to restore them after initialization is finished:

(defvar my--gc-cons-threshold gc-cons-threshold)
(defvar my--gc-cons-percentage gc-cons-percentage)
(defvar my--file-name-handler-alist file-name-handler-alist)

Now we can tweak GC. We need to raise threshold to prevent it running:

(setq gc-cons-threshold 402653184
      gc-cons-percentage 0.6
      message-log-max 16384
      auto-window-vscroll nil
      package-enable-at-startup nil
      file-name-handler-alist nil)

We need a hook that restores initial values once initialization done:

(add-hook 'after-init-hook
          (lambda ()
            (setq gc-cons-threshold my--gc-cons-threshold
                  gc-cons-percentage my--gc-cons-percentage
                  file-name-handler-alist my--file-name-handler-alist)))

package.el initialization

Doom Emacs uses this to speedup initialization, so I’ll include it too. In the Doom Emacs Wiki it is stated that Package.el initialization is expensive.

(setq package-enable-at-startup nil
      package--init-file-ensured t)

Melpa is a package repository, that I use to get packages. Since all packages that I need can be obtained from there, I didn’t bothered with different methods of installation.

(require 'package)
(setq package-enable-at-startup nil)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/") t)

Note for Windows users: change https to http

This basic piece of code is used to update package list if there’s none.

(package-initialize)
(when (not package-archive-contents)
  (package-refresh-contents))

Default Behavior Fixes

Emacs is old. I understand that back then it could be appropriate decisions, but as of today they are completely obliterated by the passage of time.

I’m not trying to insult on anyone, but usage of the bell is just insane. It’s not 1980’s, computers have their own speakers and rich displays, why would anyone want to use builtin speaker? Why everyone should know when I’m mistaken? Disable bell.

(setq ring-bell-function 'ignore)

I don’t know who decided that having backups all over the place is good idea, but I don’t think alike.

(setq backup-by-copying t
      create-lockfiles nil
      backup-directory-alist '(("." . "~/.cache/emacs-backups"))
      auto-save-file-name-transforms '((".*" "~/.cache/emacs-backups" t)))

For some reason in some situations Emacs asks for typing yes or no explicitly, instead of accepting y or n. This can be fixed with this.

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

Emacs displays pressed key after some time but it’s too fast for me, because I use Emacs on the phone too, and the keyboard is a bit small for lightning fast typing.

(add-hook 'after-init-hook (lambda () (setq echo-keystrokes 5)))

I don’t want menus on shift clicks:

(global-unset-key (kbd "S-<down-mouse-1>"))
(global-unset-key (kbd "<mouse-3>"))
(global-unset-key (kbd "S-<mouse-3>"))

I don’t like how Emacs handles scrolling, and that it uses tab characters by default. I need tab characters only when I work with C code and Makefile rules.

(setq-default indent-tabs-mode nil
              scroll-step 1
              scroll-conservatively 10000
              auto-window-vscroll nil)

I don’t like custom, and especially would like to stop it from interfering to my configuration files, so lets move it to separate file.

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file :noerror)

I also don’t like that Emacs has disabled commands, and enabling them modifies my init.el that I don’t modify myself. Let’s put those commands to custom.el instead:

(defadvice en/disable-command (around put-in-custom-file activate)
  "Put declarations in `custom-file'."
  (let ((user-init-file custom-file))
    ad-do-it))

Another feature I want is history between sessions. I’m not sure why it isn’t default.

(savehist-mode 1)

I use two keyboard layouts: qwerty and йцукен, therefore I need a way to switch between those without loosing ability to use such chords as C-x which will become a C-ч if I select Cyrillic layout in the OS. Luckily for me, Emacs provides a method to switch layouts within Emacs with C-\, so all common shortcuts will still work fine.

(setq default-input-method 'russian-computer)

I’m not using russian-jcuken here because it represents the “typewriter” layout with number-line inverted, so to access numbers I need to use Shift key. russian-computer doesn’t have this problem.

I don’t find displaying position in modeline really great idea, because I need to move my eyes too much on big screen just to see what line number I’m currently on.

(setq column-number-mode nil
      line-number-mode nil
      size-indication-mode nil
      mode-line-position nil)

I don’t want my files to contain trailing whitespaces, so this hook will get rid of those automatically for me.

(add-hook 'before-save-hook 'delete-trailing-whitespace)

One thing that bothered me is that C-g is universal way to cancel things in Emacs, gut you have to be in exact window to cancel action. This function intelligently tries to cancel regardless of active window:

;; suppress byte-compiler warnings
(declare-function minibuffer-keyboard-quit "delsel" (&optional ARGS))

(defun my/escape ()
  "Quit in current context.

When there is an active minibuffer and we are not inside it close
it.  When we are inside the minibuffer use the regular
`minibuffer-keyboard-quit' which quits any active region before
exiting.  When there is no minibuffer `keyboard-quit' unless we
are defining or executing a macro."
  (interactive)
  (cond ((active-minibuffer-window)
         (if (minibufferp)
             (minibuffer-keyboard-quit)
           (abort-recursive-edit)))
        ((when (bound-and-true-p iedit-mode)
           (iedit-mode)))
        (t
         ;; ignore top level quits for macros
         (unless (or defining-kbd-macro executing-kbd-macro)
           (keyboard-quit)))))

Let’s remap default keyboard-quit key with this function.

(global-set-key [remap keyboard-quit] #'my/escape)

Use Package

I use use-package to install, load and configure my packages. I find this way very consistent and easy to understand and maintain. What I like about Use Package is that it automatically installs packages on Emacs startup. But unfortunately it can’t be installed by itself, so we need a way to install Use Package in case I load this config on fresh system.

(defun my/ensure-installed (package)
  "Ensure that PACKAGE is installed."
  (when (not (package-installed-p package))
    (package-install package)))

Now we can install and configure Use Package itself:

(my/ensure-installed 'use-package)
(require 'use-package)
(setq use-package-always-ensure t)

All other packages were split to individual categories much like Emacs settings above. This helps me to keep track of things in different packages and do not rely on search that much.

User Interface

All user interface related settings are presented in this section. I’m big fan of DOOM themes, and modeline, so I’ll refer to them quite a lot here.

Default tweaks

Emacs displays splash screen once started with no files. I don’t need it, and I would like to start with *scratch* buffer instead. Also I would like *scratch* buffer to be an Org buffer with no initial message:

(setq inhibit-splash-screen t
      initial-major-mode 'org-mode
      initial-scratch-message "")

I don’t need all these tooltips, menus and scrollbars. Emacs enables all of those by default, so let’s disable them as early as possible:

(tooltip-mode -1)
(menu-bar-mode -1)
(fset 'menu-bar-open nil)

(when window-system
  (scroll-bar-mode -1)
  (tool-bar-mode -1))

Also let’s use bar cursor when in window system, which is visible only in active window:

(when window-system
  (setq-default cursor-type 'bar
                cursor-in-non-selected-windows nil))

Emacs uses weird method of naming a window, I’d like to see a file I’m currently working on in the task bar in case my Emacs was minimized for some reason.

(setq-default frame-title-format '("%b — Emacs"))

Also, why initial frame size is so small? When I launch Emacs in terminal it occupies whole window. When I launch it under graphical environment it defaults to really small window, which is smaller than my terminal. My window manager doesn’t adjust window size automatically so let’s just set little bit bigger initial size:

(when window-system
  (set-frame-size (selected-frame) 190 52))

With this size I’m able to fire up Emacs and get enough space to fit two vertically aligned windows and a file explorer together.

I like this Adobe Source Code Pro font. Even if I use Hack in every other editor, for some reason I find this font just right for Emacs.

(set-face-attribute 'default nil :font "Source Code Pro-10")

I want fringes to appear only in certain buffers where I really need them, so let’s disable it entirely on startup and set them with some hooks later.

(when window-system
  (fringe-mode 0))

But this adds another problem. Emacs displays $ sign at the end of the window if line is being truncated. Damn, why it is so hard to make it work how I want.. And you can’t simply set standard-display-table slot to empty space, because it isn’t ready on Emacs init.

(when window-system
  (or standard-display-table
      (setq standard-display-table (make-display-table)))
  (set-display-table-slot standard-display-table 0 ?\ ))

Small tweak to mode line, that disables changing appearance in non active window.

(setq mode-line-in-non-selected-windows nil)

Now I can list some amazing packages that provide some real GUI goodies to Emacs.

All The Icons

This package provides nice icons for emacs via custom fonts. Just don’t forget to use M-x all-the-icons-install-fonts RET after install.

(use-package all-the-icons)

I don’t use this package directly yet, but some other packages, like DOOM Themes do.

DOOM Themes

This theme collection was developed for DOOM Emacs, and I find these themes actually great on its own. This package contains nice variant of Atom-like One theme which I like, but it is also an amazingly well put package, which defines colors for pretty much everything in Emacs. It also features Treemacs configuration, which is nice, but I want to change a little bit of it.

(use-package doom-themes
  :commands (doom-themes-org-config
             doom-themes-treemacs-config)
  :functions (all-the-icons-octicon)
  :defines (treemacs-icon-root-png
            doom-treemacs-use-generic-icons
            treemacs-icon-open-png
            treemacs-icon-closed-png
            treemacs-icon-fallback
            treemacs-icons-hash
            treemacs-icon-text)
  :init
  (load-theme 'doom-one t)
  (doom-themes-org-config)
  (doom-themes-treemacs-config)
  (eval-after-load 'treemacs
    (lambda ()
      "Adjust DOOM Themes settings for Treemacs.

This lambda function sets root icon to be regular folder icon,
and adds `chevron' icons to directories in order to display
opened and closed states.  Also it indents all file icons with
two spaces to match new directory icon indentation."
      (unless (require 'all-the-icons nil t)
        (error "`all-the-icons' isn't installed"))
      (let ((all-the-icons-default-adjust 0))
        (setq treemacs-icon-root-png
              (concat " " (all-the-icons-octicon
                           "file-directory"
                           :v-adjust 0
                           :face '(:inherit font-lock-doc-face :slant normal))
                      " ")
              treemacs-icon-dir-open
              (concat (all-the-icons-octicon
                       "chevron-down"
                       :height 0.75
                       :face '(:inherit font-lock-doc-face :slant normal))
                      " "
                      (all-the-icons-octicon
                       "file-directory"
                       :v-adjust 0
                       :face '(:inherit font-lock-doc-face :slant normal))
                      " ")
              treemacs-icon-dir-closed
              (concat (all-the-icons-octicon
                       "chevron-right"
                       :height 0.9
                       :face '(:inherit font-lock-doc-face :slant normal))
                      " "
                      (all-the-icons-octicon
                       "file-directory"
                       :v-adjust 0
                       :face '(:inherit font-lock-doc-face :slant normal))
                      " "))
        (setq treemacs-icons-hash (make-hash-table :size 200 :test #'equal)
              treemacs-icon-fallback (concat "  " (all-the-icons-octicon "file-code" :v-adjust 0) " ")
              treemacs-icon-text treemacs-icon-fallback))
      (treemacs-define-custom-icon (concat "  " (all-the-icons-octicon "file-media" :v-adjust 0))
                                   "png" "jpg" "jpeg" "gif" "ico" "tif" "tiff" "svg" "bmp"
                                   "psd" "ai" "eps" "indd" "mov" "avi" "mp4" "webm" "mkv"
                                   "wav" "mp3" "ogg" "midi")
      (treemacs-define-custom-icon (concat "  " (all-the-icons-octicon "file-text" :v-adjust 0))
                                   "md" "markdown" "rst" "log" "org" "txt"
                                   "CONTRIBUTE" "LICENSE" "README" "CHANGELOG")
      (treemacs-define-custom-icon (concat "  " (all-the-icons-octicon "file-code" :v-adjust 0))
                                   "yaml" "yml" "json" "xml" "toml" "cson" "ini"
                                   "tpl" "erb" "mustache" "twig" "ejs" "mk" "haml" "pug" "jade")))
  (setq doom-themes-enable-bold t
        doom-themes-enable-italic t))

Emacs uses white titlebar, but since I’m using dark color scheme I want title bar to be dark as well. It is quite difficult to achieve this in GNOME Shell, but I found this code on the internet:

(defun my/set-frame-dark (&optional frame)
  "Set FRAME titlebar colorscheme to dark variant."
  (with-selected-frame (or frame (selected-frame))
    (call-process-shell-command
     (concat "xprop -f _GTK_THEME_VARIANT 8u -set"
             " _GTK_THEME_VARIANT \"dark\" -name \""
             (frame-parameter frame 'name)
             "\""))))

So if I use window-system I want this function to run during initialization process, and when new frame is created.

(when window-system
  (my/set-frame-dark)
  (add-hook 'after-make-frame-functions 'my/set-frame-dark :after))

There’s also another great package that adds nice colors for buffers that are actually used for editing files: Solaire Mode.

Solaire Mode

This package helps distinguish buffers that have file opened and buffers that are for utilities like file browser. Also it has cool name! \[T]/

I really want Solaire to treat *scratch* buffer as real. For that I need to define my own function that will decide if buffer is real or not:

(defun my/real-buffer-p ()
  "Determines whether buffer is real."
  (or (and (not (minibufferp))
           (buffer-file-name))
      (string-equal (buffer-name) "*scratch*")))

Now we can set up Solaire Mode and assign my/real-buffer-p to solaire-mode-real-buffer-fn.

(use-package solaire-mode
  :commands (solaire-global-mode
             solaire-mode-swap-bg
             turn-on-solaire-mode
             solaire-mode-in-minibuffer
             solaire-mode-reset)
  :config
  (add-hook 'focus-in-hook #'solaire-mode-reset)
  (add-hook 'after-revert-hook #'turn-on-solaire-mode)
  (add-hook 'change-major-mode-hook #'turn-on-solaire-mode)
  (add-hook 'org-capture-mode-hook #'turn-on-solaire-mode :after)
  (add-hook 'org-src-mode-hook #'turn-on-solaire-mode :after)
  :init
  (setq solaire-mode-real-buffer-fn #'my/real-buffer-p)
  (solaire-global-mode +1)
  (solaire-mode-swap-bg))

If only certain buffers could be so grossly incandescent have damn fringes! With this package Emacs becomes way more like true editor with actual graphical interface, but these fringes drive me crazy. That’s why I wrote this function, that enables fringes only in buffers that are affected by Solaire Mode:

(defun my/real-buffer-setup (&rest _)
  "Wrapper around `set-window-fringes' function."
  (when (my/real-buffer-p)
    (set-window-fringes nil 8 8 nil)
    (setq-local scroll-margin 3)))

Let’s assign it to these hooks:

(add-hook 'window-configuration-change-hook 'my/real-buffer-setup)
(add-hook 'org-capture-mode-hook 'my/real-buffer-setup)
(add-hook 'org-src-mode-hook 'my/real-buffer-setup)

Now my buffers looks really nice, but my modeline doesn’t. Luckily there’s another great package from DOOM Emacs - DOOM Modeline.

DOOM Modeline

I’ve tried bunch of different modelines:

  • Spaceline Great modeline, but I was experiencing slowdowns in Emacs startup, and I like my Emacs to start fast.
  • Telephone Line Another good modeline, but I was lazy to configure it.
  • Powerline This was first what I tried because I was using something similar in Vim. I don’t remember why I dropped it.
  • Moody Really lightweight configuration for default modeline, which I’ve used for quite a some time.

They’re all great, but I esperienced some troubles with DOOM Theme that I use, so I’ve decided to try out DOOM Modeline. And turns out it’s great!

(use-package doom-modeline
  :commands (doom-modeline-mode
             doom-modeline-set-selected-window)
  :init (doom-modeline-mode 1)
  (set-face-attribute 'doom-modeline-buffer-path nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-buffer-file nil :foreground (doom-color 'fg) :weight 'semi-bold)
  (set-face-attribute 'doom-modeline-buffer-modified nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-buffer-major-mode nil :foreground (doom-color 'fg) :weight 'semi-bold)
  (set-face-attribute 'doom-modeline-buffer-minor-mode nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-project-parent-dir nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-project-dir nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-project-root-dir nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-highlight nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-panel nil :foreground (doom-color 'fg) :background (doom-color 'bg-alt))
  (set-face-attribute 'doom-modeline-debug nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-info nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-warning nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-urgent nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-unread-number nil :foreground (doom-color 'fg) :weight 'normal)
  (set-face-attribute 'doom-modeline-bar nil :foreground (doom-color 'fg) :background (doom-color 'bg-alt) :weight 'normal)
  :config
  (advice-add #'select-window :after #'doom-modeline-set-selected-window)
  (setq doom-modeline-bar-width 3
        doom-modeline-major-mode-color-icon nil
        doom-modeline-buffer-file-name-style 'file-name
        doom-modeline-minor-modes t
        doom-modeline-lsp nil
        find-file-visit-truename t))

I guess that this is all for DOOM packages for now, but I have to say that I really appreciate @hlissner work. These packages already make Emacs look outstanding. But to make it look more like a traditional modern text editor we need a file explorer. Emacs has builtin package for this, named speedbar, but It uses external frame and has so many features that I don’t know if I really need. Thankfully there’s another package that provides filetree: Treemacs.

Treemacs

With this package, and its supplement packages I can have a consistent filetree inside my Emacs frame. Which is good. But there’s more: DOOM Themes support this package as well, which means that Treemacs will look just as great as DOOM themed Emacs!

(use-package treemacs
  :commands (treemacs
             treemacs-follow-mode
             treemacs-filewatch-mode
             treemacs-fringe-indicator-mode
             doom-color
             doom-modeline-focus
             treemacs-TAB-action)
  :bind (("<f8>" . treemacs)
         ("<f9>" . treemacs-select-window))
  :config
  (set-face-attribute 'treemacs-root-face nil
                      :foreground (doom-color 'fg)
                      :height 1.0
                      :weight 'normal)
  :init
  (defun treemacs-expand-all-projects (&optional _)
    "Expand all projects."
    (save-excursion
      (treemacs--forget-last-highlight)
      (dolist (project (treemacs-workspace->projects (treemacs-current-workspace)))
        (-when-let (pos (treemacs-project->position project))
          (when (eq 'root-node-closed (treemacs-button-get pos :state))
            (goto-char pos)
            (treemacs--expand-root-node pos)))))
    (treemacs--maybe-recenter 'on-distance))
  (add-hook 'treemacs-mode-hook
            (lambda ()
              (setq line-spacing 4)))
  (setq treemacs-width 27
        treemacs-is-never-other-window t
        treemacs-space-between-root-nodes nil)
  (treemacs-follow-mode t)
  (treemacs-filewatch-mode t)
  (treemacs-fringe-indicator-mode nil)
  (when window-system
    (treemacs)
    (treemacs-expand-all-projects)))

This supplement package makes some nice integration with Projectile package that I’m using. It doesn’t really need any configuration.

(use-package treemacs-projectile)

Same story with Magit integration.

(use-package treemacs-magit)

Before using Treemacs I was a happy user of another filetree package: Neotree. Unfortunately, last commit to this package dates to Nov 21, 2018, and since then there were no maintenance, and I was experiencing a bug which affects my usage of another amazing package Eyebrowse so I’ve decided to move to Treemacs, since it works fine with it.

Eyebrowse

This package imitates virtual workspaces, or virtual frames inside single Emacs frame. This is really useful, when you have bunch of windows, and you want to open new set but don’t want to loose other window configurations. It works much like Vim tabs, or Tmux windows.

(use-package eyebrowse
  :commands eyebrowse-mode
  :init
  (eyebrowse-mode t))

Diff HL Mode

This package provides nice way of diffing buffers. It displays thin bar inside fringe marking unstaged changes.

(use-package diff-hl
  :commands global-diff-hl-mode
  :init
  (add-hook 'diff-hl-mode-hook #'my/setup-fringe-bitmaps)
  (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
  (global-diff-hl-mode 1))

But I don’t quite like how it looks. So there’s another thing that I borrowed from DOOM Emacs configuration. This function sets fringe diff highlighting bitmaps to get nice looking thin vertical bars which play nicely with DOOM Themes and DOOM Modeline:

(defun my/setup-fringe-bitmaps ()
  "Set fringe bitmaps."
  (define-fringe-bitmap 'diff-hl-bmp-top [224] nil nil '(center repeated))
  (define-fringe-bitmap 'diff-hl-bmp-middle [224] nil nil '(center repeated))
  (define-fringe-bitmap 'diff-hl-bmp-bottom [224] nil nil '(center repeated))
  (define-fringe-bitmap 'diff-hl-bmp-insert [224] nil nil '(center repeated))
  (define-fringe-bitmap 'diff-hl-bmp-single [224] nil nil '(center repeated))
  (define-fringe-bitmap 'diff-hl-bmp-delete [240 224 192 128] nil nil 'top))

Speaking of defining fancier things. This configuration is written in Org Mode, so maybe we could have nice symbols for headings? Of course we can, there’s a package for it: Org Bullets.

Org Bullets

This package makes org headings use fancy bullet symbols. But I don’t quite like those, so I set them to these four symbols: , , , .

(use-package org-bullets
  :commands org-bullets-mode
  :config
  (setq-default org-bullets-bullet-list
                '("" "" "" "" "" "" "" "" "" "" ""))
  :init
  (when window-system
    (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))))

Minions

This package implements a menu that lists all enabled minor modes. Emacs mode line can become pretty long, so this can be handy, and perhaps I don’t need to use :diminish everywhere anymore.

(use-package minions
  :commands minions-mode
  :config (setq minions-direct '(multiple-cursors-mode
                                 flycheck-mode
                                 flyspell-mode
                                 parinfer-mode))
  :init (minions-mode 1))

Eldoc Box

A package that creates child frame for eldoc documentation messages.

(use-package eldoc-box
  :commands eldoc-box-hover-mode
  :config
  (set-face-attribute 'eldoc-box-border nil :background "#191B20")
  :init
  (advice-add 'eldoc-box-hover-mode :after 'eldoc-box-hover-at-point-mode)
  (add-hook 'eglot--managed-mode-hook #'eldoc-box-hover-mode t)
  (setq eldoc-box-max-pixel-width 1920
        eldoc-box-max-pixel-height 1080))

Window Divider

This package changes window dividers appearance. I want to set them to be only 1 pixel wide because if this mode is turned on, fringes don’t loose 1 pixel of width.

(when window-system
  (setq window-divider-default-right-width 1)
  (window-divider-mode 1))

Modes

In Emacs world Modes are the main thing. I’m using Org Mode quite heavily right now, as well as a lot of other modes. Configurations of these modes are stored here.

Org Mode

Org Mode is a great mode for taking notes, managing to-do lists, writing books, literate programming, and many other things. I primary use it for taking notes on different programming languages, and manage my Emacs configuration with it.

Startup Settings

For Org Mode I need spell checking to be enabled by default. Also Org-mode plays nicely with justification, so I’ll set it to full, as I think that it is the most appropriate format for text. Org files are capable of displaying images, so let’s enable them by default. There’s also a nice mode called org-indent-mode that makes easier to see different sub-trees. Speaking of sub-trees, org-startup-folded can be set to =’content=, so every document will look like table of contents by default. Last but not least, let’s enable auto-fill-mode for org mode, so all text will be folded and re-filled after its length reaches the fill-column value. document

(require 'org)
(add-hook 'org-mode-hook
          (lambda()
            (flyspell-mode)
            (setq default-justification 'full
                  org-startup-with-inline-images t
                  org-startup-folded 'content
                  org-hide-emphasis-markers t
                  org-adapt-indentation nil
                  org-hide-leading-stars t
                  org-highlight-latex-and-related '(latex)
                  revert-without-query '(".*\.pdf"))
            (auto-fill-mode)
            (set-face-attribute 'org-document-title nil :height 1.6)
            (set-face-attribute 'org-level-1        nil :height 1.4)
            (set-face-attribute 'org-level-2        nil :height 1.2)
            (set-face-attribute 'org-level-3        nil :height 1.0)))

This will fontify code inside SRC_BLOCK sections:

(setq org-src-fontify-natively t)

Source Code Blocks

Since I’m using Emacs to configure Emacs, I want flycheck to run while I’m editing emacs-lisp source blocks. But I find it distracting to see warnings like ;;; Code, so let’s disable it:

(defvar flycheck-disabled-checkers)

(defun my/disable-flycheck-in-org-src-block ()
  "Disable checkdoc in emacs-lisp buffers."
  (setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc)))

Now we need to call this function when editing source blocks:

(add-hook 'org-src-mode-hook 'my/disable-flycheck-in-org-src-block)

I often edit my configuration, so it would be cool to update Emacs config automatically on save. This function will handle it:

(defun my/org-tangle-on-config-save ()
  "Tangle source code blocks when configuration file is saved."
  (when (string= buffer-file-name (file-truename "~/.emacs.d/config.org"))
    (org-babel-tangle)
    (byte-compile-file "~/.emacs.d/init.el")))

To actually make it work we need to add this hook:

(add-hook 'after-save-hook 'my/org-tangle-on-config-save)

Inline Images

Since Org-mode allows inline images, we need a way to update them, if image changes for some reason. We will use hook to call this function is supposed to automatically update inline images in org buffers, when for some reason image was changed.

(defvar org-inline-image-overlays)

(defun my/org-update-inline-images ()
  "Update inline images in Org-mode."
  (when org-inline-image-overlays
    (org-redisplay-inline-images)))

(add-hook 'org-babel-after-execute-hook 'my/org-update-inline-images)
Inline LaTeX Preview

Latex preview feature is really awesome, but I don’t want produced images to be stored in plain sight. At least use hidden folder.

(setq org-preview-latex-image-directory ".ltximg/")

Org Mappings

I don’t like that my S-tab mapping for Company triggers org-shifftab function, so I’ll remap it to use C-tab instead.

(define-key org-mode-map [backtab] nil)
(define-key org-mode-map [S-iso-lefttab] nil)
(define-key org-mode-map [C-tab] nil)
(define-key org-mode-map [C-tab] 'org-shifttab)

Syntax Highlighting in Exported PDFs

To highlight code blocks when exporting to LaTeX we need minted package installed system-wide and this code (taken from emacs.stackexchange.com):

(require 'ox-latex)
(setq org-latex-listings 'minted)

Minted creates annoying directories named _minted-documentname, and I don’t want them to be in plain sight. So this code here will put those directories to temporary folder on my system.

(defvar minted-cache-dir
  (file-name-as-directory
   (expand-file-name ".minted/\\jombname"
                     temporary-file-directory)))

(add-to-list 'org-latex-packages-alist
             `(,(concat "cachedir=" minted-cache-dir)
               "minted" nil))

Now we need to tell pdflatex to use these escape sequence for proper colors. This part is worth checking for a more proper way of handling highlighting, because right now code blocks written in language that minted doesn’t support will not be exported to PDF at all. But I don’t know how to fix this.

(setq org-latex-pdf-process
      '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Cleanup After Export

Since I’m exporting my notes to PDF as final file format, I don’t need intermediary .tex file to stay. Let’s assume that these files are log files, so Org mode will remove those after exporting is finished.

(eval-after-load 'org
  '(add-to-list 'org-latex-logfiles-extensions "tex"))

This will get rid of .tex files.

Executable Languages

A nice feature of Org-mode over Markdown is that we can execute source code blocks with a keybinding and see results inside the document itself. Let’s define what languages can be executed with C-c C-c shortcut from org mode:

(org-babel-do-load-languages
 'org-babel-load-languages
 '((gnuplot . t)
   (scheme . t)))

And to prevent Emacs from constantly asking if I want to execute source code, lets set org-confirm-babel-evaluate to nil

(setq org-confirm-babel-evaluate nil)

Paragraphs

I would like to use the same level headings after certain part, so all my deeply nested notes contained right section separator headings. This is a hacky way but it works:

(add-to-list 'org-latex-classes
             '("article"
               "\\documentclass{article}"
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))

Markdown Export

I don’t think that I gonna use this much, but just in case if I’ll ever decide to export Org to Markdown, I’ll need this:

(require 'ox-md nil t)

Flycheck Mode

Flycheck is useful tool, which can provide syntax checking on the fly. Let’s enable it for bunch of languages here.

(add-hook 'emacs-lisp-mode-hook 'flycheck-mode)

Doc View Mode

Default resolution is too low:

(setq-default doc-view-resolution 192)

Display Line Numbers Mode

This mode provides line numbers on the left side of the window, which doesn’t lag. I don’t like that it changes width, so let’s change it:

(setq-default display-line-numbers-grow-only t
              display-line-numbers-width-start t)

Prog Mode

This isn’t really a configuration of Prog Mode itself, but some hooks that change how programming related modes behave.

Here, I want all programming modes to include line numbers by default:

(add-hook 'prog-mode-hook 'display-line-numbers-mode)

One feature that I think is really important, especially when working with lisp code, is ability to see matching bracket when cursor stands near the other bracket. So I enable it for every programming language.

(add-hook 'prog-mode-hook 'show-paren-mode)

CC Mode

These settings are for editing C source files. First, to suppress byte compilation warning lets declare variables:

(defvar c-basic-offset)
(defvar c-default-style)

Now we can create a hook that will run for all C related modes:

(add-hook 'c-mode-common-hook
          (lambda ()
            (yas-minor-mode)
            (electric-pair-mode)
            (setq c-basic-offset 4
                  c-default-style "linux"
                  indent-tabs-mode t
                  tab-width 4)))

Also, let’s fix highlighting a bit:

(mapc (lambda (mode)
        (progn
          (font-lock-add-keywords
           mode
           '(("\\<\\(\\sw+\\) *(" 1 'font-lock-function-name-face)
             ("\\(->\\|\\.\\) *\\<\\([_a-zA-Z]\\w+\\)" 2 'error)
             ("\\<-?\\(0x[0-9a-fA-F]+\\|[0-9]+\\(\\.[0-9]+\\)?\\)\\([uU][lL]\\{0,2\\}\\|[lL]\\{1,2\\}[uU]?\\|[fFdDiI]\\|\\([eE][-+]?\\d+\\)\\)?\\|'\\(\\.?\\|[^'\\]\\)'" 0 'all-the-icons-lorange)
             ("->\\|\\.\\|*\\|+\\|/\\|-\\|<\\|>\\|&\\||\\|=\\|\\[\\|\\]" 0 'font-lock-constant-face))
           t)))
      '(c-mode c++-mode))

Markdown

Sometimes I need to edit Markdown documents, so this package is handy.

(use-package markdown-mode
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (defvar markdown-command "multimarkdown"))

For markdown mode I would like to have automatic spell checking and filling. Basically the same setup as for Org Mode.

(add-hook 'markdown-mode-hook
          '(lambda()
             (flyspell-mode)
             (setq fill-column 80
                   default-justification 'left)
             (auto-fill-mode)))

Rust

I’d like to have Rust syntax highlighting and some basic facilities, since I’m planning to write my exercises in org mode. Since I make notes in Org-mode, and I started to use it for Rust too, I need some settings for Rust mode.

(use-package rust-mode
  :config (add-hook 'rust-mode-hook
                    '(lambda()
                       (racer-mode)
                       (yas-minor-mode)
                       (electric-pair-mode)
                       (setq company-tooltip-align-annotations t))))

This also means that I could use something to automatically complete Rust language facilities. Rust ecosystem provides two options for that: Racer, and RLS. I’m planning to use Racer when RLS isn’t possible to use, like in Org Mode.

(use-package racer
  :config (add-hook 'racer-mode-hook #'eldoc-mode))

There’s also a package for Cargo integration:

(use-package cargo
  :config
  (add-hook 'rust-mode-hook 'cargo-minor-mode))

Once again, additional highlighting, but for Rust:

(modify-syntax-entry ?_ "w" rust-mode-syntax-table)
(font-lock-add-keywords
 'rust-mode
 '(("\\<\\(\\sw+\\) *(" 1 'font-lock-function-name-face)
   ("\\. *\\<\\([_a-zA-Z]\\w+\\)" 1 'error)
   ("\\<-?\\(0x[0-9a-fA-F]+\\|[0-9]+\\(\\.[0-9]+\\)?\\)\\([uU][lL]\\{0,2\\}\\|[lL]\\{1,2\\}[uU]?\\|[fFdDiI]\\|\\([eE][-+]?\\d+\\)\\)?\\|'\\(\\.?\\|[^'\\]\\)'" 0 'all-the-icons-lorange)
   ("->\\|\\.\\|*\\|+\\|/\\|-\\|<\\|>\\|&\\||\\|=\\|\\[\\|\\]\\|\\^" 0 'font-lock-constant-face))
 t)

TOML

toml-mode helps with highlighting of TOML files, which Rust uses to configure project.

(use-package toml-mode)

EditorConfig

Speaking of formatting, .editorconfig file provides a nice way to synchronize my editor configurations between projects and different editors.

(use-package editorconfig
  :commands editorconfig-mode
  :config
  (editorconfig-mode 1))

Nov.el

Package that makes it possible to read ePUB formatted books inside Emacs.

(use-package nov
  :commands nov-mode
  :functions solaire-mode
  :mode "\\.epub$"
  :init
  (setq nov-text-width 80)
  (add-hook 'nov-mode-hook #'visual-line-mode)
  (add-hook 'nov-mode-hook #'solaire-mode))

Tools

Additional packages that change how Emacs works, providing more comfortable user experience.

Ansi Term

I’m using it more and more, so I’ve decided that I should have a proper shortcut for it. This function toggles bottom split with ansi-term inside it:

(defun my/ansi-term-toggle ()
  "Toggle ansi-term window on and off with the same command."
  (interactive)
  (cond ((get-buffer-window "*ansi-term*")
         (ignore-errors (delete-window
                         (get-buffer-window "*ansi-term*"))))
        (t (split-window-below)
           (other-window 1)
           (cond ((get-buffer "*ansi-term*")
                  (switch-to-buffer "*ansi-term*"))
                 (t (ansi-term "bash"))))))

Ctrl+` seems great shortcut for this.

(global-set-key (kbd "C-`") 'my/ansi-term-toggle)

Also, when I exit terminal, I’d like to kill its window automatically:

(defun my/autokill-when-no-processes (&rest _)
  "Kill buffer and its window when there's no processes left."
  (when (null (get-buffer-process (current-buffer)))
      (kill-buffer (current-buffer))
      (delete-window)))

(advice-add 'term-handle-exit :after 'my/autokill-when-no-processes)

Hydra

Hydra is something like ability to create mappings that sit in it’s own mode, like different user modes in Kakoune. It allows me to press some shortcut and be locked in a mode-like state where keys that are related to this prefix shortcut are behaving in terms of this shortcut.

This package will be used in many other package configurations to provide sane keybindings.

(use-package hydra
  :commands (hydra-default-pre
             hydra-keyboard-quit
             hydra--call-interactively-remap-maybe
             hydra-show-hint
             hydra-set-transient-map)
  :bind (("<f5>" . hydra-zoom/body))
  :config
  (defhydra hydra-zoom (:hint nil)
    "Scale text"
    ("+" text-scale-increase "in")
    ("-" text-scale-decrease "out")
    ("0" (text-scale-set 0) "reset")))

Geiser

Since I mostly use Emacs for studying LISP, and I’m reading SICP which uses Scheme as main LISP flavor for explanations and exercises, I need a tool to run Scheme, and Geiser seems like the most viable option here, since it also provides completion for it.

(use-package geiser
  :config
  (add-hook 'scheme-mode-hook 'geiser-mode)
  :init
  (setq-default geiser-active-implementations '(guile)
                geiser-default-implementation 'guile))

Parinfer

Now that’s a quality package. It makes writing LISP so easy, that I’ve never thought it could be.

There’s a lot of configuration here, but it was taken as is from Parinfer Mode repository.

(use-package parinfer
  :commands parinfer-mode
  :bind
  (("C-," . parinfer-toggle-mode))
  :init
  (progn
    (setq parinfer-extensions
          '(defaults
             pretty-parens
             smart-tab
             smart-yank))
    (add-hook 'clojure-mode-hook #'parinfer-mode)
    (add-hook 'emacs-lisp-mode-hook #'parinfer-mode)
    (add-hook 'common-lisp-mode-hook #'parinfer-mode)
    (add-hook 'scheme-mode-hook #'parinfer-mode)
    (add-hook 'lisp-mode-hook #'parinfer-mode)))

Flx

This package provides some kind of fuzzy matching for Emacs.

(use-package flx)

Ivy and Counsel

Ivy is a narrowing framework like Helm, but much lighter in my experience.

(use-package ivy
  :commands ivy-mode
  :init
  (setq ivy-use-virtual-buffers t
        enable-recursive-minibuffers t)
  :bind (("C-x C-b" . ivy-switch-buffer)
         ("C-x b" . ivy-switch-buffer))
  :config
  (setq ivy-re-builders-alist '((t . ivy--regex-fuzzy))
        ivy-count-format ""
        ivy-display-style nil
        ivy-minibuffer-faces nil)
  (ivy-mode 1))

It integrates with Counsel that handles minibuffer, so let’s install it too. I’m using fd as a great replacement for GNU Find. It’s fast and it takes .gitignore into account. Let’s assign it to find-program. I guess there will be no problems if a well known tool is replaced by something that is completely different, am I right? Well I hope that it is used by grep.el only which I don’t use anyways.

(when (executable-find "fd")
  (setq find-program "fd"))

Counsel has nice counsel-file-fump command that uses find-program variable, so it will be more performant with fd.

(use-package counsel
  :init
  (when (executable-find "fd")
    (setq counsel-file-jump-args "-L --type f --hidden"))
  (setq counsel-rg-base-command
        "rg -S --no-heading --hidden --line-number --color never %s .")
  (setenv "FZF_DEFAULT_COMMAND"
          "rg --files --hidden --follow --no-ignore --no-messages --glob '!.git/*' --glob '!.svn/*'")
  :bind (("M-x" . counsel-M-x)
         ("C-x C-f" . counsel-find-file)
         ("C-x f" . counsel-fzf)
         ("C-x p" . counsel-file-jump)
         ("C-x C-r" . counsel-recentf)
         ("C-c g" . counsel-git-grep)
         ("C-c r" . counsel-rg)
         ("C-h f" . counsel-describe-function)
         ("C-h v" . counsel-describe-variable)
         ("C-h l" . counsel-find-library)))

Flycheck

A really nice linting package that helps me track errors in most of languages.

(use-package flycheck)

Flycheck Rust

This package makes flycheck work with Rust projects.

(use-package flycheck-rust
  :commands (flycheck-rust-setup)
  :init (with-eval-after-load 'rust-mode
          (add-hook 'flycheck-mode-hook #'flycheck-rust-setup)))

Company

Complete anything framework. Nothing much to say. Does it’s job.

(use-package company
  :bind (:map company-active-map
              ("TAB" . company-complete-common-or-cycle)
              ("<tab>" . company-complete-common-or-cycle)
              ("<S-Tab>" . company-select-previous)
              ("<backtab>" . company-select-previous))
  :init
  (add-hook 'after-init-hook 'global-company-mode)
  (setq company-require-match 'never
        company-minimum-prefix-length 3
        company-tooltip-align-annotations t
        company-frontends
        '(company-pseudo-tooltip-unless-just-one-frontend
          company-preview-frontend
          company-echo-metadata-frontend))
  :config
  (setq company-backends (remove 'company-clang company-backends)
        company-backends (remove 'company-xcode company-backends)
        company-backends (remove 'company-cmake company-backends)
        company-backends (remove 'company-gtags company-backends)))

Company FLX

Fuzzy matching for company.

(use-package company-flx
 :commands company-flx-mode
 :init (with-eval-after-load 'company
         (company-flx-mode +1)))

Undo Tree

This is more familiar undo mode. It adds C-/ mapping to undo and C-? mapping to redo.

(use-package undo-tree
  :commands global-undo-tree-mode
  :init
  (global-undo-tree-mode 1))

Yasnippet

Another very handy package, that helps insert templates of code. Now I really need to write some snippets to use…

(use-package yasnippet
  :commands yas-reload-all
  :init (yas-reload-all))

Yasnippet Snippets

A enormous snippet collection for yasnippet.

(use-package yasnippet-snippets)

Projectile

Since Emacs is stupid, it changes working directory to current file location. So I need a whole plugin to workaround this silly issue.

However this plugin is quite useful with Git repositories.

(use-package projectile
  :commands (projectile-mode
             projectile-find-file
             projectile-project-root)
  :defines (my/projectile-project-find-function)
  :bind (("C-c p" . projectile-command-map))
  :init
  (with-eval-after-load 'project
    (defvar project-find-functions)
    (add-to-list 'project-find-functions
                 #'my/projectile-project-find-function))
  (projectile-mode +1)
  (setq projectile-svn-command "fd -L --type f --print0"
        projectile-generic-command "fd -L --type f --print0"
        projectile-require-project-root t
        projectile-enable-caching t
        projectile-completion-system 'ivy))

There’s still one problem though. I’m using Projectile, but some other packages, like Eglot use project.el. Which means that all nice root detection handled by projectile is bypassed and all attempts to find roots of the project are not exactly where the project root actually is. This function attempts to fix this problem. I found it in this issue joaotavora/eglot#129:

(defun my/projectile-project-find-function (dir)
  "Handle root search in DIR with Projectile."
  (unless (require 'projectile nil t)
    (error "`projectile' isn't installed"))
  (let ((root (projectile-project-root dir)))
    (and root (cons 'transient root))))

Counsel projectile

It makes using projectile easier by allowing fuzzy matching.

(use-package counsel-projectile
  :commands counsel-projectile-mode
  :config (counsel-projectile-mode))

Magit

Magit is a Emacs interface to Git. I’ve heard that it has many nice features, so I want to try it out.

(use-package magit)

Vdiff

vdiff is a package, that works similar to Vimdiff. Ediff isn’t really comfortable to use, and vdiff also has integration with Magit, so I gonna try it out.

(use-package vdiff
  :init (setq vdiff-lock-scrolling t
              vdiff-diff-algorithm 'diff
              vdiff-disable-folding nil
              vdiff-min-fold-size 4
              vdiff-subtraction-style 'full
              vdiff-subtraction-fill-char ?\ )
  :config
  (define-key vdiff-mode-map (kbd "C-c") vdiff-mode-prefix-map)
  (set-face-attribute 'vdiff-subtraction-face nil :background "#4F343A" :foreground "#F36868")
  (set-face-attribute 'vdiff-addition-face nil :background "#3E493D" :foreground "#98BE65")
  (set-face-attribute 'vdiff-change-face nil :background "#293239" :foreground "#4f97d7")
  (add-hook 'vdiff-mode-hook #'outline-show-all))

Vdiff Magit

vdiff-magit is a supplement package, that provides integration with Magit package.

(use-package vdiff-magit
  :commands (vdiff-magit-dwim vdiff-magit)
  :functions (transient-suffix-put)
  :bind (:map magit-mode-map
              ("e" . 'vdiff-magit-dwim)
              ("E" . 'vdiff-magit))
  :init
  (setq vdiff-magit-stage-is-2way t)
  (transient-suffix-put 'magit-dispatch "e" :description "vdiff (dwim)")
  (transient-suffix-put 'magit-dispatch "e" :command 'vdiff-magit-dwim)
  (transient-suffix-put 'magit-dispatch "E" :description "vdiff")
  (transient-suffix-put 'magit-dispatch "E" :command 'vdiff-magit)
  (advice-add 'vdiff-magit-dwim :before 'eyebrowse-create-window-config))

Which Key

which-key is a package that can show all possible completions for keyboard shortcuts in a popup menu. I’m still learning Emacs, so let’s enable it by default.

(use-package which-key
  :commands which-key-mode
  :init
  (which-key-mode))

Multiple Cursors

This package provides multiple cursors functionality to Emacs. It isn’t much like Kakoune’s multiple selections, but anything will do. I guess I’ll figure out best mappings over time.

(use-package multiple-cursors
  :commands (mc/cycle-backward
             mc/cycle-forward)
  :bind (("S-<mouse-1>" . mc/add-cursor-on-click)
         ("C-c m" . hydra-mc/body))
  :config (defhydra hydra-mc (:hint nil)
            "
^Select^                ^Discard^                    ^Move^
^──────^────────────────^───────^────────────────────^────^────────────
_M-s_: split lines      _M-SPC_: discard current     _&_: align
_s_:   select regexp    _b_:     discard blank lines _(_: cycle backward
_n_:   select next      _d_:     remove duplicated   _)_: cycle forward
_p_:   select previous  _q_:     exit                ^ ^
_C_:   select next line"
            ("M-s" mc/edit-ends-of-lines)
            ("s" mc/mark-all-in-region-regexp)
            ("n" mc/mark-next-like-this-word)
            ("p" mc/mark-previous-like-this-word)
            ("&" mc/vertical-align-with-space)
            ("(" mc/cycle-backward)
            (")" mc/cycle-forward)
            ("M-SPC" mc/remove-current-cursor)
            ("b" mc/remove-cursors-on-blank-lines)
            ("d" mc/remove-duplicated-cursors)
            ("C" mc/mark-next-lines)
            ("q" mc/remove-duplicated-cursors :exit t)))

MC Extras

this package provides some extra functions for multiple cursors. I’m interested in mc/remove-duplicaded-cursors. When using Phi Search it often places several cursors at the same positions, which I don’t like.

(use-package mc-extras
  :config
  (advice-add 'phi-search :after 'mc/remove-duplicated-cursors))

Iedit

Another package that allows editing several regions simultaneously.

(use-package iedit
  :commands (iedit-mode
             iedit-expand-down-to-occurrence)
  :init
  (setq iedit-toggle-key-default nil)
  (defun my/iedit-select-current-or-add ()
    "Select only current occurrence with `iedit-mode'.  Expand to
next occurrence if `iedit-mode' is already active."
    (interactive)
    (if (bound-and-true-p iedit-mode)
        (iedit-expand-down-to-occurrence)
      (iedit-mode 1)))
  (global-set-key (kbd "C-d") 'my/iedit-select-current-or-add))

Expand Region

This package allows to expand or reduce region selection semantically in most languages that I work with inside Emacs.

(use-package expand-region
  :commands (er/expand-region
             er/mark-paragraph
             er/mark-inside-pairs
             er/mark-outside-pairs
             er/mark-inside-quotes
             er/mark-outside-quotes
             er/contract-region)
  :bind (("C-c e" . hydra-er/body))
  :config (defhydra hydra-er (:hint nil)
            "
^Expand^           ^Mark^
^──────^───────────^────^─────────────────
_e_: expand region _(_: inside pairs
_-_: reduce region _)_: around pairs
^ ^                _q_: inside quotes
^ ^                _Q_: around quotes
^ ^                _p_: paragraph"
            ("e" er/expand-region :post hydra-er/body)
            ("-" er/contract-region :post hydra-er/body)
            ("p" er/mark-paragraph)
            ("(" er/mark-inside-pairs)
            (")" er/mark-outside-pairs)
            ("q" er/mark-inside-quotes)
            ("Q" er/mark-outside-quotes)))

Phi Search

This search mode is compatible with multiple cursors.

(use-package phi-search
  :bind (("C-s" . phi-search)
         ("C-r" . phi-search-backward)))

Eglot

Emacs polyglot. LSP client.

(use-package eglot
  :commands (eglot eglot-ensure)
  :config
  (add-to-list 'eglot-server-programs '((c-mode c++-mode) "clangd"))
  (add-to-list 'eglot-ignored-server-capabilites :documentHighlightProvider)
  :init
  (add-hook 'c-mode-hook 'eglot-ensure)
  (add-hook 'c++-mode-hook 'eglot-ensure))

Clang Format

A package to format C code with Clang Format.

(use-package clang-format)

GCMH

The Garbage Collector Magic Hack. Enforce aaaa sneaky Garbage Collection strategy to minimize GC interference with the activity.

(use-package gcmh
  :commands gcmh-mode
  :init (gcmh-mode 1))

GNU Plot

This is aaaa package needed for making plots with gnuplot. Required by Org mode.

(use-package gnuplot)

VLF

This package makes it easier to view large files in Emacs. It opens files in chunks, but still allows to search through file with

(use-package vlf-setup
  :ensure vlf
  :config (setq vlf-application 'dont-ask))

Imenu List

Package somewhat similar to Taglist plugin in Vim or Kakoune’s tagbar.kak. Uses imenu to get information, and since many plugins provide imenu data it’s quite useful. I’m using advice-add here to disable modeline in *imenu-list* buffer, also locking it size preventing it from changing when I close or resize other windows. If we call this lambda function with :after advice, the modeline will disappear in another window. With :after-while lambda evaluated only when imenu-list was toggled to be shown.

(use-package imenu-list
  :defines imenu-list-idle-update-delay-time
  :bind (("<f7>" . imenu-list-smart-toggle))
  :config
  (advice-add 'imenu-list-smart-toggle :after-while
              (lambda()
                (setq window-size-fixed 'width
                      mode-line-format nil)))
  :init (setq imenu-list-idle-update-delay-time 0.1
              imenu-list-size 27
              imenu-list-focus-after-activation t))

Postscript

This Emacs Configuration features various cool E-Lisp hacks somewhere that I’ve found over the Internet, and I try to keep references to original places, so reader could refer to those, because I alter these pieces of code for my personal needs and thus it may not be compatible with other people tastes, and because I want to credit original author.

If you find any issue with my config feel free to file an issue or contact me via email: andreyorst@gmail.com.

And as aaaa final step of a proper init file:

(provide 'init)
;;; init.el ends here

Thanks for reading!

You can’t perform that action at this time.