/UncleDavesEmacs

My personal ~/.emacs.d

Primary LanguageEmacs LispGNU General Public License v3.0GPL-3.0

Uncle Dave’s Emacs

./img/screen.png

Installation

Quick Installation

git clone https://github.com/daedreth/UncleDavesEmacs ~/.emacs.d

Keep in mind that you should remove your own ~/.emacs and your ~/.emacs.d prior to cloning this configuration. The next time you launch Emacs (and I recommend launching emacs in the tty for the first time) it’s going to download a good amount of packages and configure them for you, you might see warnings and errors being displayed as well, those are safe to ignore as long as you relaunch emacs and see none.

The configuration, much like emacs is self documented, I highly recommend reading through my ramblings in their entirety.

Less Quick Installation

You are free to pick out bits and pieces of this and apply them. So long as you have a working use-package configuration (take a peek at my init.el), there should be absolutely no issues. I highly encourage you to read up on the prose around the packages you are interested in, I do my absolute best to describe the configuration as well as I only can, this might help you on your emacs journey! Have some fun with it, add your own keybindings, change parameters, it’s your editor in the end.

Dependencies for:

EXWM

Since we are going to use emacs as our window manager, it would be nice to have some external software to make our life easier.

From the package manager:

  • xorg-server, for obvious reasons. (there is no wayland support as of now)
  • pulsemixer, available from https://github.com/GeorgeFilipkin/pulsemixer.
  • imagemagick, if you are going to be using emacs to take screenshots.
  • ibus-daemon, if you need multiple keyboard input options (emacs handles them well on its own but ibus is nice).
  • terminus-font, for it to work out of the box, if you don’t want terminus you need to edit your init.el.

Optional

  • A browser (sadly, the built in xwidgets-webkit thingie is unreliable).
  • A composite manager (highly recommended, I personally use compton, the built in one does not work as intended.).
  • noto-cjk, for all the fonts imaginable.
  • slock, if you would like to lock the screen.

TRAMP

From the package manager:

  • sudo, properly configured for your user.

EMMS

From the package manager:

  • mpd, since we are going to be using EMMS for music playback, I recommend setting up a working mpd server.
  • mpv, for video playback.

Programming

Package manager

  • clang, for c/c++ completion.
  • sbcl, for clisp completion and repl.
  • virtualenv, for python completion.
  • lua, for obvious reasons.

pip

  • pip install jedi flake8 autopep8, here, a one line install.

Some notes

On use-package some more

We utilize use-package to handle downloading and configuring other packages painlessly. With init.el written the way it is, emacs checks for the presence of use-package on launch and downloads it and installs if necessary. el-Get is too old and not configurable enough.

On the format of the configuration

As you may have noticed, as you scroll down my bit of prose, this is the configuration file itself. This configuration is written in org-mode, which is a great emacs package and a great markup language. On launch, this file is being sourced into ~/.emacs.d/init.el, the prose is being automatically stripped (as to not affect performance) and the remaining config.el file is parsed.

This is also the reason why your org-mode configuration file is never called init.org.

On the use case of this configuration

This entire config is meant to be used as a full on desktop environment, it is tailored to sit on top of xorg and be awesome. It is perfectly possible to use it without exwm and emms, as a regular emacs config, just make sure to delete the unwanted sections, ex.g EXWM, Audio Control, EMMS and all the launchers. There is barely any learning curve to using exwm, since it makes x-windows act as regular buffers as much as possible, so chances are, as long as you know your way around emacs itself, you will instinctively know how to make use of its features.

On the keybindings

I do my best to not pollute keymaps with my own keybindings. Most of the bindings I defined myself utilize the Super key (noted as s-). This is the least used modifier key together with Shift (noted as S-), thus using those, chances are all the bindings you already know and love are going to work flawlessly. For all the others, just keep on reading.

Theme

The most important part of every configuration.

A nice theme

My new favourite one I guess, really decent default values.

(use-package zerodark-theme
  :ensure t
  :init
    (load-theme 'zerodark t))

Some customization

The theme is great, really, but some of the concepts just suck with powerline.

(let ((class '((class color) (min-colors 89)))
      (default (if (true-color-p) "#abb2bf" "#afafaf"))
      (light (if (true-color-p) "#ccd4e3" "#d7d7d7"))
      (background (if (true-color-p) "#282c34" "#333333"))
      (background-dark (if (true-color-p) "#24282f" "#222222"))
      (background-darker (if (true-color-p) "#22252c" "#222222"))
      (mode-line-inactive (if "#1c2129" "#222222"))
      (mode-line-active (if (true-color-p) "#6f337e" "#875f87"))
      (background-lighter (if (true-color-p) "#3a3f4b" "#5f5f5f"))
      (background-red (if (true-color-p) "#4c3840" "#5f5f5f"))
      (bright-background-red (if (true-color-p) "#744a5b" "#744a5b"))
      (background-purple (if (true-color-p) "#48384c" "#5f5f5f"))
      (background-blue (if (true-color-p) "#38394c" "#444444"))
      (bright-background-blue (if (true-color-p) "#4e5079" "#4e5079"))
      (background-green (if (true-color-p) "#3d4a41" "#5f5f5f"))
      (bright-background-green (if (true-color-p) "#3f6d54" "#3f6d54"))
      (background-orange (if (true-color-p) "#4a473d" "#5f5f5f"))
      (hl-line (if (true-color-p) "#2c323b" "#333333"))
      (grey (if (true-color-p) "#cccccc" "#cccccc"))
      (grey-dark (if (true-color-p) "#666666" "#666666"))
      (highlight (if (true-color-p) "#3e4451" "#5f5f5f"))
      (comment (if (true-color-p) "#687080" "#707070"))
      (orange (if (true-color-p) "#da8548" "#d7875f"))
      (orange-light (if (true-color-p) "#ddbd78" "#d7af87"))
      (red (if (true-color-p) "#ff6c6b" "#ff5f5f"))
      (purple (if (true-color-p) "#c678dd" "#d787d7"))
      (purple-dark (if (true-color-p) "#64446d" "#5f5f5f"))
      (blue (if (true-color-p) "#61afef" "#5fafff"))
      (blue-dark (if (true-color-p) "#1f5582" "#005f87"))
      (green (if (true-color-p) "#98be65" "#87af5f"))
      (green-light (if (true-color-p) "#9eac8c" "#afaf87"))
      (peach "PeachPuff3")
      (diff-added-background (if (true-color-p) "#284437" "#284437"))
      (diff-added-refined-background (if (true-color-p) "#1e8967" "#1e8967"))
      (diff-removed-background (if (true-color-p) "#583333" "#580000"))
      (diff-removed-refined-background (if (true-color-p) "#b33c49" "#b33c49"))
      (diff-current-background (if (true-color-p) "#29457b" "#29457b"))
      (diff-current-refined-background (if (true-color-p) "#4174ae" "#4174ae")))

  (custom-theme-set-faces
   'zerodark

   `(fancy-battery-charging ((,class (:background ,background-blue :height 1.0 :bold t))))
   `(fancy-battery-discharging ((,class (:background ,background-blue :height 1.0))))
   `(fancy-battery-critical ((,class (:background ,background-blue :height 1.0))))
   
   ;; mode line stuff
   `(mode-line ((,class (:background ,background-blue :height 1.0 :foreground ,blue
                                     :distant-foreground ,background-blue
                                     :box ,(when zerodark-use-paddings-in-mode-line
                                             (list :line-width 6 :color background-blue))))))
   
   `(mode-line-inactive ((,class (:background ,background-blue :height 1.0 :foreground ,default
                                              :distant-foreground ,background-blue
                                              :box ,(when zerodark-use-paddings-in-mode-line
                                                      (list :line-width 6 :color background-blue))))))

   `(header-line ((,class (:inherit mode-line-inactive))))

   `(powerline-active0 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
                                         :distant-foreground ,background-blue))))
   `(powerline-active1 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
                                         :distant-foreground ,background-blue))))
   `(powerline-active2 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
                                         :distant-foreground ,background-blue))))
   `(powerline-inactive0 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
                                           :distant-foreground ,background-blue))))
   `(powerline-inactive1 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
                                           distant-foreground ,background-blue))))
   `(powerline-inactive2 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
                                           :distant-foreground ,background-blue))))

   `(dashboard-heading-face ((,class (:background ,background :foreground ,blue
                                                  :bold t :height 1.2))))
   `(dashboard-banner-logo-title-face ((,class (:background ,background :foreground ,blue
                                                            :bold t :height 1.2))))
   `(widget-button ((,class (:background ,background :foreground ,default :bold nil
                                         :underline t :height 0.9))))
   
   ;; erc stuff
   `(erc-nick-default-face ((,class :foreground ,blue :background ,background :weight bold)))

   ;; org stuff
   `(outline-1 ((,class (:foreground ,blue :weight bold :height 1.8 :bold nil))))
   `(outline-2 ((,class (:foreground ,purple :weight bold :height 1.7 :bold nil))))
   `(outline-3 ((,class (:foreground ,peach :weight bold :height 1.6 :bold nil))))
   `(outline-4 ((,class (:foreground ,green-light :weight bold :height 1.5 :bold nil))))
   `(outline-5 ((,class (:foreground ,blue :weight bold :height 1.4 :bold nil))))
   `(outline-6 ((,class (:foreground ,purple :weight bold :height 1.3 :bold nil))))
   `(outline-7 ((,class (:foreground ,peach :weight bold :height 1.2 :bold nil))))
   `(outline-8 ((,class (:foreground ,green-light :weight bold :height 1.1 :bold nil))))
   
   `(org-block-begin-line ((,class (:background ,background-blue :foreground ,blue
                                                :bold t :height 1.0))))
   `(org-block-end-line ((,class (:background ,background-blue :foreground ,blue
                                              :bold t :height 1.0))))))

Basic Interface Settings

These are setting that do not depend on packages and are built-in enhancements to the UI.

Looks

Remove lame startup screen

We use an actual replacement for it, keep reading or head directly to dashboard.

(setq inhibit-startup-message t)

Disable menus and scrollbars

If you like using any of those, change -1 to 1.

(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)

Disable bell

This is annoying, remove this line if you like being visually reminded of events.

(setq ring-bell-function 'ignore)

Set UTF-8 encoding

(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)

Highligh current line

hl-line is awesome! It’s not very awesome in the terminal version of emacs though, so we don’t use that. Besides, it’s only used for programming.

(when window-system (add-hook 'prog-mode-hook 'hl-line-mode))

Pretty symbols

Changes lambda to an actual symbol and a few others as well, only in the GUI version though.

(when window-system
      (use-package pretty-mode
      :ensure t
      :config
      (global-pretty-mode t)))

Functionality

Disable backups and auto-saves

I don’t use either, you might want to turn those from nil to t if you do.

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

Change yes-or-no questions into y-or-n questions

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

Async

Lets us use asynchronous processes wherever possible, pretty useful.

(use-package async
  :ensure t
  :init (dired-async-mode 1))

Window Manager

Everything regarding the WM or DE-like functionality is bundled here, remove the entire section if you do not wish to use exwm.

exwm

The only time I actually had to use comments, this is for ease of removal if you happen to not like exwm.

Installation

(use-package exwm
  :ensure t
  :config

    ;; necessary to configure exwm manually
    (require 'exwm-config)

    ;; fringe size, most people prefer 1 
    (fringe-mode 3)
    
    ;; emacs as a daemon, use "emacsclient <filename>" to seamlessly edit files from the terminal directly in the exwm instance
    (server-start)

    ;; this fixes issues with ido mode, if you use helm, get rid of it
    (exwm-config-ido)

    ;; a number between 1 and 9, exwm creates workspaces dynamically so I like starting out with 1
    (setq exwm-workspace-number 1)

    ;; this is a way to declare truly global/always working keybindings
    ;; this is a nifty way to go back from char mode to line mode without using the mouse
    (exwm-input-set-key (kbd "s-r") #'exwm-reset)
    (exwm-input-set-key (kbd "s-k") #'exwm-workspace-delete)
    (exwm-input-set-key (kbd "s-w") #'exwm-workspace-swap)

    ;; the next loop will bind s-<number> to switch to the corresponding workspace
    (dotimes (i 10)
      (exwm-input-set-key (kbd (format "s-%d" i))
                          `(lambda ()
                             (interactive)
                             (exwm-workspace-switch-create ,i))))

    ;; the simplest launcher, I keep it in only if dmenu eventually stopped working or something
    (exwm-input-set-key (kbd "s-&")
                        (lambda (command)
                          (interactive (list (read-shell-command "$ ")))
                          (start-process-shell-command command nil command)))

    ;; an easy way to make keybindings work *only* in line mode
    (push ?\C-q exwm-input-prefix-keys)
    (define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key)

    ;; simulation keys are keys that exwm will send to the exwm buffer upon inputting a key combination
    (exwm-input-set-simulation-keys
     '(
       ;; movement
       ([?\C-b] . left)
       ([?\M-b] . C-left)
       ([?\C-f] . right)
       ([?\M-f] . C-right)
       ([?\C-p] . up)
       ([?\C-n] . down)
       ([?\C-a] . home)
       ([?\C-e] . end)
       ([?\M-v] . prior)
       ([?\C-v] . next)
       ([?\C-d] . delete)
       ([?\C-k] . (S-end delete))
       ;; cut/paste
       ([?\C-w] . ?\C-x)
       ([?\M-w] . ?\C-c)
       ([?\C-y] . ?\C-v)
       ;; search
       ([?\C-s] . ?\C-f)))

    ;; this little bit will make sure that XF86 keys work in exwm buffers as well
    (dolist (k '(XF86AudioLowerVolume
               XF86AudioRaiseVolume
               XF86PowerOff
               XF86AudioMute
               XF86AudioPlay
               XF86AudioStop
               XF86AudioPrev
               XF86AudioNext
               XF86ScreenSaver
               XF68Back
               XF86Forward
               Scroll_Lock
               print))
    (cl-pushnew k exwm-input-prefix-keys))
    
    ;; this just enables exwm, it started automatically once everything is ready
    (exwm-enable))

Launchers

Since I do not use a GUI launcher and do not have an external one like dmenu or rofi, I figured the best way to launch my most used applications would be direct emacsy keybindings.

dmenu for emacs

Who would’ve thought this was available, together with ido-vertical it’s a nice large menu with its own cache for most launched applications.

(use-package dmenu
  :ensure t
  :bind
    ("s-SPC" . 'dmenu))

Functions to start processes

I guess this goes without saying but you absolutely have to change the arguments to suit the software that you are using. What good is a launcher for discord if you don’t use it at all.

(defun exwm-async-run (name)
  (interactive)
  (start-process name nil name))

(defun daedreth/launch-discord ()
  (interactive)
  (exwm-async-run "discord"))

(defun daedreth/launch-browser ()
  (interactive)
  (exwm-async-run "qutebrowser"))

(defun daedreth/lock-screen ()
  (interactive)
  (exwm-async-run "slock"))

(defun daedreth/shutdown ()
  (interactive)
  (start-process "halt" nil "sudo" "halt"))

Keybindings to start processes

These can be modified as well, suit yourself.

(global-set-key (kbd "s-d") 'daedreth/launch-discord)
(global-set-key (kbd "<s-tab>") 'daedreth/launch-browser)
(global-set-key (kbd "<XF86ScreenSaver>") 'daedreth/lock-screen)
(global-set-key (kbd "<XF86PowerOff>") 'daedreth/shutdown)

Audio controls

This is a set of bindings to my XF86 keys that invokes pulsemixer with the correct parameters

Volume modifier

It goes without saying that you are free to modify the modifier as you see fit, 4 is good enough for me though.

(defconst volumeModifier "4")

Functions to start processes

(defun audio/mute ()
  (interactive)
  (start-process "audio-mute" nil "pulsemixer" "--toggle-mute"))

(defun audio/raise-volume ()
  (interactive)
  (start-process "raise-volume" nil "pulsemixer" "--change-volume" (concat "+" volumeModifier)))

(defun audio/lower-volume ()
  (interactive)
  (start-process "lower-volume" nil "pulsemixer" "--change-volume" (concat "-" volumeModifier)))

Keybindings to start processes

You can also change those if you’d like, but I highly recommend keeping ‘em the same, chances are, they will just work.

(global-set-key (kbd "<XF86AudioMute>") 'audio/mute)
(global-set-key (kbd "<XF86AudioRaiseVolume>") 'audio/raise-volume)
(global-set-key (kbd "<XF86AudioLowerVolume>") 'audio/lower-volume)

Screenshots

I don’t need scrot to take screenshots, or shutter or whatever tools you might have. This is enough. These won’t work in the terminal version or the virtual console, obvious reasons.

Screenshotting the entire screen

(defun daedreth/take-screenshot ()
  "Takes a fullscreen screenshot of the current workspace"
  (interactive)
  (when window-system
  (loop for i downfrom 3 to 1 do
        (progn
          (message (concat (number-to-string i) "..."))
          (sit-for 1)))
  (message "Cheese!")
  (sit-for 1)
  (start-process "screenshot" nil "import" "-window" "root" 
             (concat (getenv "HOME") "/" (subseq (number-to-string (float-time)) 0 10) ".png"))
  (message "Screenshot taken!")))
(global-set-key (kbd "<print>") 'daedreth/take-screenshot)

Screenshotting a region

(defun daedreth/take-screenshot-region ()
  "Takes a screenshot of a region selected by the user."
  (interactive)
  (when window-system
  (call-process "import" nil nil nil ".newScreen.png")
  (call-process "convert" nil nil nil ".newScreen.png" "-shave" "1x1"
                (concat (getenv "HOME") "/" (subseq (number-to-string (float-time)) 0 10) ".png"))
  (call-process "rm" nil nil nil ".newScreen.png")))
(global-set-key (kbd "<Scroll_Lock>") 'daedreth/take-screenshot-region)

Default browser

I use qutebrowser, so that’s what I’ll set up.

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "qutebrowser")

Projectile

Projectile is an awesome project manager, mostly because it recognizes directories with a .git directory as projects and helps you manage them accordingly.

Enable projectile globally

This makes sure that everything can be a project.

(use-package projectile
  :ensure t
  :init
    (projectile-mode 1))

Let projectile call make

(global-set-key (kbd "<f5>") 'projectile-compile-project)

Dashboard

This is your new startup screen, together with projectile it works in unison and provides you with a quick look into your latest projects and files. Change the welcome message to whatever string you want and change the numbers to suit your liking, I find 5 to be enough.

(use-package dashboard
  :ensure t
  :config
    (dashboard-setup-startup-hook)
    (setq dashboard-startup-banner "~/.emacs.d/img/dashLogo.png")
    (setq dashboard-items '((recents  . 5)
                            (projects . 5)))
    (setq dashboard-banner-logo-title ""))

Modeline

The modeline is the heart of emacs, it offers information at all times, it’s persistent and verbose enough to gain a full understanding of modes and states you are in.

Due to the fact that we attempt to use emacs as a desktop environment replacement, and external bar showing the time, the battery percentage and more system info would be great to have. I have however abandoned polybar in favor of a heavily modified modeline, this offers me more space on the screen and better integration.

One modeline-related setting that is missing and is instead placed at the bottom is diminish.

Spaceline!

I may not use spacemacs, since I do not like evil-mode and find spacemacs incredibly bloated and slow, however it would be stupid not to acknowledge the best parts about it, the theme and their modified powerline setup.

This enables spaceline, it looks better and works very well with my theme of choice.

(use-package spaceline
  :ensure t
  :config
  (require 'spaceline-config)
    (setq spaceline-buffer-encoding-abbrev-p nil)
    (setq spaceline-line-column-p nil)
    (setq spaceline-line-p nil)
    (setq powerline-default-separator (quote arrow))
    (spaceline-spacemacs-theme))

No separator!

(setq powerline-default-separator nil)

Cursor position

Show the current line and column for your cursor. We are not going to have relative-linum-mode in every major mode, so this is useful.

(setq line-number-mode t)
(setq column-number-mode t)

Clock

If you prefer the 12hr-format, change the variable to nil instead of t.

Time format

(setq display-time-24hr-format t)
(setq display-time-format "%H:%M - %d %B %Y")

Enabling the mode

This turns on the clock globally.

(display-time-mode 1)

Battery indicator

A package called fancy-battery will be used if we are in GUI emacs, otherwise the built in battery-mode will be used. Fancy battery has very odd colors if used in the tty, hence us disabling it.

(use-package fancy-battery
  :ensure t
  :config
    (setq fancy-battery-show-percentage t)
    (setq battery-update-interval 15)
    (if window-system
      (fancy-battery-mode)
      (display-battery-mode)))

System monitor

A teeny-tiny system monitor that can be enabled or disabled at runtime, useful for checking performance with power-hungry processes in ansi-term

symon can be toggled on and off with Super + h.

(use-package symon
  :ensure t
  :bind
  ("s-h" . symon-mode))

File manager

I abandoned treemacs, looking for an alternative as we speak I found an alternative, Sunrise Commander! I’m not changing much, just disabling the mouse and adding buttons.

(use-package sunrise-commander
  :ensure t
  :init
    (use-package sunrise-x-buttons :ensure t)
    (use-package sunrise-x-modeline :ensure t)
    (setq sr-cursor-follows-mouse nil)
    (define-key sr-mode-map [mouse-1] nil)
    (define-key sr-mode-map [mouse-movement] nil)
  :bind ("s-t" . sunrise))

The terminal

I have used urxvt for years, and I miss it sometimes, but ansi-term is enough for most of my tasks.

Default shell should be bash

I don’t know why this is a thing, but asking me what shell to launch every single time I open a terminal makes me want to slap babies, this gets rid of it. This goes without saying but you can replace bash with your shell of choice.

(defvar my-term-shell "/bin/bash")
(defadvice ansi-term (before force-bash)
  (interactive (list my-term-shell)))
(ad-activate 'ansi-term)

Easy to remember keybinding

In loving memory of bspwm, Super + Enter opens a new terminal, old habits die hard.

(global-set-key (kbd "<s-return>") 'ansi-term)

Moving around emacs

One of the most important things about a text editor is how efficient you manage to be when using it, how much time do basic tasks take you and so on and so forth. One of those tasks is moving around files and buffers, whatever you may use emacs for you will be jumping around buffers like it’s serious business, the following set of enhancements aims to make it easier.

As a great emacs user once said:

Do me the favor, do me the biggest favor, matter of fact do yourself the biggest favor and integrate those into your workflow.

a prerequisite for others packages

(use-package ivy
  :ensure t)

scrolling and why does the screen move

I don’t know to be honest, but this little bit of code makes scrolling with emacs a lot nicer.

(setq scroll-conservatively 100)

which-key and why I love emacs

In order to use emacs, you don’t need to know how to use emacs. It’s self documenting, and coupled with this insanely useful package, it’s even easier. In short, after you start the input of a command and stop, pondering what key must follow, it will automatically open a non-intrusive buffer at the bottom of the screen offering you suggestions for completing the command, that’s it, nothing else.

It’s beautiful

(use-package which-key
  :ensure t
  :config
    (which-key-mode))

windows,panes and why I hate other-window

Some of us have large displays, others have tiny netbook screens, but regardless of your hardware you probably use more than 2 panes/windows at times, cycling through all of them with C-c o is annoying to say the least, it’s a lot of keystrokes and takes time, time you could spend doing something more productive.

switch-window

This magnificent package takes care of this issue. It’s unnoticeable if you have <3 panes open, but with 3 or more, upon pressing C-x o you will notice how your buffers turn a solid color and each buffer is asigned a letter (the list below shows the letters, you can modify them to suit your liking), upon pressing a letter asigned to a window, your will be taken to said window, easy to remember, quick to use and most importantly, it annihilates a big issue I had with emacs. An alternative is ace-window, however by default it also changes the behaviour of C-x o even if only 2 windows are open, this is bad, it also works less well with exwm for some reason.

(use-package switch-window
  :ensure t
  :config
    (setq switch-window-input-style 'minibuffer)
    (setq switch-window-increase 4)
    (setq switch-window-threshold 2)
    (setq switch-window-shortcut-style 'qwerty)
    (setq switch-window-qwerty-shortcuts
        '("a" "s" "d" "f" "j" "k" "l" "i" "o"))
  :bind
    ([remap other-window] . switch-window))

Following window splits

After you split a window, your focus remains in the previous one. This annoyed me so much I wrote these two, they take care of it.

(defun split-and-follow-horizontally ()
  (interactive)
  (split-window-below)
  (balance-windows)
  (other-window 1))
(global-set-key (kbd "C-x 2") 'split-and-follow-horizontally)

(defun split-and-follow-vertically ()
  (interactive)
  (split-window-right)
  (balance-windows)
  (other-window 1))
(global-set-key (kbd "C-x 3") 'split-and-follow-vertically)

swiper and why is the default search so lame

I like me some searching, the default search is very meh. In emacs, you mostly use search to get around your buffer, much like with avy, but sometimes it doesn’t hurt to search for entire words or mode, swiper makes sure this is more efficient.

(use-package swiper
  :ensure t
  :bind ("C-s" . 'swiper))

buffers and why I hate list-buffers

Another big thing is, buffers. If you use emacs, you use buffers, everyone loves them. Having many buffers is useful, but can be tedious to work with, let us see how we can improve it.

Always murder current buffer

Doing C-x k should kill the current buffer at all times, we have ibuffer for more sophisticated thing.

(defun kill-current-buffer ()
  "Kills the current buffer."
  (interactive)
  (kill-buffer (current-buffer)))
(global-set-key (kbd "C-x k") 'kill-current-buffer)

Kill buffers without asking for confirmation

Unless you have the muscle memory, I recommend omitting this bit, as you may lose progress for no reason when working.

(setq kill-buffer-query-functions (delq 'process-kill-buffer-query-function kill-buffer-query-functions))

Turn switch-to-buffer into ibuffer

I don’t understand how ibuffer isn’t the default option by now. It’s vastly superior in terms of ergonomics and functionality, you can delete buffers, rename buffer, move buffers, organize buffers etc.

(global-set-key (kbd "C-x b") 'ibuffer)

expert-mode

If you feel like you know how ibuffer works and need not to be asked for confirmation after every serious command, enable this as follows.

(setq ibuffer-expert t)

close-all-buffers

It’s one of those things where I genuinely have to wonder why there is no built in functionality for it. Once in a blue moon I need to kill all buffers, and having ~150 of them open would mean I’d need to spend a few too many seconds doing this than I’d like, here’s a solution.

This can be invoked using C-M-s-k. This keybinding makes sure you don’t hit it unless you really want to.

(defun close-all-buffers ()
  "Kill all buffers without regard for their origin."
  (interactive)
  (mapc 'kill-buffer (buffer-list)))
(global-set-key (kbd "C-M-s-k") 'close-all-buffers)

line numbers and programming

Every now and then all of us feel the urge to be productive and write some code. In the event that this happens, the following bit of configuration makes sure that we have access to relative line numbering in programming-related modes. I highly recommend not enabling linum-relative-mode globally, as it messed up something like ansi-term for instance.

(use-package linum-relative
  :ensure t
  :config
    (setq linum-relative-current-symbol "")
    (add-hook 'prog-mode-hook 'linum-relative-mode))

ido and why I started using helm

Sometimes, you don’t realize how good something is until you try it extensively. I give in, helm is awesome. I’ll end up customizing it more eventually, it’s rather similar to ido-vertical though.

helm

(use-package helm
  :ensure t
  :bind
  ("C-x C-f" . 'helm-find-files)
  ("C-x C-b" . 'helm-buffers-list)
  ("M-x" . 'helm-M-x)
  :config
  (defun daedreth/helm-hide-minibuffer ()
    (when (with-helm-buffer helm-echo-input-in-header-line)
      (let ((ov (make-overlay (point-min) (point-max) nil nil t)))
        (overlay-put ov 'window (selected-window))
        (overlay-put ov 'face
                     (let ((bg-color (face-background 'default nil)))
                       `(:background ,bg-color :foreground ,bg-color)))
        (setq-local cursor-type nil))))
  (add-hook 'helm-minibuffer-set-up-hook 'daedreth/helm-hide-minibuffer)
  (setq helm-autoresize-max-height 0
        helm-autoresize-min-height 40
        helm-M-x-fuzzy-match t
        helm-buffers-fuzzy-matching t
        helm-recentf-fuzzy-match t
        helm-semantic-fuzzy-match t
        helm-imenu-fuzzy-match t
        helm-split-window-in-side-p nil
        helm-move-to-line-cycle-in-source nil
        helm-ff-search-library-in-sexp t
        helm-scroll-amount 8 
        helm-echo-input-in-header-line t)
  :init
  (helm-mode 1))

(require 'helm-config)    
(helm-autoresize-mode 1)
(define-key helm-find-files-map (kbd "C-b") 'helm-find-files-up-one-level)
(define-key helm-find-files-map (kbd "C-f") 'helm-execute-persistent-action)

avy and why it’s the best thing in existence

Many times have I pondered how I can move around buffers even quicker. I’m glad to say, that avy is precisely what I needed, and it’s precisely what you need as well. In short, as you invoke one of avy’s functions, you will be prompted for a character that you’d like to jump to in the visible portion of the current buffer. Afterwards you will notice how all instances of said character have additional letter on top of them. Pressing those letters, that are next to your desired character will move your cursor over there. Admittedly, this sounds overly complicated and complex, but in reality takes a split second and improves your life tremendously.

I like M-s for it, same as C-s is for moving by searching string, now M-s is moving by searching characters.

(use-package avy
  :ensure t
  :bind
    ("M-s" . avy-goto-char))

Text manipulation

Here I shall collect self-made functions that make editing text easier.

Mark-Multiple

I can barely contain my joy. This extension allows you to quickly mark the next occurence of a region and edit them all at once. Wow!

(use-package mark-multiple
  :ensure t
  :bind ("C-c q" . 'mark-next-like-this))

Improved kill-word

Why on earth does a function called kill-word not .. kill a word. It instead deletes characters from your cursors position to the end of the word, let’s make a quick fix and bind it properly.

(defun daedreth/kill-inner-word ()
  "Kills the entire word your cursor is in. Equivalent to 'ciw' in vim."
  (interactive)
  (forward-char 1)
  (backward-word)
  (kill-word 1))
(global-set-key (kbd "C-c w k") 'daedreth/kill-inner-word)

Improved copy-word

And again, the same as above but we make sure to not delete the source word.

(defun daedreth/copy-whole-word ()
  (interactive)
  (save-excursion
    (forward-char 1)
    (backward-word)
    (kill-word 1)
    (yank)))
(global-set-key (kbd "C-c w c") 'daedreth/copy-whole-word)

Copy a line

Regardless of where your cursor is, this quickly copies a line.

(defun daedreth/copy-whole-line ()
  "Copies a line without regard for cursor position."
  (interactive)
  (save-excursion
    (kill-new
     (buffer-substring
      (point-at-bol)
      (point-at-eol)))))
(global-set-key (kbd "C-c l c") 'daedreth/copy-whole-line)

Kill a line

And this quickly deletes a line.

(global-set-key (kbd "C-c l k") 'kill-whole-line)

Minor conveniences

Emacs is at it’s best when it just does things for you, shows you the way, guides you so to speak. This can be best achieved using a number of small extensions. While on their own they might not be particularly impressive. Together they create a nice environment for you to work in.

Visiting the configuration

Quickly edit ~/.emacs.d/config.org

(defun config-visit ()
  (interactive)
  (find-file "~/.emacs.d/config.org"))
(global-set-key (kbd "C-c e") 'config-visit)

Reloading the configuration

Simply pressing Control-c r will reload this file, very handy. You can also manually invoke config-reload.

(defun config-reload ()
  "Reloads ~/.emacs.d/config.org at runtime"
  (interactive)
  (org-babel-load-file (expand-file-name "~/.emacs.d/config.org")))
(global-set-key (kbd "C-c r") 'config-reload)

Subwords

Emacs treats camelCase strings as a single word by default, this changes said behaviour.

(global-subword-mode 1)

Electric

If you write any code, you may enjoy this. Typing the first character in a set of 2, completes the second one after your cursor. Opening a bracket? It’s closed for you already. Quoting something? It’s closed for you already.

You can easily add and remove pairs yourself, have a look.

(setq electric-pair-pairs '(
                           (?\{ . ?\})
                           (?\( . ?\))
                           (?\[ . ?\])
                           (?\" . ?\")
                           ))

And now to enable it

(electric-pair-mode t)

Beacon

While changing buffers or workspaces, the first thing you do is look for your cursor. Unless you know its position, you can not move it efficiently. Every time you change buffers, the current position of your cursor will be briefly highlighted now.

(use-package beacon
  :ensure t
  :config
    (beacon-mode 1))

Rainbow

Mostly useful if you are into web development or game development. Every time emacs encounters a hexadecimal code that resembles a color, it will automatically highlight it in the appropriate color. This is a lot cooler than you may think.

(use-package rainbow-mode
  :ensure t
  :init
    (add-hook 'prog-mode-hook 'rainbow-mode))

Show parens

I forgot about that initially, it highlights matching parens when the cursor is just behind one of them.

(show-paren-mode 1)

Rainbow delimiters

Colors parentheses and other delimiters depending on their depth, useful for any language using them, especially lisp.

(use-package rainbow-delimiters
  :ensure t
  :init
    (add-hook 'prog-mode-hook #'rainbow-delimiters-mode))

Expand region

A pretty simple package, takes your cursor and semantically expands the region, so words, sentences, maybe the contents of some parentheses, it’s awesome, try it out.

(use-package expand-region
  :ensure t
  :bind ("C-q" . er/expand-region))

Hungry deletion

On the list of things I like doing, deleting big whitespaces is pretty close to the bottom. Backspace or Delete will get rid of all whitespace until the next non-whitespace character is encountered. You may not like it, thus disable it if you must, but it’s pretty decent.

(use-package hungry-delete
  :ensure t
  :config
    (global-hungry-delete-mode))

Zapping to char

A nifty little package that kills all text between your cursor and a selected character. A lot more useful than you might think. If you wish to include the selected character in the killed region, change zzz-up-to-char into zzz-to-char.

(use-package zzz-to-char
  :ensure t
  :bind ("M-z" . zzz-up-to-char))

Kill ring

There is a lot of customization to the kill ring, and while I have not used it much before, I decided that it was time to change that.

Maximum entries on the ring

The default is 60, I personally need more sometimes.

(setq kill-ring-max 100)

popup-kill-ring

Out of all the packages I tried out, this one, being the simplest, appealed to me most. With a simple M-y you can now browse your kill-ring like browsing autocompletion items. C-n and C-p totally work for this.

(use-package popup-kill-ring
  :ensure t
  :bind ("M-y" . popup-kill-ring))

Programming

Minor, non-completion related settings and plugins for writing code.

yasnippet

(use-package yasnippet
  :ensure t
  :config
    (use-package yasnippet-snippets
      :ensure t)
    (yas-reload-all))

flycheck

(use-package flycheck
  :ensure t)

company mode

I set the delay for company mode to kick in to half a second, I also make sure that it starts doing its magic after typing in only 2 characters.

I prefer C-n and C-p to move around the items, so I remap those accordingly.

(use-package company
  :ensure t
  :config
  (setq company-idle-delay 0)
  (setq company-minimum-prefix-length 3))

(with-eval-after-load 'company
  (define-key company-active-map (kbd "M-n") nil)
  (define-key company-active-map (kbd "M-p") nil)
  (define-key company-active-map (kbd "C-n") #'company-select-next)
  (define-key company-active-map (kbd "C-p") #'company-select-previous)
  (define-key company-active-map (kbd "SPC") #'company-abort))

specific languages

Be it for code or prose, completion is a must. After messing around with auto-completion for a while I decided to drop it in favor of company, and it turns out to have been a great decision.

Each category also has additional settings.

c/c++

yasnippet

(add-hook 'c++-mode-hook 'yas-minor-mode)
(add-hook 'c-mode-hook 'yas-minor-mode)

flycheck

(use-package flycheck-clang-analyzer
  :ensure t
  :config
  (with-eval-after-load 'flycheck
    (require 'flycheck-clang-analyzer)
     (flycheck-clang-analyzer-setup)))

company

Requires libclang to be installed.

(with-eval-after-load 'company
  (add-hook 'c++-mode-hook 'company-mode)
  (add-hook 'c-mode-hook 'company-mode))

(use-package company-c-headers
  :ensure t)

(use-package company-irony
  :ensure t
  :config
  (setq company-backends '((company-c-headers
                            company-dabbrev-code
                            company-irony))))

(use-package irony
  :ensure t
  :config
  (add-hook 'c++-mode-hook 'irony-mode)
  (add-hook 'c-mode-hook 'irony-mode)
  (add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options))

python

yasnippet

(add-hook 'python-mode-hook 'yas-minor-mode)

flycheck

(add-hook 'python-mode-hook 'flycheck-mode)

company

(with-eval-after-load 'company
    (add-hook 'python-mode-hook 'company-mode))

(use-package company-jedi
  :ensure t
  :config
    (require 'company)
    (add-to-list 'company-backends 'company-jedi))

(defun python-mode-company-init ()
  (setq-local company-backends '((company-jedi
                                  company-etags
                                  company-dabbrev-code))))

(use-package company-jedi
  :ensure t
  :config
    (require 'company)
    (add-hook 'python-mode-hook 'python-mode-company-init))

emacs-lisp

eldoc

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

yasnippet

(add-hook 'emacs-lisp-mode-hook 'yas-minor-mode)

company

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

(use-package slime
  :ensure t
  :config
  (setq inferior-lisp-program "/usr/bin/sbcl")
  (setq slime-contribs '(slime-fancy)))

(use-package slime-company
  :ensure t
  :init
    (require 'company)
    (slime-setup '(slime-fancy slime-company)))

lua

yasnippet

(add-hook 'lua-mode-hook 'yas-minor-mode)

flycheck

(add-hook 'lua-mode-hook 'flycheck-mode)

company

(add-hook 'lua-mode-hook 'company-mode)

(defun custom-lua-repl-bindings ()
  (local-set-key (kbd "C-c C-s") 'lua-show-process-buffer)
  (local-set-key (kbd "C-c C-h") 'lua-hide-process-buffer))

(defun lua-mode-company-init ()
  (setq-local company-backends '((company-lua
                                  company-etags
                                  company-dabbrev-code))))

(use-package company-lua
  :ensure t
  :config
    (require 'company)
    (setq lua-indent-level 4)
    (setq lua-indent-string-contents t)
    (add-hook 'lua-mode-hook 'custom-lua-repl-bindings)
    (add-hook 'lua-mode-hook 'lua-mode-company-init))

bash

yasnippet

(add-hook 'shell-mode-hook 'yas-minor-mode)

flycheck

(add-hook 'shell-mode-hook 'flycheck-mode)

company

(add-hook 'shell-mode-hook 'company-mode)

(defun shell-mode-company-init ()
  (setq-local company-backends '((company-shell
                                  company-shell-env
                                  company-etags
                                  company-dabbrev-code))))

(use-package company-shell
  :ensure t
  :config
    (require 'company)
    (add-hook 'shell-mode-hook 'shell-mode-company-init))

Git integration

Countless are the times where I opened ansi-term to use git on something. These times are also something that I’d prefer stay in the past, since magit is great. It’s easy and intuitive to use, shows its options at a keypress and much more.

magit

(use-package magit
  :ensure t
  :config
  (setq magit-push-always-verify nil)
  (setq git-commit-summary-max-length 50)
  :bind
  ("M-g" . magit-status))

Remote editing

I have no need to directly edit files over SSH, but what I do need is a way to edit files as root. Opening up nano in a terminal as root to play around with grubs default settings is a no-no, this solves that.

Editing with sudo

Pretty self-explanatory, useful as hell if you use exwm.

(use-package sudo-edit
  :ensure t
  :bind
    ("s-e" . sudo-edit))

Org

One of the absolute greatest features of emacs is called “org-mode”. This very file has been written in org-mode, a lot of other configurations are written in org-mode, same goes for academic papers, presentations, schedules, blogposts and guides. Org-mode is one of the most complex things ever, lets make it a bit more usable with some basic configuration.

Those are all rather self-explanatory.

Common settings

(setq org-ellipsis " ")
(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)
(setq org-confirm-babel-evaluate nil)
(setq org-export-with-smart-quotes t)
(setq org-src-window-setup 'current-window)
(add-hook 'org-mode-hook 'org-indent-mode)

Syntax highlighting for documents exported to HTML

(use-package htmlize
  :ensure t)

Line wrapping

(add-hook 'org-mode-hook
	    '(lambda ()
	       (visual-line-mode 1)))

Keybindings

(global-set-key (kbd "C-c '") 'org-edit-src-code)

Org Bullets

Makes it all look a bit nicer, I hate looking at asterisks.

(use-package org-bullets
  :ensure t
  :config
    (add-hook 'org-mode-hook (lambda () (org-bullets-mode))))

Easy-to-add emacs-lisp template

Hitting tab after an “<el” in an org-mode file will create a template for elisp insertion.

(add-to-list 'org-structure-template-alist
	       '("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))

Exporting options

One of the best things about org is the ability to export your file to many formats. Here is how we add more of them!

latex

(when (file-directory-p "/usr/share/emacs/site-lisp/tex-utils")
  (add-to-list 'load-path "/usr/share/emacs/site-lisp/tex-utils")
  (require 'xdvi-search))

Twitter Bootstrap

(use-package ox-twbs
  :ensure t)

Diminishing modes

Your modeline is sacred, and if you have a lot of modes enabled, as you will if you use this config, you might end up with a lot of clutter there, the package diminish disables modes on the mode line but keeps them running, it just prevents them from showing up and taking up space.

THIS WILL BE REMOVED SOON AS USE-PACKAGE HAS THE FUNCTIONALITY BUILT IN

Edit this list as you see fit!

(use-package diminish
  :ensure t
  :init
  (diminish 'which-key-mode)
  (diminish 'linum-relative-mode)
  (diminish 'hungry-delete-mode)
  (diminish 'visual-line-mode)
  (diminish 'subword-mode)
  (diminish 'beacon-mode)
  (diminish 'irony-mode)
  (diminish 'page-break-lines-mode)
  (diminish 'auto-revert-mode)
  (diminish 'rainbow-delimiters-mode)
  (diminish 'rainbow-mode))

Instant messaging

I like IRC, I also like other protocols but I enjoy IRC most, it’s obvious that I long for a way to do my messaging from within emacs. There is plenty of IRC clients in the repositories, and some more in the emacs repositories but I find that the default erc does the job best, it’s easy to use and offers some conveniences that more sophisticated ones don’t, so I use it.

erc, also known as “a way to ask for help on #emacs”

You might want to edit the default nick, it’s password protected anyway so don’t bother.

Some common settings

This also hides some of the channel messages to avoid cluttering the buffer. The other line changes the prompt for each channel buffer to match the channel name, this way you always know who you are typing to.

(setq erc-nick "daedreth")
(setq erc-prompt (lambda () (concat "[" (buffer-name) "]")))
(setq erc-hide-list '("JOIN" "PART" "QUIT"))

Poor mans selectable server list

What it says on the tin, this changes the erc history to include the server I connect to often.

(setq erc-server-history-list '("irc.freenode.net"
                                "localhost"))

Nick highlighting

You can even highlight nicks to make the buffers a bit more visually pleasing and easier to look at.

(use-package erc-hl-nicks
  :ensure t
  :config
    (erc-update-modules))

rich presence for discord

Memes, but it’s fun and tiny.

(use-package elcord
  :ensure t)

Media

Why bother with an external media manager when emacs is a thing. EMMS is huge, incredibly powerful and luckily well documented. All I need it for is to play music and video, that’s it. I also need it to display metadata on the modeline correctly, which it does with mpd automatically.

EMMS with mpd

There is many backends, many players and codecs for EMMS, we use mpd now.

Basic setup for mpd

The non XF86 keys are made to be somewhat logical to follow and easy to remember. At the bottom part of the configuration, you will notice how XF86 keys are used by default, so unless you keyboard is broken it should work out of the box. Obviously you might have to adjust server-name and server-port to fit your configuration.

(use-package emms
  :ensure t
  :config
    (require 'emms-setup)
    (require 'emms-player-mpd)
    (emms-all) ; don't change this to values you see on stackoverflow questions if you expect emms to work
    (setq emms-seek-seconds 5)
    (setq emms-player-list '(emms-player-mpd))
    (setq emms-info-functions '(emms-info-mpd))
    (setq emms-player-mpd-server-name "localhost")
    (setq emms-player-mpd-server-port "6601")
  :bind
    ("s-m p" . emms)
    ("s-m b" . emms-smart-browse)
    ("s-m r" . emms-player-mpd-update-all-reset-cache)
    ("<XF86AudioPrev>" . emms-previous)
    ("<XF86AudioNext>" . emms-next)
    ("<XF86AudioPlay>" . emms-pause)
    ("<XF86AudioStop>" . emms-stop))

MPC Setup

Setting the default port

We use non-default settings for the socket, to use the built in mpc functionality we need to set up a variable. Adjust according to your setup.

(setq mpc-host "localhost:6601")

Some more fun stuff

Starting the daemon from within emacs

If you have an absolutely massive music library, it might be a good idea to get rid of mpc-update and only invoke it manually when needed.

(defun mpd/start-music-daemon ()
  "Start MPD, connects to it and syncs the metadata cache."
  (interactive)
  (shell-command "mpd")
  (mpd/update-database)
  (emms-player-mpd-connect)
  (emms-cache-set-from-mpd-all)
  (message "MPD Started!"))
(global-set-key (kbd "s-m c") 'mpd/start-music-daemon)

Killing the daemon from within emacs

(defun mpd/kill-music-daemon ()
  "Stops playback and kill the music daemon."
  (interactive)
  (emms-stop)
  (call-process "killall" nil nil nil "mpd")
  (message "MPD Killed!"))
(global-set-key (kbd "s-m k") 'mpd/kill-music-daemon)

Updating the database easily.

(defun mpd/update-database ()
  "Updates the MPD database synchronously."
  (interactive)
  (call-process "mpc" nil nil nil "update")
  (message "MPD Database Updated!"))
(global-set-key (kbd "s-m u") 'mpd/update-database)