Emacs configuration file

Table of Contents

Basics

My configuration is modal in nature. The best example of a modal editor is Vim. In fact, I use Vim every now and then and was a Vim user for almost 4 years before I switched to Emacs (primarily because of Org mode). I like modal editing as a concept and have set it up that way. That’s probably the most important thing about this configuration. This is better explained under the section - Modal states. However, it can still be used as a non-modal editor too since all bindings start with M-m as a leader key except for some basic defaults.

The best way to get help is to ask Emacs about it. C-h is the prefix key for getting help. This means pressing Control and h together. So, if you are new to Emacs you might want to start by pressing C-h C-h, i.e., press C-h twice. Everything else regarding help will be explained along the way.

Finally, all Emacs configuration resides in a directory named the user-emacs-directory which is ~/.emacs.d in most UNIX-like systems and all changes/configuration for Emacs is stored in a file called init.el. The most important command, IMO, M-x which stands for press “Meta” and then press x. “Meta” is usually the Alt key.

Also, the current configuration is tested only on OS X so far.

To get the complete list of key-bindings, check out the relevant section.

Installing Emacs

On OS X, the best way to install Emacs is to use the Emacs Mac Port. It can easily be done via the command line after installing Homebrew like so:

brew tap railwaycat/emacsmacport
brew install emacs-mac --with-gnutls --with-imagemagick --with-xml2 --with-d-bus --with-modern-icon
brew linkapps emacs-mac

Note that, in this port, the “Meta” key is actually the OS X Command key. I prefer it this way but if you want to change this to the Option key, you need to put the following code in the appropriate place in Defaults, or in your init.el file.

(when (eq system-type 'darwin)
  (setq mac-option-modifier 'meta))

To install GNU Emacs,

brew install emacs --with-imagemagick --with-d-bud --with-cocoa

Getting this configuration

This configuration resides on GitHub in my repository.

If you have an already existing configuration, it is better to back it up.

mv ~/.emacs.d ~/.emacs.d.bak

Else, you can just delete it.

rm -rf ~/.emacs.d

Then, clone this repository in .emacs.d

git clone https://github.com/sriramkswamy/dotemacs.git ~/.emacs.d

If you are just updating the configuration, you can just pull the latest changes

cd ~/.emacs.d && git pull

Init file

The init.el file in the Emacs directory is the file Emacs looks to load when it starts up. Since I’m planning to have THIS org file as my configuration file, The actual init.el file is very simple. I first increase the garbage collection to around 500 MB so that Emacs doesn’t pause much during the load. Then, I add a few repositories for us to download packages from. Finally, I make sure Org and Org contributed packages are installed before telling Org babel to load this file. Finally, I reduce the threshold for garbage collection once again so that Emacs functions smoothly.

;;; init.el --- Global settings -*- lexical-binding: t; -*-

;;; Commentary:

;; Here be dragons

;;; Code:

;; Increase the garbage collection threshold to 500 MB to ease startup
(setq gc-cons-threshold (* 500 1024 1024))

;; List package archives and initialize them
(require 'package)
(when (>= emacs-major-version 24)
  (require 'package)
  (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)
  (add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t))
(when (< emacs-major-version 24)
  ;; For important compatibility libraries like cl-lib
  (add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)

;; Make sure Org is installed
(unless (package-installed-p 'org)
  (package-refresh-contents)
  (package-install 'org))

;; Org plus contrib needs to be loaded before any org related functionality is called
(unless (package-installed-p 'org-plus-contrib)
  (package-refresh-contents)
  (package-install 'org-plus-contrib))

;; Load config.org - my Emacs configuration
(org-babel-load-file (concat user-emacs-directory "config.org"))

;; Garbage collector - decrease threshold to 5 MB
(add-hook 'after-init-hook (lambda () (setq gc-cons-threshold (* 5 1024 1024))))
;;; init.el ends here

Emacs has a built-in garbage collection mechanism (read this article for a good intro to garbage collection in general). However, the memory size for which it fires is set pretty low and for the right reasons. Since I operate on a pretty good machine, I set it high initially (around 500 MB) and later reduce it. Therefore, the only thing in the init.el file is the GC increase, loading this org file and reduce the threshold again.

This configuration

My current configuration is the actual file you are reading. All the intertwined pieces of code are collected and put in to a separate file thanks to Org and Org babel. So, if this file is named config.org, a corresponding config.el file will be produced and loaded. By default, all the Emacs Lisp codes are collected. However, there might be some cases where I want to document how to do something but don’t particularly want to do it. There are few portions like this - Init file being one of them. We can tell babel to ignore a code by giving the option :tangle no to the source code. The following is an example. You can view the actual code by opening this file in Emacs.

#+BEGIN_SRC emacs-lisp :tangle no
(message "I don't exist!")

#+ENDSRC

Learning Emacs Lisp

Emacs Lisp is the name of the language Emacs we use to configure Emacs. Start with this article for a good introduction to the language and this one for the style of Emacs Lisp. Learning Emacs Lisp in 15 minutes is a good introduction too. This can be further complemented by the Introduction to Emacs Lisp and the Emacs Lisp manual.

Profiling

Profiling is a way to track and minimize the startup time of Emacs. The current config takes about 3 seconds to load in my computer. I use the profile-dotemacs file for all the profiling. Since my Init file already takes care of initializing packages, I need not do it here. However, while profiling, I profile my init.el separately and the config.el this Org file creates separately. In order for the config.el file to be profiled separately, the packages need to be initialized again. For now, I tell Emacs to not include the code below into the config.el file by explicitly giving the option :tangle no. This option can be viewed by clicking the Raw view file option on GitHub or just opening the Org file in Emacs. When I want to profile it, I just change the no to yes and call the aliases I have set up in my dotfiles from the terminal.

(require 'package)
(package-initialize)

Defaults

Personal information

I’m not too big on letting Emacs manage a lot of personal things but I do want it to know my name. Therefore,

(setq user-full-name "Sriram Krishnaswamy")

Custom file

Emacs has a built-in customization interface that helps beginners customize a lot of options. However it is limited and one needs to grok some Emacs Lisp anyway. Sometimes, I change stuff in my configuration that invokes/adds via the customization interface. This results in automatic inserting of some code into my init.el which messes up the version control. We tell Emacs to store in a separate file and load it if it exists.

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

Packages

Packages are managed by package.el but it has access to only the default ELPA repository by default. Let’s add more repositories and initialize it. Note that the (package-initialize) operation takes a while and is one of the major factors that increase startup time. The packages are loaded and initialized in the Init file.

Startup

Disabling some GUI elements

Emacs has a different GUI implementation to that of its terminal implementation and I prefer the GUI any day. But there are some things that I find annoying - like tool bar, menu bar, scroll bar and the tool tip. Therefore, I disable it.

(when window-system
  (tool-bar-mode 0)
  (scroll-bar-mode 0)
  (tooltip-mode 0))

Set the initial frame size

Frames in Emacs parlance are the equivalent of windows in normal usage. So, when you open Emacs, it opens a frame and not a window. Windows are the what splits are referred to. For further reference look it up in the Emacs manual.

;; initial window
(setq initial-frame-alist
      '((width . 102)   ; characters in a line
        (height . 54))) ; number of lines

;; sebsequent frame
(setq default-frame-alist
      '((width . 100)   ; characters in a line
        (height . 52))) ; number of lines

Set the cursor shape

Since I use a modal configuration, it is so easy to detect modes by just changing the cursor shape. Also, the “bar” cursor feels more natural because that’s what most application outside of Emacs use anyway. Also, while I’m at it, I don’t want the cursor to blink either.

;; Bar cursor
(setq-default cursor-type '(bar . 1))
;; Don't blink the cursor
(blink-cursor-mode -1)

Initial screen

The default screen of Emacs is just so useless and cluttered with links and text and help pages. I just need a simple start screen which gently reminds me some things that are important while staying out of the most of the time. This is also called the scratch buffer.

;; No welcome screen - opens directly in scratch buffer
(setq inhibit-startup-message t
      initial-scratch-message ""
      initial-major-mode 'fundamental-mode
      inhibit-splash-screen t)

Startup echo message

There is a small advert for GNU that is displayed in the mini-buffer during startup and, of course, there is a way to change it.

;; Change the echo message
(defun display-startup-echo-area-message ()
  (message "Let the games begin!"))

Backups

Emacs takes regular backups of once you switch on auto-saving and, by default, puts the backups in the same directory. This is annoying most of the time since I tend to save pretty often and also clobbers with my file system tree when I’m trying to search within the files. Therefore, Emacs wiki has a pretty nice solution for that, which keeps various versions of the backup file and stores it under a folder in the home directory.

;; Backups at .saves folder in the current folder
(setq backup-by-copying t      ; don't clobber symlinks
      backup-directory-alist
      '(("." . "~/.saves"))    ; don't litter my fs tree
      delete-old-versions t
      kept-new-versions 6
      kept-old-versions 2
      version-control t)       ; use versioned backups

Auto saving

I make sure Emacs auto-saves often but the result is that it messes up my file tree. So, let’s ask Emacs to store its backups in some temporary directory.

(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
      create-lockfiles nil)

File encoding system

UTF-8 works for most of the files I tend to use

(prefer-coding-system 'utf-8)
(setq-default buffer-file-coding-system 'utf-8-auto-unix)

Error message

Mac has an annoying bug when visibly warning you about errors. I hate it. And while we’re at it, let’s ask Emacs to ignore the audible warning too.

(setq visible-bell nil)
(setq ring-bell-function 'ignore)

Truncating lines

Emacs has an option to wrap lines which is disabled by default. What this means is that, if the text goes beyond the screen, it will wrap it so that you can see all the text. However, it wraps it with these weird indicators that I find more annoying. Plus, I make sure my code stays within 100 characters always and prefer the soft line wrap while writing prose. Let’s make sure it doesn’t wrap again.

(setq-default truncate-lines t)

Large file warning

Whenever, a large file (by Emacs standards) is opened, it asks for confirmation whether we really want to open it but the problem is the limit for this file is set pretty low. Let’s increase it a bit so that it doesn’t prompt so often.

(setq large-file-warning-threshold (* 15 1024 1024))

Lazier prompting

While we are in the topic of prompting, a lot of the default prompts ask for a yes or a no. I’m lazy and so I don’t want to type the full words. Let’s just make it accept y or n.

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

Expand some words and auto-correct

abbrev-mode or abbreviation mode is a built-in mode that auto-corrects the word you mistype on pressing space. For how I practically use it, see the auto-correction section.

(setq save-abbrevs 'silently)
(setq-default abbrev-mode t)

gdb

gdb is the GNU debugger which is used to debug programs. Let’s make it multi-windowed like all the other debuggers out there.

(setq gdb-many-windows t
      gdb-show-main t)

ediff

Ediff mode is a UNIX patching tool and my version controlling package uses this to help resolve merge conflicts and having some better defaults will be useful for this.

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

tramp

Tramp lets you edit files remotely from your local Emacs which is useful because it lets you have all the default configuration. Let’s make sure the default protocol is ssh.

(setq tramp-default-method "ssh"
      tramp-backup-directory-alist backup-directory-alist
      tramp-ssh-controlmaster-options "ssh")

Move correctly over camelCased words

subword-mode is a built-in mode that helps moving over camelCase words correctly.

(subword-mode)

Understand the more common sentence

By default, Emacs thinks a sentence is a full-stop followed by 2 spaces. Let’s make it full-stop and 1 space.

(setq sentence-end-double-space nil)

Recenter screen

Emacs lets you move the current line to the top, middle or bottom of the screen to get appropriate context. The default goes to the middle first. I prefer that the default goes to the top first. Let’s change this.

(setq recenter-positions '(top middle bottom))

Better wild cards in search

The built-in incremental search is pretty good but the most common regex I type is .* which stands for anything. This makes sure space between words acts the same way. It’s much better for me to use it now.

(setq search-whitespace-regexp ".*?")

Persistent history

I prefer to have some persistent history for some prompts.

(savehist-mode)

Narrow to region

This is such a an amazing feature but is disabled by default. Let’s re-enable it. For further reference on narrow region, refer to the Emacs manual.

(put 'narrow-to-region 'disabled nil)

PDF files

Emacs has the built-in DocView mode which lets me view PDFs. Since I use Org and note taking extensively, I actually prefer reading PDFs in Emacs. Not to mention, this is one of the few PDF readers that lets me view the PDF in split-views which is immensely useful while reading research papers.

(setq doc-view-continuous t)

Window management

Winner mode is an Emacs built-in package that lets you undo and redo window configurations. Incredibly useful since I keep splitting and merging windows all the time. Let’s enable it.

(when (fboundp 'winner-mode)
  (winner-mode 1))

Recent files

An Emacs “mode” is a collection of behavior. It has both major and minor modes. One such useful mode is the recentf-mode, which stands for recent files mode. Let’s give configure some options and enable it.

;; Recentf mode changes
(setq recentf-max-saved-items 1000
      recentf-exclude '("/tmp/" "/ssh:"))
(recentf-mode)

Fullscreen

In Mac, the default fullscreen goes to a new workspace. Change this behavior so that it’s non-native.

(setq ns-use-native-fullscreen nil)

Fonts

Switching font is something I do quite often. Also, zooming in text isn’t the same as just increasing the font size. So, these are some convenience functions taken from Jay Dixit’s emacs configuration and Oleh Krehel’s configuration.

(cond ((eq system-type 'gnu/linux)
       (set-frame-font "DejaVu Sans Mono"))
      ((eq system-type 'darwin)
       (set-frame-font "Monaco"))
      ((eq system-type 'windows-nt)
       (set-frame-font "Lucida Sans Typewriter")))
;;; Some convenience font functions
(defun sk/courier-font ()
  (interactive)
  (set-face-attribute 'default nil :font "Courier")
    (set-frame-width (selected-frame) 97))
(defun sk/georgia-font ()
  (interactive)
  (set-face-attribute 'default nil :font "Georgia" :height 160))
(defun sk/hack-font ()
  (interactive)
  (set-face-attribute 'default nil :font "Hack"))
(defun sk/monaco-font ()
  (interactive)
  (set-face-attribute 'default nil :font "Monaco"))
(defun sk/consolas-font ()
  (interactive)
  (set-face-attribute 'default nil :font "Consolas"))
(defun sk/deja-vu-font ()
  (interactive)
  (set-face-attribute 'default nil :font "DejaVu Sans Mono"))

;; Font types
(defun sk/tiny-type ()
  (interactive)
  (set-face-attribute 'default nil  :height 150))
(defun sk/miniscule-type ()
  (interactive)
  (set-face-attribute 'default nil  :height 140))
(defun sk/small-type ()
  (interactive)
  (set-face-attribute 'default nil  :height 190)
  (set-frame-width (selected-frame) 89))
(defun sk/medium-type ()
  (interactive)
  (set-face-attribute 'default nil  :height 215)
  (set-frame-width (selected-frame) 89))
(defun sk/large-type ()
  (interactive)
  (set-face-attribute 'default nil  :height 350)
  (set-frame-width (selected-frame) 68))

Use package macro

use-package is a macro that helps in downloading and managing package a breeze. It has lots of options for lazy loading and configurations and this configuration relies on it completely for all package management. This is a requirement. The following piece of code checks if it is already installed and installs it if it is not and also loads it and bind-key. I should extend this to add key words for :modalka and :which-key.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(eval-when-compile
  (require 'use-package))
(require 'bind-key)                ;; if you use any :bind variant

dired

Dired is an amazing file/directory browser that comes bundled with Emacs but I don’t like it displaying all the details regarding the files when I open it. I prefer it minimal.

(use-package dired
  :bind (:map dired-mode-map
              ("C-c C-e" . wdired-change-to-wdired-mode))
  :init
  (setq dired-dwim-target t
        dired-recursive-copies 'top
        dired-recursive-deletes 'top
        dired-listing-switches "-alh")
  :config
  (add-hook 'dired-mode-hook 'dired-hide-details-mode))

Diminish minor modes from the mode line

Now that we have made sure we have installed use-package, we will make sure another nice package to change the mode-line minor mode list. For this, we can use use-package itself and also go ahead and diminish some built-in minor modes.

(use-package diminish
  :ensure t
  :demand t
  :diminish (visual-line-mode . "ω")
  :diminish hs-minor-mode
  :diminish abbrev-mode
  :diminish auto-fill-function
  :diminish subword-mode)

However, some built-in minor modes are notorious and don’t seem to work with the above method. Let’s diminish them using functions one by one.

  • Diminish org-indent mode

    I like to enable the org-indent mode for a clean view in Org mode and this doesn’t seem to get diminish the usual way. We define a function and a add a hook to achieve this.

    (defun sk/diminish-org-indent ()
      (interactive)
      (diminish 'org-indent-mode ""))
    (add-hook 'org-indent-mode-hook 'sk/diminish-org-indent)
    
  • Diminish auto-revert mode

    auto-revert mode is useful when Emacs auto-saves your file and you want to load the backup.

    (defun sk/diminish-auto-revert ()
      (interactive)
      (diminish 'auto-revert-mode ""))
    (add-hook 'auto-revert-mode-hook 'sk/diminish-auto-revert)
    
  • Diminish eldoc mode

    Eldoc mode is a mode to display documentation for languages in Emacs

    (defun sk/diminish-eldoc ()
      (interactive)
      (diminish 'eldoc-mode ""))
    (add-hook 'eldoc-mode-hook 'sk/diminish-eldoc)
    
  • Diminish subword mode

    subword-mode is described here.

    (defun sk/diminish-subword ()
      (interactive)
      (diminish 'subword-mode ""))
    (add-hook 'subword-mode-hook 'sk/diminish-subword)
    

Manage the built-in flyspell mode

Flyspell mode is a built-in mode for prose spell-checking. It sometimes slows down Emacs. It also has flyspell-prog-mode which checks for spelling in programming comments.

(use-package flyspell
  :diminish (flyspell-mode . "φ")
  :bind* (("M-m ] s" . flyspell-goto-next-error)))

Manage the built-in browser eww

(use-package eww
  :bind* (("M-m g x" . eww)
          ("M-m g :" . eww-browse-with-external-browser)
          ("M-m g #" . eww-list-histories)
          ("M-m g {" . eww-back-url)
          ("M-m g }" . eww-forward-url))
  :config
  (progn
    (add-hook 'eww-mode-hook 'visual-line-mode)))

Add a package to set the correct path

Sometimes when opening the Mac Emacs.app via GUI, by clicking the button, it doesn’t load all the proper utilities from the OS. This package fixes that.

(use-package exec-path-from-shell
  :ensure t
  :demand t
  :init
  (setq exec-path-from-shell-check-startup-files nil)
  :config
  ;; (exec-path-from-shell-copy-env "PYTHONPATH")
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

Add some useful libraries

async, s, dash, and cl-lib are libraries for asynchronous processing, string manipulation, list manipulation and backward compatibility respectively.

(use-package async
  :ensure t
  :commands (async-start))

(use-package cl-lib
  :ensure t)

(use-package dash
  :ensure t)

(use-package s
  :ensure t)

Restart emacs from emacs

The heading says it all. I find it useful

(use-package restart-emacs
  :ensure t
  :bind* (("C-x M-c" . restart-emacs)))

Bind key to bind some unbound defaults

There are some pretty nice default functions that are unbound. I use the bind-key package that comes along with use-package to bind these keys. I hijack the M-m (which can be command + m or alt + m depending on how you configure it) prefix. I find the default action bound to M-m - going to the first non-whitespace character in line - pretty useless as our smarter start of line already takes care of this for us.

(bind-keys*
  ("C-r"       . dabbrev-expand)
  ("M-/"       . hippie-expand)
  ("C-S-d"     . kill-whole-line)
  ("M-m SPC c" . load-theme)
  ("M-m SPC R" . locate)
  ("M-m W"     . winner-undo)
  ("M-m g m"   . make-frame)
  ("M-m g M"   . delete-frame)
  ("M-m g n"   . select-frame-by-name)
  ("M-m g N"   . set-frame-name)
  ("M-m B"     . mode-line-other-buffer)
  ("M-m ="     . indent-region)
  ("M-m g ("   . Info-prev)
  ("M-m g )"   . Info-next)
  ("M-m ^"     . Info-up)
  ("M-m &"     . Info-goto-node)
  ("M-m g f"   . find-file-at-point)
  ("M-m g u"   . downcase-region)
  ("M-m g U"   . upcase-region)
  ("M-m g C"   . capitalize-region)
  ("M-m g F"   . follow-mode)
  ("M-m R"     . overwrite-mode)
  ("M-m g j"   . doc-view-next-page)
  ("M-m g k"   . doc-view-previous-page)
  ("M-m : t"   . emacs-init-time)
  ("M-m g q"   . fill-paragraph)
  ("M-m g @"   . compose-mail)
  ("M-m SPC ?" . describe-bindings))

Tangle on save

This was taken from Alan Pearce’s dotfiles so as to tangle it on save instead of tangling it every time I open it again.

(defun tangle-if-init ()
  "If the current buffer is 'init.org' the code-blocks are
    tangled, and the tangled file is compiled."

  (when (string-suffix-p "config.org" (buffer-file-name))
    (tangle-init)))

(defun tangle-init-sync ()
  (interactive)
  (message "Tangling init")
  ;; Avoid running hooks when tangling.
  (let ((prog-mode-hook nil)
        (src  (expand-file-name "config.org" user-emacs-directory))
        (dest (expand-file-name "config.el"  user-emacs-directory)))
    (require 'ob-tangle)
    (org-babel-tangle-file src dest)
    (if (byte-compile-file dest)
        (byte-compile-dest-file dest)
      (with-current-buffer byte-compile-log-buffer
        (buffer-string)))))

(defun tangle-init ()
  "Tangle init.org asynchronously."

  (interactive)
  (message "Tangling init")
  (async-start
   (symbol-function #'tangle-init-sync)
   (lambda (result)
     (message "Init tangling completed: %s" result))))

Key hints

Which key

Emacs has 100s of bindings and it is impossible to remember them all. Sometimes I can remember the start of a key chord but not the entire one. Which-key is a package that gives you key hints on delay or if prompted. I really like it and use it extensively to setup the modal state.

(use-package which-key
  :ensure t
  :defer t
  :diminish which-key-mode
  :init
  (setq which-key-sort-order 'which-key-key-order-alpha)
  :bind* (("M-m ?" . which-key-show-top-level))
  :config
  (which-key-mode)
  (which-key-add-key-based-replacements
    "M-m ?" "top level bindings"))

Discover my major

This package helps to discover the major mode bindings. I use it very occasionally and hence not binding it to any modal binding.

(use-package discover-my-major
  :ensure t
  :bind (("C-h C-m" . discover-my-major)
         ("C-h M-m" . discover-my-mode)))

Modal states

Modalka mode

As mentioned before, I like and prefer modal editing. I feel pressing modifier keys all the time just leads to wrist pain and repetitive stress injuries. Modalka is a package that is pretty simple and gives a platform for anyone to implement a modal mode. Let’s call this state Modalka state and the normal Emacs way as Emacs state. This is also one of the core packages that I rely on. First, to load and configure some defaults. Note that when using in terminals, the default toggle (pressing escape key) might not work because of how terminals operate. I have added another key to toggle it in that case - C-z (which is control + z) - bound to suspend-frame by default. This same function is also bound to C-x C-z by default and this binding seems pointless to me. We use M-m as sort of a “leader” key, as in all bindings start from there. This can be easily remembered as the mnemonic of “Modalka mode” (M-m).

(use-package modalka
  :ensure t
  :demand t
  :bind* (("C-z" . modalka-mode))
  :diminish (modalka-mode . "μ")
  :init
  (setq modalka-cursor-type 'box)
  :config
  (global-set-key (kbd "<escape>") #'modalka-mode)
  (modalka-global-mode 1)
  (add-to-list 'modalka-excluded-modes 'magit-status-mode)
  (add-to-list 'modalka-excluded-modes 'magit-popup-mode)
  (add-to-list 'modalka-excluded-modes 'eshell-mode)
  (add-to-list 'modalka-excluded-modes 'deft-mode)
  (add-to-list 'modalka-excluded-modes 'term-mode)
  (which-key-add-key-based-replacements
    "M-m"     "Modalka prefix"
    "M-m :"   "extended prefix"
    "M-m m"   "move prefix"
    "M-m s"   "send code prefix"
    "M-m SPC" "user prefix"
    "M-m g"   "global prefix"
    "M-m o"   "org prefix"
    "M-m a"   "expand around prefix"
    "M-m i"   "expand inside prefix"
    "M-m ["   "prev nav prefix"
    "M-m ]"   "next nav prefix"))
  • Default modal keybindings

    These are modal bindings that just wrap around the existing, already bound, Emacs defaults. The explanations for each can be found in the corresponding which key explanations.

    • Numbers

      Pressing the numbers in Modalka state should trigger numbers and not insert them instead.

      (modalka-define-kbd "0" "C-0")
      (modalka-define-kbd "1" "C-1")
      (modalka-define-kbd "2" "C-2")
      (modalka-define-kbd "3" "C-3")
      (modalka-define-kbd "4" "C-4")
      (modalka-define-kbd "5" "C-5")
      (modalka-define-kbd "6" "C-6")
      (modalka-define-kbd "7" "C-7")
      (modalka-define-kbd "8" "C-8")
      (modalka-define-kbd "9" "C-9")
      
    • Movement and one key presses

      This is based on Vi-style but only slightly and uses the default movement keys in Emacs.

      (modalka-define-kbd "h" "C-b")
      (modalka-define-kbd "j" "C-n")
      (modalka-define-kbd "k" "C-p")
      (modalka-define-kbd "l" "C-f")
      (modalka-define-kbd "e" "M-f")
      (modalka-define-kbd "b" "M-b")
      (modalka-define-kbd "n" "M-n")
      (modalka-define-kbd "N" "M-p")
      (modalka-define-kbd "{" "M-{")
      (modalka-define-kbd "}" "M-}")
      (modalka-define-kbd "0" "C-a")
      (modalka-define-kbd "$" "C-e")
      (modalka-define-kbd "G" "M->")
      (modalka-define-kbd "y" "M-w")
      (modalka-define-kbd "p" "C-y")
      (modalka-define-kbd "P" "M-y")
      (modalka-define-kbd "x" "C-d")
      (modalka-define-kbd "D" "C-k")
      (modalka-define-kbd "z" "C-l")
      (modalka-define-kbd "!" "M-&")
      (modalka-define-kbd "J" "C-v")
      (modalka-define-kbd "K" "M-v")
      (modalka-define-kbd "M" "C-u")
      (modalka-define-kbd "(" "M-a")
      (modalka-define-kbd ")" "M-e")
      (modalka-define-kbd "/" "C-s")
      (modalka-define-kbd "E" "C-g")
      (modalka-define-kbd "d" "C-w")
      (modalka-define-kbd "w" "C-x o")
      (modalka-define-kbd "W" "M-m W")
      (modalka-define-kbd "B" "M-m B")
      (modalka-define-kbd "H" "C-x >")
      (modalka-define-kbd "L" "C-x <")
      (modalka-define-kbd "Z" "C-x 1")
      (modalka-define-kbd "q" "C-x (")
      (modalka-define-kbd "Q" "C-x )")
      (modalka-define-kbd "." "M-m .")
      (modalka-define-kbd "?" "M-m ?")
      (modalka-define-kbd "v" "C-SPC")
      (modalka-define-kbd "V" "M-m V")
      (modalka-define-kbd "=" "M-m =")
      (modalka-define-kbd "R" "M-m R")
      (modalka-define-kbd "X" "C-x C-x")
      (modalka-define-kbd "+" "C-x r m")
      (modalka-define-kbd "'" "C-x r b")
      (modalka-define-kbd "\\" "C-c C-c")
      
    • Global prefixed keys
      (modalka-define-kbd "g g" "M-<")
      (modalka-define-kbd "g o" "C-x C-e")
      (modalka-define-kbd "g O" "C-M-x")
      (modalka-define-kbd "g m" "M-m g m")
      (modalka-define-kbd "g M" "M-m g M")
      (modalka-define-kbd "g n" "M-m g n")
      (modalka-define-kbd "g N" "M-m g N")
      (modalka-define-kbd "g f" "M-m g f")
      (modalka-define-kbd "g F" "M-m g F")
      (modalka-define-kbd "g j" "M-m g j")
      (modalka-define-kbd "g k" "M-m g k")
      (modalka-define-kbd "g q" "M-m g q")
      (modalka-define-kbd "g w" "C-x 3")
      (modalka-define-kbd "g W" "C-x 2")
      (modalka-define-kbd "g @" "M-m g @")
      (modalka-define-kbd "g ;" "M-m g ;")
      (modalka-define-kbd "g :" "M-m g :")
      (modalka-define-kbd "g #" "M-m g #")
      (modalka-define-kbd "g {" "M-m g {")
      (modalka-define-kbd "g }" "M-m g }")
      (modalka-define-kbd "g (" "M-m g (")
      (modalka-define-kbd "g )" "M-m g )")
      (modalka-define-kbd "^" "M-m ^")
      (modalka-define-kbd "&" "M-m &")
      (modalka-define-kbd "g S" "C-j")
      (modalka-define-kbd "g ?" "C-h k")
      
    • Select region prefixed keys
      (modalka-define-kbd "i a" "C-x h")
      
    • Forward navigation prefixed keys
      (modalka-define-kbd "] ]" "C-x n n")
      (modalka-define-kbd "] s" "M-m ] s")
      
    • Backward navigation prefixed keys
      (modalka-define-kbd "[ [" "C-x n w")
      
    • Extended prefix to quit/restart and time
      (modalka-define-kbd ": q" "C-x C-c")
      (modalka-define-kbd ": r" "C-x M-c")
      (modalka-define-kbd ": t" "M-m : t")
      
    • User prefix for common functions
      (modalka-define-kbd "g U" "C-c C-k")
      (modalka-define-kbd "SPC j" "M-x")
      (modalka-define-kbd "SPC a" "C-x b")
      (modalka-define-kbd "SPC k" "C-x k")
      (modalka-define-kbd "SPC g" "M-g g")
      (modalka-define-kbd "SPC d" "C-x d")
      (modalka-define-kbd "SPC q" "C-x 0")
      (modalka-define-kbd "SPC f" "C-x C-f")
      (modalka-define-kbd "SPC w" "C-x C-s")
      (modalka-define-kbd "SPC c" "M-m SPC c")
      (modalka-define-kbd "SPC R" "M-m SPC R")
      (modalka-define-kbd "SPC ?" "M-m SPC ?")
      
  • Which key explanations for everything

    I have added the which key explanations for everything at once because it is easier for me to look it up.

    • Numbers
      (which-key-add-key-based-replacements
        "0" "0"
        "1" "1"
        "2" "2"
        "3" "3"
        "4" "4"
        "5" "5"
        "6" "6"
        "7" "7"
        "8" "8"
        "9" "9")
      
    • Movement and one key presses
      (which-key-add-key-based-replacements
        "ESC" "toggle mode"
        "DEL" "smart del"
        "TAB" "smart tab"
        "RET" "smart enter"
        "h"   "prev char"
        "j"   "next line"
        "k"   "prev line"
        "l"   "next char"
        "e"   "next word"
        "b"   "prev word"
        "n"   "next history item"
        "N"   "prev history item"
        "{"   "next para"
        "}"   "prev para"
        "0"   "start of line"
        "$"   "end of line"
        "("   "start of sentence"
        ")"   "end of sentence"
        "/" "search"
        "E"   "exit anything"
        "B"   "previous buffer"
        "W"   "winner undo"
        "w"   "other window"
        "G"   "end of file"
        "d"   "delete selection"
        "y"   "copy selection"
        "p"   "paste"
        "P"   "paste history"
        "x"   "delete char"
        "D"   "delete rest of line"
        "M"   "modify argument"
        "z"   "scroll center/top/bot"
        "Z"   "zoom into window"
        "H"   "scroll left"
        "J"   "scroll down"
        "K"   "scroll up"
        "L"   "scroll right"
        "'"   "org edit separately"
        "q"   "start macro"
        "Q"   "end macro"
        "?"   "top level bindings"
        "v"   "start selection"
        "R"   "overwrite mode"
        "X"   "exchange point and mark"
        "+"   "set bookmark"
        "'"   "jump to bookmark"
        "="   "indent region"
        "\\"  "C-c C-c"
        "!"   "async shell command"
        "&"   "shell command")
      
    • Global prefixed keys
      (which-key-add-key-based-replacements
        "g"   "global prefix"
        "g g" "start of file"
        "g m" "make frame"
        "g M" "delete frame"
        "g n" "select frame by name"
        "g N" "name frame"
        "g j" "next pdf page"
        "g k" "previous pdf page"
        "g f" "file/url at cursor"
        "g F" "enable follow mode"
        "g o" "eval elisp"
        "g O" "eval defun"
        "g w" "vertical split win"
        "g W" "horizontal split win"
        "g S" "split line"
        "g @" "compose mail"
        "g #" "list eww histories"
        "g x" "browse with eww"
        "g :" "browse with external browser"
        "g {" "eww back"
        "g }" "eww forward"
        "g (" "info previous"
        "g )" "info next"
        "^"   "info up"
        "&"   "info goto"
        "g q" "format para"
        "g ?" "find command bound to key")
      
    • Select region prefixed keys
      (which-key-add-key-based-replacements
        "i"   "expand prefix"
        "i a" "expand entire buffer")
      
    • Forward navigation prefixed keys
      (which-key-add-key-based-replacements
        "]"   "forward nav/edit"
        "] ]" "narrow region"
        "] s" "next spell error")
      
    • Backward navigation prefixed keys
      (which-key-add-key-based-replacements
        "["   "backward nav/edit"
        "[ [" "widen region")
      
    • Extended prefix to quit/restart and time
      (which-key-add-key-based-replacements
        ":"   "extended prefix"
        ": q" "quit emacs"
        ": r" "restart emacs"
        ": t" "initiliazation time")
      
    • User prefix for common functions
      (which-key-add-key-based-replacements
        "SPC"   "custom prefix"
        "SPC ?" "describe bindings"
        "SPC j" "jump to cmd"
        "SPC f" "find file"
        "SPC a" "switch buffers"
        "SPC g" "goto line"
        "SPC d" "dired"
        "SPC k" "close buffer"
        "SPC w" "save buffer"
        "SPC c" "load theme"
        "SPC R" "locate"
        "SPC q" "quit window"
        "g U"   "simulate C-c C-k")
      

Hydras

Hydra is not strictly a modal package but it is one that lets you define sticky bindings and I would call it semi-modal. I love it and need it. Currently, I don’t define an hydras. They are defined under appropriate sections.

(use-package hydra
  :ensure t)

Navigation

This section contains all the packages and custom functions I use for navigating within the buffer, within a project, in Emacs, etc. This is organized based on packages. Deleting the section of a package deletes the entire configuration surrounding it.

Flx

Flx is a package that helps in fuzzy file narrowing like Sublime Text. The most prominent package that uses this is flx-ido found in the same repository. I find ivy’s built-in fuzzy matching more than good enough. However, this is in case I need something different/more and when this is installed, Ivy automatically uses this for fuzzy finding. This is particularly useful if I decide to use Helm full time instead of ivy.

(use-package flx-ido
  :ensure t
  :defer t)

Smex

smex is an amazing program that helps order the M-x commands based on usage and recent items. Let’s install it.

(use-package smex
  :ensure t
  :config
  (smex-initialize))

Beacon mode

Beacon is just a tiny utility that indicates the cursor position when the cursor moves suddenly. You can also manually invoke it by calling the function beacon-blink and it is bound by default.

(use-package beacon
  :ensure t
  :demand t
  :diminish beacon-mode
  :bind* (("M-m g z" . beacon-blink))
  :config
  (beacon-mode 1))
  • Modal binding
    (modalka-define-kbd "g z" "M-m g z")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g z" "blink cursor")
    

Undo tree

The default Emacs undo command is weird. Better undo and redo states are given by undo-tree mode and, as an added bonus, also gives a visualization tree

(use-package undo-tree
  :ensure t
  :diminish undo-tree-mode
  :bind* (("M-m u" . undo-tree-undo)
          ("M-m r" . undo-tree-redo)
          ("M-m U" . undo-tree-visualize))
  :config
  (global-undo-tree-mode 1))
  • Modal binding
    (modalka-define-kbd "u" "M-m u")
    (modalka-define-kbd "U" "M-m U")
    (modalka-define-kbd "r" "M-m r")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "u" "undo"
      "r" "redo"
      "U" "undo tree")
    

Goto the last change

This package goes to the last place I made a change in the file, and compliments marks.

(use-package goto-chg
  :ensure t
  :bind* (("M-m g ;" . goto-last-change)
          ("M-m g ," . goto-last-change-reverse)))
  • Modal binding
    (modalka-define-kbd "g ;" "M-m g ;")
    (modalka-define-kbd "g ," "M-m g ,")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g ;" "goto last change"
      "g ," "goto last change reverse")
    

Avy

Avy is a package that lets you jump anywhere on screen based on character, characters, lines or words. It’s one of my most used packages.

(use-package avy
  :ensure t
  :init
  (setq avy-keys-alist
        `((avy-goto-char-timer . (?j ?k ?l ?f ?s ?d ?e ?r ?u ?i))
          (avy-goto-line . (?j ?k ?l ?f ?s ?d ?e ?r ?u ?i))))
  (setq avy-style 'pre)
  :bind* (("M-m f" . avy-goto-char-timer)
          ("M-m F" . avy-goto-line)))
  • Modal binding
    (modalka-define-kbd "f" "M-m f")
    (modalka-define-kbd "F" "M-m F")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "f" "find on-screen"
      "F" "find line")
    

Highlight symbol

This package is pretty simple. It adds functionality to highlight the current word or symbol and navigate to other instances.

(use-package highlight-symbol
  :ensure t
  :bind (("M-n" . highlight-symbol-next)
         ("M-p" . highlight-symbol-prev))
  :config
  (highlight-symbol-nav-mode))
  • Modal binding
    (modalka-define-kbd "n" "M-n")
    (modalka-define-kbd "N" "M-p")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "n" "smart next item"
      "N" "smart prev item")
    

Projectile

Projectile is a nice package for project navigation. It hijacks the C-c p prefix by default and you can learn more about its commands by pressing C-c p and waiting for which key to show hints.

(use-package projectile
  :ensure t
  :bind* (("M-m SPC d"   . projectile-find-file)
          ("M-m SPC D"   . projectile-switch-project)
          ("M-m SPC TAB" . projectile-find-other-file))
  :init
  (setq projectile-file-exists-remote-cache-expire (* 10 60))
  :diminish projectile-mode
  :config
  (projectile-global-mode))
  • Modal binding
    (modalka-define-kbd "SPC d"   "M-m SPC d")
    (modalka-define-kbd "SPC D"   "M-m SPC D")
    (modalka-define-kbd "SPC TAB" "M-m SPC TAB")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "SPC d"   "project files"
      "SPC D"   "project switch"
      "SPC TAB" "alternate file")
    

Ztree

ztree and its function ztree-diff is super useful when comparing directory trees.

(use-package ztree
  :ensure t
  :bind* (("M-m g v" . ztree-dir)
          ("M-m g V" . ztree-diff))
  :init
  (setq ztree-dir-move-focus t))
  • Modal binding
    (modalka-define-kbd "g v" "M-m g v")
    (modalka-define-kbd "g V" "M-m g V")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g v" "tree directory"
      "g V" "diff directories")
    

Neotree

Neotree is basically a directory drawer that is popular in all the modern text editors. I use it occasionally.

(use-package neotree
  :ensure t
  :bind* (("M-m SPC n". neotree-toggle))
  :init
  (setq neo-smart-open t))
  • Modal binding
    (modalka-define-kbd "SPC n" "C-c n")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "SPC n" "directory tree")
    

Tags based navigation

GNU global is a code tagging system and I use it to navigate the code base. It can use different backends, including ctags.

(use-package ggtags
  :ensure t
  :diminish ggtags-mode
  :bind* (("M-m T"   . ggtags-find-tag-regexp)
          ("M-m g t" . ggtags-create-tags)
          ("M-m g T" . ggtags-update-tags))
  :init
  (setq-local imenu-create-index-function #'ggtags-build-imenu-index)
  :config
  (add-hook 'prog-mode-hook 'ggtags-mode))
  • Modal binding
    (modalka-define-kbd "T"   "M-m T")
    (modalka-define-kbd "g t" "M-m g t")
    (modalka-define-kbd "g T" "M-m g T")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g t" "create tags"
      "g T" "update tags"
      "T"   "global tags search")
    

Dumb jump

Dumb jump is a simple package that uses the grep or ag to jump to the source of the symbol at point. This is the fallback when language specific navigation is not possible

(use-package dumb-jump
  :ensure t
  :bind (("C-c S" . dumb-jump-go))
  :config
  (dumb-jump-mode))
  • Modal binding
    (modalka-define-kbd "S" "C-c S")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "S" "src at point")
    

Perspective

Perspective creates different view ports in Emacs preserving the Window configuration. Super useful.

(use-package perspective
  :ensure t
  :bind* (("M-m SPC p" . persp-switch)
          ("M-m SPC P" . persp-kill)
          ("M-m SPC A" . persp-switch-to-buffer)
          ("M-m g r"   . persp-rename))
  :config
  (persp-mode 1))
  • Modal binding
    (modalka-define-kbd "SPC p" "M-m SPC p")
    (modalka-define-kbd "SPC P" "M-m SPC P")
    (modalka-define-kbd "SPC A" "M-m SPC A")
    (modalka-define-kbd "g r"   "M-m g r")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "SPC p" "perspective switch"
      "SPC P" "perspective kill"
      "SPC A" "perspective buffer switch"
      "g r"   "perspective rename")
    

Toggle zoom

I generally have multiple windows open and might need to zoom into one every now and then. I also might split them and change them. I would like to think of this package as smart zoom where I zoom into a window, split further, and have winner handle the history and just zoom right back out and continue working on the previous configuration. It also indicates if you are zoomed in or not via the status line color.

(use-package zoom-window
  :ensure t
  :bind* (("M-m Z" . zoom-window-zoom)))
  • Modal binding
    (modalka-define-kbd "Z" "M-m Z")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "Z"   "zoom window")
    

Code documentation

Dash is a nice little app that stores documents offline for reference. Let’s bring that to Emacs.

(use-package dash-at-point
  :ensure t
  :bind (("C-c I" . dash-at-point))
  :bind* (("M-m SPC i" . dash-at-point-with-docset)
          ("M-m SPC I" . dash-at-point)))
  • Modal binding
    (modalka-define-kbd "SPC i" "M-m SPC i")
    (modalka-define-kbd "SPC I" "M-m SPC I")
    (modalka-define-kbd "I"     "C-c I")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "I"     "info at point"
      "SPC i" "documentation prompt"
      "SPC I" "documentation at point")
    

Custom functions

Go to the closest number

Go to the closest numeral that is present in the file or change the number at point.

(defun sk/goto-closest-number ()
  (interactive)
  (let ((closest-behind (save-excursion (search-backward-regexp "[0-9]" nil t)))
        (closest-ahead (save-excursion (search-forward-regexp "[0-9]" nil t))))
    (push-mark)
    (goto-char
     (cond
      ((and (not closest-ahead) (not closest-behind)) (error "No numbers in buffer"))
      ((and closest-ahead (not closest-behind)) closest-ahead)
      ((and closest-behind (not closest-ahead)) closest-behind)
      ((> (- closest-ahead (point)) (- (point) closest-behind)) closest-behind)
      ((> (- (point) closest-behind) (- closest-ahead (point))) closest-ahead)
      :else closest-ahead))))
  • Key binding
    (bind-keys*
      ("M-m %" . sk/goto-closest-number))
    
    • Modal binding
      (modalka-define-kbd "%" "M-m %")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "%" "goto closest number")
      

Open the config.org file

This function opens the current file when invoked from anywhere so that I can edit my configuration quickly, whenever I want

(defun sk/open-config ()
  "Opens the configuration file from anywhere"
  (interactive)
  (find-file (concat user-emacs-directory "config.org")))
  • Key binding
    (bind-keys*
      ("M-m SPC v" . sk/open-config))
    
    • Modal binding
      (modalka-define-kbd "SPC v" "M-m SPC v")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "SPC v" "view configuration file")
      

Flyspell go to the previous spell error

Flyspell mode offers only go to the next error by default. This is a custom function borrowed from Stack Overflow (I think) to do the opposite.

;; Flyspell previous error
(defun sk/flyspell-goto-previous-error (arg)
  "Go to arg previous spelling error."
  (interactive "p")
  (while (not (= 0 arg))
    (let ((pos (point))
          (min (point-min)))
      (if (and (eq (current-buffer) flyspell-old-buffer-error)
               (eq pos flyspell-old-pos-error))
          (progn
            (if (= flyspell-old-pos-error min)

                ;; goto beginning of buffer
                (progn
                  (message "Restarting from end of buffer")
                  (goto-char (point-max)))
              (backward-word 1))
            (setq pos (point))))

      ;; seek the next error
      (while (and (> pos min)
                  (let ((ovs (overlays-at pos))
                        (r '()))
                    (while (and (not r) (consp ovs))
                      (if (flyspell-overlay-p (car ovs))
                          (setq r t)
                        (setq ovs (cdr ovs))))
                    (not r)))
        (backward-word 1)
        (setq pos (point)))

      ;; save the current location for next invocation
      (setq arg (1- arg))
      (setq flyspell-old-pos-error pos)
      (setq flyspell-old-buffer-error (current-buffer))
      (goto-char pos)
      (if (= pos min)
          (progn
            (message "No more miss-spelled word!")
            (setq arg 0))
        (forward-word)))))
  • Key binding
    (bind-keys*
      ("M-m [ s" . sk/flyspell-goto-previous-error))
    
    • Modal binding
      (modalka-define-kbd "[ s" "M-m [ s")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "[ s" "previous spell error")
      

Non native full screen

As mentioned here, this is to further ensure that we use a non-native fullscreen.

(defun sk/toggle-frame-fullscreen-non-native ()
  "Toggle full screen non-natively. Uses the `fullboth' frame paramerter
   rather than `fullscreen'. Useful to fullscreen on OSX w/o animations."
  (interactive)
  (modify-frame-parameters
   nil
   `((maximized
      . ,(unless (memq (frame-parameter nil 'fullscreen) '(fullscreen fullboth))
           (frame-parameter nil 'fullscreen)))
     (fullscreen
      . ,(if (memq (frame-parameter nil 'fullscreen) '(fullscreen fullboth))
             (if (eq (frame-parameter nil 'maximized) 'maximized)
                 'maximized)
           'fullboth)))))
  • Key binding
    (bind-keys*
      ("M-m SPC z" . sk/toggle-frame-fullscreen-non-native))
    
    • Modal binding
      (modalka-define-kbd "SPC z" "M-m SPC z")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "SPC z" "toggle fullscreen")
      

Split window and move

I hate the default Emacs behavior of split windows which just splits the window but doesn’t go there.

(defun sk/split-below-and-move ()
  (interactive)
  (split-window-below)
  (other-window 1))
(defun sk/split-right-and-move ()
  (interactive)
  (split-window-right)
  (other-window 1))
  • Key binding

    This is one of the few occasions I just replace it with my custom function because I use it so much. Since the defaults of modalka and which-key already use and define this binding, there is no need for another one.

    (bind-keys
      ("C-x 2" . sk/split-below-and-move)
      ("C-x 3" . sk/split-right-and-move))
    

Turn the adjoining PDF (only with 2 windows)

(defun sk/other-pdf-next ()
  "Turns the next page in adjoining PDF file"
  (interactive)
  (other-window 1)
  (doc-view-next-page)
  (other-window 1))
(defun sk/other-pdf-previous ()
  "Turns the previous page in adjoining PDF file"
  (interactive)
  (other-window 1)
  (doc-view-previous-page)
  (other-window 1))
  • Key binding
    (bind-keys*
      ("M-m ] d" . sk/other-pdf-next)
      ("M-m [ d" . sk/other-pdf-previous))
    
    • Modal binding
      (modalka-define-kbd "] d" "M-m ] d")
      (modalka-define-kbd "[ d" "M-m [ d")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "] d" "adjacent pdf next page"
        "[ d" "adjacent pdf prev page")
      

Turn the adjoining window (only with 2 windows)

(defun sk/other-window-down ()
  "Scrolls down in adjoining window"
  (interactive)
  (other-window 1)
  (scroll-up-command)
  (other-window 1))
(defun sk/other-window-up ()
  "Scrolls up in adjoining window"
  (interactive)
  (other-window 1)
  (scroll-down-command)
  (other-window 1))
  • Key binding
    (bind-keys*
      ("M-m g ]" . sk/other-window-down)
      ("M-m g [" . sk/other-window-up))
    
    • Modal binding
      (modalka-define-kbd "g ]" "M-m g ]")
      (modalka-define-kbd "g [" "M-m g [")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g ]" "adjacent pdf next page"
        "g [" "adjacent pdf prev page")
      

Smarter start of line

This function, from emacsredux blog, defines a better start of line and remaps C-a for it.

(defun sk/smarter-move-beginning-of-line (arg)
  "Move point back to indentation of beginning of line.
Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.
If ARG is not nil or 1, move forward ARG - 1 lines first.  If
point reaches the beginning or end of the buffer, stop there."
  (interactive "^p")
  (setq arg (or arg 1))
  ;; Move lines first
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))
  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))
  • Key binding
    ;; remap C-a to `smarter-move-beginning-of-line'
    (global-set-key [remap move-beginning-of-line]
                    'sk/smarter-move-beginning-of-line)
    

Rotate the windows

Stolen from Magnar Sveen’s configuration. This function is bound to the hydra defined for window navigation.

(defun sk/rotate-windows ()
  "Rotate your windows"
  (interactive)
  (cond ((not (> (count-windows)1))
         (message "You can't rotate a single window!"))
        (t
         (setq i 1)
         (setq numWindows (count-windows))
         (while  (< i numWindows)
           (let* (
                  (w1 (elt (window-list) i))
                  (w2 (elt (window-list) (+ (% i numWindows) 1)))

                  (b1 (window-buffer w1))
                  (b2 (window-buffer w2))

                  (s1 (window-start w1))
                  (s2 (window-start w2))
                  )
             (set-window-buffer w1  b2)
             (set-window-buffer w2 b1)
             (set-window-start w1 s2)
             (set-window-start w2 s1)
             (setq i (1+ i)))))))

Open the current HTML file in browser

Not sure where I got this from. Most likely Magnar Sveen.

(defun sk/browse-current-file ()
  "Open the current file as a URL using `browse-url'."
  (interactive)
  (let ((file-name (buffer-file-name)))
    (if (and (fboundp 'tramp-tramp-file-p)
             (tramp-tramp-file-p file-name))
        (error "Cannot open tramp file")
      (browse-url (concat "file://" file-name)))))
  • Key binding
    (bind-keys*
      ("M-m g B" . sk/browse-current-file))
    
    • Modal binding
      (modalka-define-kbd "g B" "M-m g B")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g B" "browse file in browser")
      

Hydras

As described previously, hydras are amazing sticky bindings and these are some that I use tons of time a day.

Window navigation

(defhydra sk/hydra-of-windows (:color red
                               :hint nil)
  "
 ^Move^    ^Size^    ^Change^                    ^Split^           ^Text^
 ^^^^^^^^^^^------------------------------------------------------------------
 ^ ^ _k_ ^ ^   ^ ^ _K_ ^ ^   _u_: winner-undo _o_: rotate  _v_: vertical     _+_: zoom in
 _h_ ^+^ _l_   _H_ ^+^ _L_   _r_: winner-redo            _s_: horizontal   _-_: zoom out
 ^ ^ _j_ ^ ^   ^ ^ _J_ ^ ^   _c_: close                  _z_: zoom         _q_: quit
"
  ("h" windmove-left)
  ("j" windmove-down)
  ("k" windmove-up)
  ("l" windmove-right)
  ("H" shrink-window-horizontally)
  ("K" shrink-window)
  ("J" enlarge-window)
  ("L" enlarge-window-horizontally)
  ("v" sk/split-right-and-move)
  ("s" sk/split-below-and-move)
  ("c" delete-window)
  ("f" sk/toggle-frame-fullscreen-non-native :color blue)
  ("o" sk/rotate-windows)
  ("z" delete-other-windows)
  ("u" (progn
         (winner-undo)
         (setq this-command 'winner-undo)))
  ("r" winner-redo)
  ("+" text-scale-increase)
  ("-" text-scale-decrease)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m SPC u" . sk/hydra-of-windows/body))
    
    • Modal binding
      (modalka-define-kbd "SPC u" "M-m SPC u")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "SPC u" "window menu")
      

Bookmark navigation

(defhydra sk/hydra-bookmarks (:color blue
                              :hint nil)
  "
 _s_: set  _b_: bookmark   _j_: jump   _d_: delete   _q_: quit
  "
  ("s" bookmark-set)
  ("b" bookmark-save)
  ("j" bookmark-jump)
  ("d" bookmark-delete)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m `" . sk/hydra-bookmarks/body))
    
    • Modal binding
      (modalka-define-kbd "`" "M-m `")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "`" "bookmark menu")
      

Editing

This section lists all the custom functions and packages that help during any generic text editing.

Expand region

This is one of those packages I cannot imagine being without. Quickly expand semantically or directly to text/code with precision and change/delete/replace them.

Some functions/regions have complimenting expansions - you can expand inside the brackets or around the brackets. The small case letters after pressing the prefix i will select the inner blocks most of the time while the upper case letters after pressing prefix i will select the complete blocks. To do this, we need to define a few functions.

(defun sk/mark-inside-org-code ()
  "Select inside an Org code block without the org specific syntax"
  (interactive)
  (er/mark-org-code-block)
  (next-line 1)
  (exchange-point-and-mark)
  (previous-line 1)
  (end-of-line 1))

(defun sk/mark-around-LaTeX-environment ()
  "Select around a LaTeX environment with both the begin and end keywords"
  (interactive)
  (er/mark-LaTeX-inside-environment)
  (previous-line 1)
  (exchange-point-and-mark)
  (next-line 1)
  (end-of-line 1))

(defun sk/mark-around-word ()
  "Mark the word and the adjacent whitespace"
  (interactive)
  (er/mark-word)
  (exchange-point-and-mark)
  (forward-char 1))

(defun sk/mark-around-text-paragraph ()
  "Mark the paragraph and the newline"
  (interactive)
  (er/mark-text-paragraph)
  (exchange-point-and-mark)
  (next-line 1))

(defun sk/mark-inside-LaTeX-math ()
  "Mark inside the latex math"
  (interactive)
  (er/mark-LaTeX-math)
  (forward-char 1)
  (exchange-point-and-mark)
  (backward-char 1))

(defun sk/mark-inside-python-block ()
  "Mark inside a python block"
  (interactive)
  (er/mark-python-block)
  (next-line 1))

(defun sk/mark-inside-ruby-block ()
  "Mark inside a ruby/julia block"
  (interactive)
  (er/mark-ruby-block-up)
  (next-line 1)
  (exchange-point-and-mark)
  (previous-line 1))

(defun sk/mark-around-symbol ()
  "Mark around a symbol including the nearby whitespace"
  (interactive)
  (er/mark-symbol)
  (exchange-point-and-mark)
  (forward-char 1))

We also add some helpful additional expand regions depending on the mode.

(defun er/add-org-mode-expansions ()
  (make-variable-buffer-local 'er/try-expand-list)
  (setq er/try-expand-list (append
                            er/try-expand-list
                            '(mark-LaTeX-math))))
(add-hook 'org-mode-hook 'er/add-org-mode-expansions)
(use-package expand-region
  :ensure t
  :bind* (("M-m a o" . er/mark-org-code-block)
          ("M-m a w" . sk/mark-around-word)
          ("M-m a p" . sk/mark-around-text-paragraph)
          ("M-m a f" . er/mark-defun)
          ("M-m a e" . sk/mark-around-LaTeX-environment)
          ("M-m a t" . er/mark-LaTeX-math)
          ("M-m a m" . er/mark-python-block)
          ("M-m a j" . er/mark-ruby-block-up)
          ("M-m a q" . er/mark-outside-quotes)
          ("M-m a b" . er/mark-outside-pairs)
          ("M-m a u" . er/mark-url)
          ("M-m a c" . er/mark-comment)
          ("M-m a v" . sk/mark-around-symbol)
          ("M-m i p" . er/mark-text-paragraph)
          ("M-m i f" . er/mark-defun)
          ("M-m i w" . er/mark-word)
          ("M-m i e" . er/mark-LaTeX-inside-environment)
          ("M-m i t" . sk/mark-inside-LaTeX-math)
          ("M-m i u" . er/mark-url)
          ("M-m i c" . er/mark-comment)
          ("M-m i b" . er/mark-inside-pairs)
          ("M-m i q" . er/mark-inside-quotes)
          ("M-m i o" . sk/mark-inside-org-code)
          ("M-m i m" . sk/mark-inside-python-block)
          ("M-m i j" . sk/mark-inside-ruby-block)
          ("M-m i v" . er/mark-symbol)))
  • Modal binding
    (modalka-define-kbd "a o" "M-m a o")
    (modalka-define-kbd "a w" "M-m a w")
    (modalka-define-kbd "a p" "M-m a p")
    (modalka-define-kbd "a f" "M-m a f")
    (modalka-define-kbd "a e" "M-m a e")
    (modalka-define-kbd "a t" "M-m a t")
    (modalka-define-kbd "a m" "M-m a m")
    (modalka-define-kbd "a j" "M-m a j")
    (modalka-define-kbd "a q" "M-m a q")
    (modalka-define-kbd "a b" "M-m a b")
    (modalka-define-kbd "a u" "M-m a u")
    (modalka-define-kbd "a c" "M-m a c")
    (modalka-define-kbd "a v" "M-m a v")
    (modalka-define-kbd "i p" "M-m i p")
    (modalka-define-kbd "i f" "M-m i f")
    (modalka-define-kbd "i w" "M-m i w")
    (modalka-define-kbd "i u" "M-m i u")
    (modalka-define-kbd "i t" "M-m i t")
    (modalka-define-kbd "i e" "M-m i e")
    (modalka-define-kbd "i c" "M-m i c")
    (modalka-define-kbd "i b" "M-m i b")
    (modalka-define-kbd "i q" "M-m i q")
    (modalka-define-kbd "i o" "M-m i o")
    (modalka-define-kbd "i m" "M-m i m")
    (modalka-define-kbd "i j" "M-m i j")
    (modalka-define-kbd "i v" "M-m i v")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "a"   "expand around prefix"
      "a o" "expand an org block"
      "a w" "expand a word"
      "a p" "expand a para"
      "a f" "expand a func"
      "a e" "expand a latex env"
      "a t" "expand a latex math"
      "a m" "expand a python block"
      "a j" "expand a julia block"
      "a q" "expand a quote"
      "a b" "expand a pair"
      "a u" "expand a url"
      "a c" "expand a comment"
      "a v" "expand a variable"
      "i" "expand inside prefix"
      "i p" "expand in para"
      "i f" "expand in func"
      "i w" "expand in word"
      "i u" "expand in url"
      "i e" "expand in latex env"
      "i t" "expand in latex math"
      "i c" "expand in comment"
      "i b" "expand in pair"
      "i q" "expand in quote"
      "i o" "expand in org code"
      "i m" "expand in python block"
      "i j" "expand in julia block"
      "i v" "expand in symbol")
    

Commenting

comment-dwim-2 improves on the existing comment-dwim command for easy commenting. Pretty useful.

(use-package comment-dwim-2
  :ensure t
  :bind* (("M-m g c" . comment-dwim-2)))
  • Modal binding
    (modalka-define-kbd "g c" "M-m g c")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g c" "comment line/region")
    

Smartparens

This package aims to be the one-stop solution for semantic language in any language. It is a little hard to wrap your head around though. I mostly use it for its wrapping and unwrapping features more than semantic navigation. A caveat here is that, when trying to wrap a region with either parens/quotes/whatever, you need to exit OUT of modalka state to the Emacs state.

(use-package smartparens
  :ensure t
  :demand t
  :bind* (("M-m m j" . sp-down-sexp)
          ("M-m m k" . sp-backward-up-sexp)
          ("M-m m h" . sp-backward-down-sexp)
          ("M-m m l" . sp-up-sexp)
          ("M-m m f" . sp-forward-sexp)
          ("M-m m b" . sp-backward-sexp)
          ("M-m m a" . sp-beginning-of-sexp)
          ("M-m m e" . sp-end-of-sexp)
          ("M-m m n" . sp-next-sexp)
          ("M-m m p" . sp-previous-sexp)
          ("M-m m >" . sp-forward-barf-sexp)
          ("M-m m <" . sp-backward-barf-sexp)
          ("M-m m )" . sp-forward-slurp-sexp)
          ("M-m m (" . sp-backward-slurp-sexp)
          ("M-m m x" . sp-transpose-sexp)
          ("M-m m d" . sp-kill-sexp)
          ("M-m m y" . sp-copy-sexp)
          ("M-m m u" . sp-unwrap-sexp)
          ("M-m m U" . sp-backward-unwrap-sexp)
          ("M-m m C" . sp-convolute-sexp)
          ("M-m m r" . sp-raise-sexp)
          ("M-m m s" . sp-split-sexp)
          ("M-m m S" . sp-splice-sexp)
          ("M-m m F" . sp-splice-sexp-killing-forward)
          ("M-m m B" . sp-splice-sexp-killing-backward)
          ("M-m m A" . sp-splice-sexp-killing-around))
  :diminish smartparens-mode
  :diminish smartparens-strict-mode
  :config
  (require 'smartparens-config)
  (smartparens-global-mode)
  (smartparens-global-strict-mode)
  (show-smartparens-global-mode)
  (which-key-add-key-based-replacements
    "M-m m" "move prefix"))
  • Modal binding
    (modalka-define-kbd "m j" "M-m m j")
    (modalka-define-kbd "m k" "M-m m k")
    (modalka-define-kbd "m h" "M-m m h")
    (modalka-define-kbd "m l" "M-m m l")
    (modalka-define-kbd "m f" "M-m m f")
    (modalka-define-kbd "m b" "M-m m b")
    (modalka-define-kbd "m a" "M-m m a")
    (modalka-define-kbd "m e" "M-m m e")
    (modalka-define-kbd "m n" "M-m m n")
    (modalka-define-kbd "m p" "M-m m p")
    (modalka-define-kbd "m >" "M-m m >")
    (modalka-define-kbd "m <" "M-m m <")
    (modalka-define-kbd "m )" "M-m m )")
    (modalka-define-kbd "m (" "M-m m (")
    (modalka-define-kbd "m x" "M-m m x")
    (modalka-define-kbd "m d" "M-m m d")
    (modalka-define-kbd "m y" "M-m m y")
    (modalka-define-kbd "m u" "M-m m u")
    (modalka-define-kbd "m U" "M-m m U")
    (modalka-define-kbd "m U" "M-m m U")
    (modalka-define-kbd "m U" "M-m m U")
    (modalka-define-kbd "m C" "M-m m C")
    (modalka-define-kbd "m r" "M-m m r")
    (modalka-define-kbd "m s" "M-m m s")
    (modalka-define-kbd "m S" "M-m m S")
    (modalka-define-kbd "m F" "M-m m F")
    (modalka-define-kbd "m B" "M-m m B")
    (modalka-define-kbd "m A" "M-m m A")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "m" "move prefix"
      "m j" "move down"
      "m k" "move backward up"
      "m h" "move backward down"
      "m l" "move up"
      "m f" "move forward"
      "m b" "move backward"
      "m a" "move beginning"
      "m e" "move end"
      "m n" "move next"
      "m p" "move previous"
      "m >" "expression forward barf"
      "m <" "expression backward barf"
      "m )" "expression forward slurp"
      "m (" "expression backward slurp"
      "m x" "smart transpose"
      "m d" "smart delete"
      "m y" "smart copy"
      "m u" "selection unwrap"
      "m U" "backward unwrap"
      "m C" "convolute sexp"
      "m r" "raise sexp"
      "m s" "split sexp"
      "m S" "splice sexp"
      "m F" "splice forward"
      "m B" "splice backward"
      "m A" "splice around")
    

Interactive edit

Iedit-mode is the bomb. Quick, fast edits of every symbol selected. Although Multiple cursors has some more features, this is the best choice for quick renaming of variables/words.

(use-package iedit
  :ensure t
  :commands (iedit-mode)
  :bind* (("M-m *" . iedit-mode)))
  • Modal binding
    (modalka-define-kbd "*" "M-m *")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "*" "multi edit all")
    

Multiple cursors

Multiple cursors is an amazing, but admittedly quirky, package that needs some setting up. Also, this conflicts with Modalka mode and when having multiple cursors, it’s best to stay in Emacs state. Since I use this for quick edits, I’m fine with this. Also, Region bindings mode acts as a glue between this and its corresponding modal binding

(use-package multiple-cursors
  :ensure t
  :bind* (("M-m ." . mc/edit-lines)
          ("M-m >" . mc/mark-next-like-this)
          ("M-m ," . mc/skip-to-next-like-this)
          ("M-m <" . mc/mark-previous-like-this)))

Region bindings mode

This is also kind of a modal mode. This activates only when a region is active. I use it exclusively for multiple cursors.

(use-package region-bindings-mode
  :ensure t
  :demand t
  :bind (:map region-bindings-mode-map
              ("<" . mc/mark-previous-like-this)
              ("," . mc/skip-to-next-like-this)
              (">" . mc/mark-next-like-this)
              ("." . mc/edit-lines))
  :diminish (region-bindings-mode . "ρ")
  :config
  (progn
    (add-hook 'after-init-hook 'region-bindings-mode-enable)))
  • Modal binding
    (modalka-define-kbd "." "M-m .")
    (modalka-define-kbd ">" "M-m >")
    (modalka-define-kbd "," "M-m ,")
    (modalka-define-kbd "<" "M-m <")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      ">" "multi cursor next"
      "," "multi cursor skip"
      "<" "multi cursor prev"
      "." "multi edit lines")
    

Shrink white space

This package helps to reduce the number of blank lines/whitespace between words easily. Useful when deleting chunks of text and just want to make it neat.

(use-package shrink-whitespace
  :ensure t
  :bind* (("M-m g SPC" . shrink-whitespace)))
  • Modal binding
    (modalka-define-kbd "g SPC" "M-m g SPC")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g SPC" "shrink space")
    

Visual replace

This is the good old search and replace as opposed to the fancy alternatives such as iedit. You search for a word in the buffer/region, type in the replacement and confirm each one by pressing y or n or just press ! to apply this to everything.

(use-package visual-regexp
  :ensure t
  :commands (vr/query-replace)
  :bind* (("M-m SPC SPC" . vr/query-replace))
  :config
  (use-package visual-regexp-steroids
    :ensure t
    :commands (vr/select-query-replace)))
  • Modal binding
    (modalka-define-kbd "SPC SPC" "M-m SPC SPC")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "SPC SPC" "replace word/expression")
    

Snippets

Yasnippets gives you the snippets functionality. It also comes bundled with a lot of pre-configured snippets and is extensible via Emacs Lisp. This following code also includes auto-yasnippet to create temporary snippets and save them later if need be. It acts as a good compliment to the Yasnippets package. Also, this is the only package where many of the bindings have no equivalent in modal mode because it makes no sense. Just press the “trigger” (by visiting the snippet file using C-<escape>) and “TAB” to expand and jump. If you don’t use snippets all that much, then seeing the list of all snippets by pressing “S” in modal mode should be good enough. Furthermore, I have set C-o to list all snippets in Emacs state but this maybe changed by mode specific keybindings.

(use-package yasnippet
  :ensure t
  :commands (yas-insert-snippet yas-new-snippet)
  :bind (("C-o" . yas-insert-snippet))
  :bind* (("C-="        . yas-new-snippet)
          ("C-<escape>" . yas-visit-snippet-file))
  :diminish (yas-minor-mode . "γ")
  :config
  (setq yas/triggers-in-field t); Enable nested triggering of snippets
  (setq yas-prompt-functions '(yas-completing-prompt))
  (add-hook 'snippet-mode-hook '(lambda () (setq-local require-final-newline nil)))
  (yas-global-mode))

Also, I don’t want to trigger snippets when I’m in shell. So, let’s turn it off.

(defun sk/force-yasnippet-off ()
  (yas-minor-mode -1)
  (setq yas-dont-activate t))
(add-hook 'term-mode-hook 'sk/force-yasnippet-off)
(add-hook 'shell-mode-hook 'sk/force-yasnippet-off)

Cycle quotes

This package helps to cycle quotes when in strings.

(use-package cycle-quotes
  :ensure t
  :bind* (("M-m s q" . cycle-quotes)))
  • Modal binding
    (modalka-define-kbd "s q" "M-m s q")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "s q" "switch quotes")
    

Custom functions

Toggle case

This is from somewhere on the internet. It toggles the case of the word or selection.

(defun sk/toggle-letter-case ()
  "Toggle the letter case of current word or text selection.
Toggles from 3 cases: UPPER CASE, lower case, Title Case,
in that cyclic order."
  (interactive)
  (let (pos1 pos2 (deactivate-mark nil) (case-fold-search nil))
    (if (and transient-mark-mode mark-active)
        (setq pos1 (region-beginning)
              pos2 (region-end))
      (setq pos1 (car (bounds-of-thing-at-point 'word))
            pos2 (cdr (bounds-of-thing-at-point 'word))))

    (when (not (eq last-command this-command))
      (save-excursion
        (goto-char pos1)
        (cond
         ((looking-at "[[:lower:]][[:lower:]]") (put this-command 'state
                                                     "all lower"))
         ((looking-at "[[:upper:]][[:upper:]]") (put this-command 'state
                                                     "all caps") )
         ((looking-at "[[:upper:]][[:lower:]]") (put this-command 'state
                                                     "init caps") )
         (t (put this-command 'state "all lower") ))))

    (cond
     ((string= "all lower" (get this-command 'state))
      (upcase-initials-region pos1 pos2) (put this-command 'state "init caps"))
     ((string= "init caps" (get this-command 'state))
      (upcase-region pos1 pos2) (put this-command 'state "all caps"))
     ((string= "all caps" (get this-command 'state))
      (downcase-region pos1 pos2) (put this-command 'state "all lower")))))
  • Key binding
    (bind-keys*
      ("M-m ~" . sk/toggle-letter-case))
    
    • Modal binding
      (modalka-define-kbd "~" "M-m ~")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
         "~" "toggle case")
      

Copy region on line

This function copies the current line or the region if the region is active.

(defun sk/copy-whole-lines (arg)
  "Copy lines (as many as prefix argument) in the kill ring"
  (interactive "p")
  (kill-ring-save (line-beginning-position)
                  (line-beginning-position (+ 1 arg)))
  (message "%d line%s copied" arg (if (= 1 arg) "" "s")))

(defun sk/copy-region-or-line (arg)
  (interactive "p")
  (if (region-active-p)
      (kill-ring-save (region-beginning) (region-end))
    (sk/copy-whole-lines arg)))
  • Key binding
    (bind-keys*
      ("M-w" . sk/copy-region-or-line))
    

Kill region or backward word

This function kills the region if active and kills the backward word if it is not.

(defun sk/kill-region-or-backward-word (arg)
  (interactive "p")
  (if (region-active-p)
      (kill-region (region-beginning) (region-end))
    (backward-kill-word arg)))
  • Key binding
    (bind-keys*
      ("C-w" . sk/kill-region-or-backward-word))
    

Change region/till end of line

Being almost always in Modalka mode implies I have to toggle it after deleting a region if I want to overwrite it. These functions, similar to the Vi style commands, let me delete and start typing in one swoop.

(defun sk/change-region ()
  "delete and put me in Emacs state in one swoop"
  (interactive)
  (if (region-active-p)
      (let ((beg (region-beginning))
            (end (region-end)))
        (kill-region beg end)))
  (modalka-mode 0))

(defun sk/change-rest-of-line ()
  "delete till the end of line and put me in Emacs state in one swoop"
  (interactive)
  (kill-line)
  (modalka-mode 0))
  • Key binding
    (bind-keys*
      ("M-m c" . sk/change-region)
      ("M-m C" . sk/change-rest-of-line))
    
    • Modal binding
      (modalka-define-kbd "c" "M-m c")
      (modalka-define-kbd "C" "M-m C")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "c" "change region"
        "C" "change rest of line")
      

Increase or decrease number at point

Once you go to the closest number, you might want to change it. These functions are useful for that.

(defun sk/incs (s &optional num)
  (let* ((inc (or num 1))
         (new-number (number-to-string (+ inc (string-to-number s))))
         (zero-padded? (s-starts-with? "0" s)))
    (if zero-padded?
        (s-pad-left (length s) "0" new-number)
      new-number)))

(defun sk/change-number-at-point (arg)
  (interactive "p")
  (unless (or (looking-at "[0-9]")
              (looking-back "[0-9]"))
    (sk/goto-closest-number))
  (save-excursion
    (while (looking-back "[0-9]")
      (forward-char -1))
    (re-search-forward "[0-9]+" nil)
    (replace-match (sk/incs (match-string 0) arg) nil nil)))

(defun sk/subtract-number-at-point (arg)
  (interactive "p")
  (sk/change-number-at-point (- arg)))
  • Key binding
    (bind-keys*
      ("M-m g +" . sk/change-number-at-point)
      ("M-m g -" . sk/subtract-number-at-point))
    
    • Modal binding
      (modalka-define-kbd "g +" "M-m g +")
      (modalka-define-kbd "g -" "M-m g -")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g +" "increase number"
        "g -" "decrease number")
      

Deactivate region

By default there is no way to deactivate mark/region once you start selecting some region without pressing keyboard-quit. If we press this during a macro, it would stop the macro. So, I prefer to have a function that deactivates mark instead.

(defun sk/remove-mark ()
  "Deactivate the region"
  (interactive)
  (if (region-active-p)
      (deactivate-mark)))
  • Key binding
    (bind-keys*
      ("M-m E" . sk/remove-mark))
    
    • Modal binding
      (modalka-define-kbd "E" "M-m E")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "E" "deactivate mark")
      

Align blocks

Emacs has the built-in command align-regexp to align via regular expression. However, it is not very straightforward, especially since I don’t know Emacs regular expressions. Hence, let’s use some wrapper functions

(defun sk/align-whitespace (start end)
  "Align columns by whitespace"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\)\\s-" 1 0 t))

(defun sk/align-ampersand (start end)
  "Align columns by ampersand"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\)&" 1 1 t))

(defun sk/align-quote-space (start end)
  "Align columns by quote and space"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\).*\\s-\"" 1 0 t))

(defun sk/align-equals (start end)
  "Align columns by equals sign"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\)=" 1 0 t))

(defun sk/align-comma (start end)
  "Align columns by comma"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\)," 1 1 t))

(defun sk/align-dot (start end)
  "Align columns by dot"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\)\\\." 1 1 t))

(defun sk/align-colon (start end)
  "Align columns by equals sign"
  (interactive "r")
  (align-regexp start end
                "\\(\\s-*\\):" 1 0 t))
  • Key binding
    (bind-keys*
      ("M-m g A SPC" . sk/align-whitespace)
      ("M-m g A &"   . sk/align-ampersand)
      ("M-m g A ,"   . sk/align-comma)
      ("M-m g A \""  . sk/align-quote-space)
      ("M-m g A      ." . sk/align-dot)
      ("M-m g A ="   . sk/align-equals)
      ("M-m g A :"   . sk/align-colon)
      ("M-m g A A"   . align-regexp))
    

    Which key binding explanation

    (which-key-add-key-based-replacements
      "M-m g A" "align prefix")
    
    • Modal binding
      (modalka-define-kbd "g A SPC" "M-m g A SPC")
      (modalka-define-kbd "g A \""  "M-m g A \"")
      (modalka-define-kbd "g A &"   "M-m g A &")
      (modalka-define-kbd "g A ,"   "M-m g A ,")
      (modalka-define-kbd "g A ."   "M-m g A .")
      (modalka-define-kbd "g A ="   "M-m g A =")
      (modalka-define-kbd "g A :"   "M-m g A :")
      (modalka-define-kbd "g A A"   "M-m g A A")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g A"     "align prefix"
        "g A SPC" "align based on spaces"
        "g A &"   "align based on &"
        "g A ,"   "align based on ,"
        "g A \""  "align based on \""
        "g A ."   "align based on ."
        "g A ="   "align based on ="
        "g A :"   "align based on :"
        "g A A"   "align based on regex")
      

Insert date or date and time

This function is to easily insert date or datetime in the current buffer.

(defun sk/insert-date (prefix)
  "Insert the current date. With prefix-argument, write out the day and month name."
  (interactive "P")
  (let ((format (cond
                 ((not prefix) "%Y-%m-%d")
                 ((equal prefix '(4)) "%A, %d %B %Y")
                 ((equal prefix '(16)) "%Y-%m-%d %H:%M:%S"))))
    (insert (format-time-string format))))
  • Key binding
    (bind-keys*
      ("M-m g D" . sk/insert-date))
    
    • Modal binding
      (modalka-define-kbd "g D" "M-m g D")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g D"   "insert date")
      

Rename the current buffer and the file associated with it

Stolen from Magnar Sveen’s config.

(defun sk/rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " filename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))
  • Key binding
    (bind-keys*
      ("M-m g R" . sk/rename-current-buffer-file))
    
    • Modal binding
      (modalka-define-kbd "g R" "M-m g R")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g R" "rename buffer and file")
      

Delete the current buffer and the file associated with it

Stolen from Magnar Sveen’s config.

(defun sk/delete-current-buffer-file ()
  "Removes file connected to current buffer and kills buffer."
  (interactive)
  (let ((filename (buffer-file-name))
        (buffer (current-buffer))
        (name (buffer-name)))
    (if (not (and filename (file-exists-p filename)))
        (ido-kill-buffer)
      (when (yes-or-no-p "Are you sure you want to remove this file? ")
        (delete-file filename)
        (kill-buffer buffer)
        (message "File '%s' successfully removed" filename)))))
  • Key binding
    (bind-keys*
      ("M-m g K" . sk/delete-current-buffer-file))
    
    • Modal binding
      (modalka-define-kbd "g K" "M-m g K")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g K" "delete buffer and file")
      

Copy the current file path

Get the file path of the current file.

(defun sk/copy-current-file-path ()
  "Add current file path to kill ring. Limits the filename to project root if possible."
  (interactive)
  (kill-new buffer-file-name))
  • Key binding
    (bind-keys*
      ("M-m g y" . sk/copy-current-file-path))
    
    • Modal binding
      (modalka-define-kbd "g y" "M-m g y")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g y" "copy current file path")
      

Transposing words

Transpose is a built-in functionality to exchange words/characters/functions. I just build wrapper functions around it to make it exchange it the way I want it to.

;; Transpose words forward
(defun sk/transpose-words-forward ()
  "Transpose words forward"
  (interactive)
  (forward-word 1)
  (forward-char 1)
  (transpose-words 1)
  (backward-word 1))
;; Transpose words backward
(defun sk/transpose-words-backward ()
  "Transpose words backward"
  (interactive)
  (transpose-words 1)
  (backward-word 1))
  • Key binding
    (bind-keys*
      ("M-m [ w" . sk/transpose-words-backward)
      ("M-m ] w" . sk/transpose-words-forward))
    
    • Modal binding
      (modalka-define-kbd "[ w" "M-m [ w")
      (modalka-define-kbd "] w" "M-m ] w")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "[ w" "exchange with prev word"
        "] w" "exchange with next word")
      

Transposing characters

Though this is used much less that transposing words, it is still useful.

;; Transpose chars forward
(defun sk/transpose-chars-forward ()
  "Transpose chars forward"
  (interactive)
  (forward-char 1)
  (transpose-chars 1)
  (backward-char 1))
;; Transpose chars backward
(defun sk/transpose-chars-backward ()
  "Transpose chars backward"
  (interactive)
  (transpose-chars 1)
  (backward-char 1))
  • Key binding
    (bind-keys*
      ("M-m [ c" . sk/transpose-chars-backward)
      ("M-m ] c" . sk/transpose-chars-forward))
    
    • Modal binding
      (modalka-define-kbd "[ c" "M-m [ c")
      (modalka-define-kbd "] c" "M-m ] c")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "[ c" "exchange with prev char"
        "] c" "exchange with next char")
      

Copy to the end of the line

We already have delete to the end of line (pressing C-k). It is only natural there has to be copy too.

(defun sk/copy-to-end-of-line ()
  (interactive)
  (kill-ring-save (point)
                  (line-end-position))
  (message "Copied to end of line"))
  • Keybinding
    (bind-keys*
      ("M-m Y" . sk/copy-to-end-of-line))
    
    • Modal binding
      (modalka-define-kbd "Y" "M-m Y")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "Y" "copy till end of line")
      

Duplicate line or region

Duplicate the selected region or line.

(defun sk/duplicate-region (&optional num start end)
  "Duplicates the region bounded by START and END NUM times.
If no START and END is provided, the current region-beginning and
region-end is used."
  (interactive "p")
  (save-excursion
    (let* ((start (or start (region-beginning)))
           (end (or end (region-end)))
           (region (buffer-substring start end)))
      (goto-char end)
      (dotimes (i num)
        (insert region)))))

(defun sk/duplicate-current-line (&optional num)
  "Duplicate the current line NUM times."
  (interactive "p")
  (save-excursion
    (when (eq (point-at-eol) (point-max))
      (goto-char (point-max))
      (newline)
      (forward-char -1))
    (sk/duplicate-region num (point-at-bol) (1+ (point-at-eol)))))

(defun sk/duplicate-line-or-region (&optional num)
  "Duplicate the current line or region if active"
  (interactive "p")
  (if (region-active-p)
      (let ((beg (region-beginning))
            (end (region-end)))
        (sk/duplicate-region num beg end)))
  (sk/duplicate-current-line num))
  • Key binding
    (bind-keys*
      ("M-m g d" . sk/duplicate-line-or-region))
    
    • Modal binding
      (modalka-define-kbd "g d" "M-m g d")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g d" "duplicate line or region")
      

Open line

The built-in command for open-line (triggered by C-o) is weird. I prefer Vi style opening of lines. This creates a new line either above or below accordingly and puts you there. I open lines above more than below and hence, prefer that to be a single-key binding. I feel that this kind of opening lines is sensible enough that it has to be the default (like window splitting).

(defun sk/open-line-above (args)
  "Insert a new line above the current one or open a new line above for editing"
  (interactive "P")
  (if (equal args '(4))
      (save-excursion
        (unless (bolp)
          (beginning-of-line))
        (newline)
        (indent-according-to-mode))
    (unless (bolp)
      (beginning-of-line))
    (newline)
    (forward-line -1)
    (indent-according-to-mode)
    (modalka-mode 0)))
  • Key binding
    (bind-keys*
      ("M-o" . sk/open-line-above))
    
    • Modal binding
      (modalka-define-kbd "O" "M-o")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "O" "open line above")
      

Join line

I use electric-newline-and-maybe-indent as a substitute for splitting lines. Of course, I would want something to join them.

(defun sk/join-line ()
  "Join the current line with the next line"
  (interactive)
  (next-line)
  (delete-indentation))
  • Key binding

    I bind this to C-S-j since C-j is the key to split a line.

    (bind-keys
      ("C-S-j" . sk/join-line))
    
    • Modal binding
      (modalka-define-kbd "g J" "C-S-j")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g J" "join line")
      

Select the current line

Expand region is pretty good but sometimes, I want to select the current line and this function takes care of that. Also note that this depends on smarter start of line to select the line. Also, sometimes we need to select the current line with the newline character and for this we define another function which uses this function.

(defun sk/select-inside-line ()
  "Select the current line"
  (interactive)
  (sk/smarter-move-beginning-of-line 1)
  (set-mark (line-end-position))
  (exchange-point-and-mark))

(defun sk/select-around-line ()
  "Select line including the newline character"
  (interactive)
  (sk/select-inside-line)
  (next-line 1)
  (sk/smarter-move-beginning-of-line 1))
  • Key binding
    (bind-keys*
      ("M-m i l" . sk/select-inside-line)
      ("M-m a l" . sk/select-around-line))
    
    • Modal binding
      (modalka-define-kbd "i l" "M-m i l")
      (modalka-define-kbd "a l" "M-m a l")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "i l" "select inside line"
        "a l" "select around line")
      

Correct those annoying DOuble capitals

I am not great at typing and I make plenty of mistakes. I especially hate that double characters. I found this on Stack overflow and just had to put it in my config. First, a function to detect and correct it.

(defun sk/dcaps-to-scaps ()
  "Convert word in DOuble CApitals to Single Capitals."
  (interactive)
  (and (= ?w (char-syntax (char-before)))
       (save-excursion
         (and (if (called-interactively-p)
                  (skip-syntax-backward "w")
                (= -3 (skip-syntax-backward "w")))
              (let (case-fold-search)
                (looking-at "\\b[[:upper:]]\\{2\\}[[:lower:]]"))
              (capitalize-word 1)))))

Then, let’s define a minor mode for it to be activated.

(define-minor-mode sk/dubcaps-mode
  "Toggle `sk/dubcaps-mode'.  Converts words in DOuble CApitals to
Single Capitals as you type."
  :init-value nil
  :lighter (" DC")
  (if sk/dubcaps-mode
      (add-hook 'post-self-insert-hook #'sk/dcaps-to-scaps nil 'local)
    (remove-hook 'post-self-insert-hook #'sk/dcaps-to-scaps 'local)))

Finally, let’s add a hook so that it is on for all the text files Emacs opens.

(add-hook 'text-mode-hook #'sk/dubcaps-mode)

Also, since we add a minor mode string (it might be useful sometimes), currently I prefer to diminish it.

(defun sk/diminish-dubcaps ()
  (interactive)
  (diminish 'sk/dubcaps-mode ""))
(add-hook 'sk/dubcaps-mode-hook 'sk/diminish-dubcaps)

Move lines

I adapted this from the Emacs wiki entry about the same thing. This will move a line up or down and also a region if the region is selected. While selecting a region, make sure you select the newline character for best results. First to define the core function that makes this happen.

(defun sk/move-text-internal (arg)
  (cond
   ((and mark-active transient-mark-mode)
    (if (> (point) (mark))
        (exchange-point-and-mark))
    (let ((column (current-column))
          (text (delete-and-extract-region (point) (mark))))
      (forward-line arg)
      (move-to-column column t)
      (set-mark (point))
      (insert text)
      (exchange-point-and-mark)
      (setq deactivate-mark nil)))
   (t
    (let ((column (current-column)))
      (beginning-of-line)
      (when (or (> arg 0) (not (bobp)))
        (forward-line)
        (when (or (< arg 0) (not (eobp)))
          (transpose-lines arg)
          (when (and (eval-when-compile
                       '(and (>= emacs-major-version 24)
                             (>= emacs-minor-version 3)))
                     (< arg 0))
            (forward-line -1)))
        (forward-line -1))
      (move-to-column column t)))))

Now, let’s define wrapper functions which we bind

(defun sk/move-text-down (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines down."
  (interactive "*p")
  (sk/move-text-internal arg))
(defun sk/move-text-up (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines up."
  (interactive "*p")
  (sk/move-text-internal (- arg)))
  • Key binding
    (bind-keys*
      ("M-m [ e" . sk/move-text-up)
      ("M-m ] e" . sk/move-text-down))
    
    • Modal binding
      (modalka-define-kbd "[ e" "M-m [ e")
      (modalka-define-kbd "] e" "M-m ] e")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "[ e" "move text up"
        "] e" "move text down")
      

Change from snake case to camel case

snake_case and camelCase are common ways to write variable names and this function helps to convert one to the other. Stolen from Magnar Sveen’s configuration.

(defun sk/replace-next-underscore-with-camel (arg)
  (interactive "p")
  (if (> arg 0)
 (setq arg (1+ arg))) ; 1-based index to get eternal loop with 0
  (let ((case-fold-search nil))
    (while (not (= arg 1))
      (search-forward-regexp "\\b_[a-z]")
      (forward-char -2)
      (delete-char 1)
      (capitalize-word 1)
      (setq arg (1- arg)))))
  • Key binding
    (bind-keys*
      ("M-m g C" . sk/replace-next-underscore-with-camel))
    
    • Modal binding
      (modalka-define-kbd "g C" "M-m g C")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g C" "camelCase word")
      

Make current word snake case

Works just like the previous case. Note that this depends on Expand region. Also, Magnar’s config.

(defun sk/snakeify-current-word ()
  (interactive)
  (er/mark-word)
  (let* ((beg (region-beginning))
         (end (region-end))
         (current-word (buffer-substring-no-properties beg end))
         (snakified (snake-case current-word)))
    (replace-string current-word snakified nil beg end)))

(defun sk/kebab-current-word ()
  (interactive)
  (er/mark-word)
  (let* ((beg (region-beginning))
         (end (region-end))
         (current-word (buffer-substring-no-properties beg end))
         (kebabed (s-dashed-words current-word)))
    (replace-string current-word kebabed nil beg end)))
  • Key binding
    (bind-keys*
      ("M-m g _" . sk/snakeify-current-word))
    
    • Modal binding
      (modalka-define-kbd "g _" "M-m g _")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g _" "snake_case word")
      

Transpose paranthesis

This function was something I missed when I was using Vim with the vim-surround plugin. This helps to change the kind of paranthesis. Toggle between pairs of “()”, “[]” and “{}”. First let’s put a couple of functions that this depends on.

(defun move-forward-out-of-param ()
  (while (not (looking-at ")\\|, \\| ?}\\| ?\\]"))
    (cond
     ((point-is-in-string-p) (move-point-forward-out-of-string))
     ((looking-at "(\\|{\\|\\[") (forward-list))
     (t (forward-char)))))

(defun move-backward-out-of-param ()
  (while (not (looking-back "(\\|, \\|{ ?\\|\\[ ?"))
    (cond
     ((point-is-in-string-p) (move-point-backward-out-of-string))
     ((looking-back ")\\|}\\|\\]") (backward-list))
     (t (backward-char)))))
(defun transpose-params ()
  "Presumes that params are in the form (p, p, p) or {p, p, p} or [p, p, p]"
  (interactive)
  (let* ((end-of-first (cond
                        ((looking-at ", ") (point))
                        ((and (looking-back ",") (looking-at " ")) (- (point) 1))
                        ((looking-back ", ") (- (point) 2))
                        (t (error "Place point between params to transpose."))))
         (start-of-first (save-excursion
                           (goto-char end-of-first)
                           (move-backward-out-of-param)
                           (point)))
         (start-of-last (+ end-of-first 2))
         (end-of-last (save-excursion
                        (goto-char start-of-last)
                        (move-forward-out-of-param)
                        (point))))
    (transpose-regions start-of-first end-of-first start-of-last end-of-last)))
  • Key binding
    (bind-keys*
      ("M-m s c" . transpose-params))
    
    • Modal binding
      (modalka-define-kbd "s c" "M-m s c")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s c" "change surroundings")
      

Add for auto correction

This function was taken from endless paranthesis post on auto-correction. Note that to use this, you obviously have to have flyspell mode enabled in the buffer.

(defun sk/simple-get-word ()
  (car-safe (save-excursion (ispell-get-word nil))))
(defun sk/ispell-word-then-abbrev (p)
  "Call `ispell-word', then create an abbrev for it.
With prefix P, create local abbrev. Otherwise it will
be global.
If there's nothing wrong with the word at point, keep
looking for a typo until the beginning of buffer. You can
skip typos you don't want to fix with `SPC', and you can
abort completely with `C-g'."
  (interactive "P")
  (let (bef aft)
    (save-excursion
      (while (if (setq bef (sk/simple-get-word))
                 ;; Word was corrected or used quit.
                 (if (ispell-word nil 'quiet)
                     nil ; End the loop.
                   ;; Also end if we reach `bob'.
                   (not (bobp)))
               ;; If there's no word at point, keep looking
               ;; until `bob'.
               (not (bobp)))
        (backward-word)
        (backward-char))
      (setq aft (sk/simple-get-word)))
    (if (and aft bef (not (equal aft bef)))
        (let ((aft (downcase aft))
              (bef (downcase bef)))
          (define-abbrev
            (if p local-abbrev-table global-abbrev-table)
            bef aft)
          (message "\"%s\" now expands to \"%s\" %sally"
                   bef aft (if p "loc" "glob")))
      (user-error "No typo at or before point"))))
  • Key binding
    (bind-keys*
      ("M-m g =" . sk/ispell-word-then-abbrev))
    
    • Modal binding
      (modalka-define-kbd "g =" "M-m g =")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g =" "autocorrect word")
      

Disable modalka mode when overwriting

I want to use overwrite-mode now and then and this is a small optimization to disable modalka mode when in overwrite mode and enable it again when not overwriting.

(defun sk/modalka-toggle-on-overwrite ()
  "Toggle modalka-mode on overwrite-mode."
  (if (bound-and-true-p overwrite-mode)
      (modalka-mode 0)
    (modalka-mode 1)))
(add-hook 'overwrite-mode-hook 'sk/modalka-toggle-on-overwrite)

Hydras

Expand region hydra

(defhydra sk/hydra-expand (:pre (er/mark-word)
                           :color red
                           :hint nil)
  "
 _a_: add    _r_: reduce   _q_: quit
 "
  ("a" er/expand-region)
  ("r" er/contract-region)
  ("q" nil :color blue))
  • Key bindings
    (bind-keys*
      ("M-m a a" . sk/hydra-expand/body))
    
    • Modal binding
      (modalka-define-kbd "a a" "M-m a a")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "a a" "expand region hydra")
      

Multiple cursors hydra

(defhydra sk/hydra-multiple-cursors (:color red
                                     :hint nil)
  "
  _k_: prev         _j_: next         _a_: all     _b_: beg of lines   _q_: quit
  _K_: skip prev    _J_: skip next    _d_: defun   _e_: end of lines
  _p_: unmark prev  _n_: unmark next  _r_: regexp  _l_: lines
"
  ("j" mc/mark-next-like-this)
  ("J" mc/skip-to-next-like-this)
  ("n" mc/unmark-next-like-this)
  ("k" mc/mark-previous-like-this)
  ("K" mc/skip-to-previous-like-this)
  ("p" mc/unmark-previous-like-this)
  ("a" mc/mark-all-like-this :color blue)
  ("d" mc/mark-all-like-this-in-defun :color blue)
  ("r" mc/mark-all-in-region-regexp :color blue)
  ("b" mc/edit-beginnings-of-lines)
  ("e" mc/edit-ends-of-lines)
  ("l" mc/edit-lines :color blue)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("C-t" . sk/hydra-multiple-cursors/body))
    

Rectangle edit mode

Rectangle edit mode is one of the unique features of Emacs (and Vim) and this hydra makes it easier to interact with it.

(defhydra sk/hydra-rectangle (:pre (rectangle-mark-mode 1)
                              :color pink
                              :hint nil)
  "
 _p_: paste   _r_: replace  _I_: insert
 _y_: copy    _o_: open     _V_: reset
 _d_: kill    _n_: number   _q_: quit
"
  ("h" backward-char nil)
  ("l" forward-char nil)
  ("k" previous-line nil)
  ("j" next-line nil)
  ("y" copy-rectangle-as-kill)
  ("d" kill-rectangle)
  ("x" clear-rectangle)
  ("o" open-rectangle)
  ("p" yank-rectangle)
  ("r" string-rectangle)
  ("n" rectangle-number-lines)
  ("I" string-insert-rectangle)
  ("V" (if (region-active-p)
           (deactivate-mark)
         (rectangle-mark-mode 1)) nil)
  ("q" keyboard-quit :color blue))
  • Key binding
    (bind-keys*
      ("M-m V" . sk/hydra-rectangle/body))
    
    • Modal binding
      (modalka-define-kbd "V" "M-m V")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "V" "edit rectangle region")
      

Macros

Macros are awesome too! It’s a little complicated to wrap your head around first but I’m very used to it from my Vim days and cannot live without it.

(defhydra sk/hydra-of-macros (:color pink
                              :hint nil)
  "
 _m_: macro  _L_: lossage  _v_: view      _n_: forward    _D_: delete   _q_: quit
 _M_: prev   _E_: edit     _r_: register  _p_: backward   _K_: key
  "
  ("m" kmacro-call-macro)
  ("M" kmacro-call-ring-2nd)
  ("L" kmacro-edit-lossage :color blue)
  ("E" kmacro-edit-macro :color blue)
  ("v" kmacro-view-macro :color blue)
  ("r" kmacro-to-register :color blue)
  ("n" kmacro-cycle-ring-next)
  ("p" kmacro-cycle-ring-previous)
  ("D" kmacro-delete-ring-head :color blue)
  ("K" kmacro-bind-to-key :color blue)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m @" . sk/hydra-of-macros/body))
    
    • Modal binding
      (modalka-define-kbd "@" "M-m @")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "@" "macros menu")
      

Registers

Registers are one of my under-used features but it’s still nice to have around though.

(defhydra sk/hydra-registers (:color blue
                              :hint nil)
  "
 _a_: append     _c_: copy-to    _j_: jump       _r_: rectangle-copy   _q_: quit
 _i_: insert     _n_: number-to  _f_: frameset   _w_: window-config
 _+_: increment  _p_: point-to
  "
  ("a" append-to-register)
  ("c" copy-to-register)
  ("i" insert-register)
  ("f" frameset-to-register)
  ("j" jump-to-register)
  ("n" number-to-register)
  ("r" copy-rectangle-to-register)
  ("w" window-configuration-to-register)
  ("+" increment-register)
  ("p" point-to-register)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
     ("M-m \"" . sk/hydra-registers/body))
    
    • Modal binding
      (modalka-define-kbd "\"" "M-m \"")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "\"" "registers")
      

Visual

This section adds some packages which enhance visual feedback and mostly work behind the scenes to get stuff done.

Volatile highlights

I particularly like this package. It gives visual feedback on some of the common operations like undo, copying and pasting and also inherits the color scheme very well.

(use-package volatile-highlights
  :ensure t
  :demand t
  :diminish volatile-highlights-mode
  :config
  (volatile-highlights-mode t))

Highlight thing at point

This package highlights the thing at point. That’s it. This can be nice to use when combined with Highlight symbol package.

(use-package highlight-thing
  :ensure t
  :diminish highlight-thing-mode
  :bind* (("M-m g *" . highlight-thing-mode)))
  • Modal binding
    (modalka-define-kbd "g *" "M-m g *")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g *" "highlight symbol")
    

Column enforce mode

Just like the previous package, this one is also subtle. It highlights characters that exceed a particular column margin. Very useful while coding.

(use-package column-enforce-mode
  :ensure t
  :diminish column-enforce-mode
  :init
  (setq column-enforce-column 99)
  :config
  (progn
    (add-hook 'prog-mode-hook 'column-enforce-mode)))

Highlight indentation

Languages like Python and rarely even huge functions in C/C++ are indented and it’s hard to judge it’s scope. That’s when this package becomes particularly useful. I never leave this on. I always turn it on and the off pretty soon.

(use-package highlight-indentation
  :ensure t
  :commands (highlight-indentation-mode))

Fill column indicator

This package is similar to Column enforce mode but adds a line as a margin instead of being subtle. I make sure my code has a soft limit of 80 characters per line and a hard limit of 100 characters per line. Therefore I enable this for 80 characters and column enforce mode for 100.

(use-package fill-column-indicator
  :ensure t
  :commands (fci-mode)
  :init
  (setq fci-rule-width 5
        fci-rule-column 79))

White space butler

The improvement this package provides over the built-in delete-trailing-whitespace is that this deletes whitespace from changes that only I made.

(use-package ws-butler
  :ensure t
  :diminish ws-butler-mode
  :config
  (ws-butler-global-mode))

Region state

This is a fairly simple package that provides information about the active region.

(use-package region-state
  :ensure t
  :config
  (region-state-mode))

Color themes

Of course we need color themes. I like the sanityinc tomorrow color themes. They have night, dark, eighties and blue version, all of which are nice. That’s the only one I’m including.

(use-package color-theme-sanityinc-tomorrow
  :ensure t
  :config
  (progn
    (load-theme 'sanityinc-tomorrow-day t)))

Mode line

Spaceline is similar to the Spacemacs mode-line. I like it. It’s pretty cool.

(use-package spaceline
  :ensure t
  :demand t
  :init
  (setq powerline-default-separator 'arrow-fade)
  :config
  (require 'spaceline-config)
  (spaceline-spacemacs-theme)
  (spaceline-helm-mode))

Fancy battery status

I’m on Emacs full screen most of the day and battery status would sure be helpful. While, Emacs has a built-in display-battery-mode but why not go fancy.

(use-package fancy-battery
  :ensure t
  :init
  (setq fancy-battery-show-percentage t)
  :config
  (fancy-battery-mode))

Origami - folding based on indentation/syntax

Emacs has a built-in hide-show mode but it isn’t great. Origami improves it a bit.

(use-package origami
  :ensure t
  :commands (origami-toggle-node)
  :bind* (("M-m -" . orgiami-toggle-node)))
  • Modal binding
    (modalka-define-kbd "-" "M-m -")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
        "-" "syntax-based fold toggle")
    

Vimish fold - Fold regions based on selection

Syntax based folding is great and all but sometimes I need to fold some random piece of text and Vimish fold is good for that.

(use-package vimish-fold
  :ensure t
  :commands (vimish-fold-toggle
             vimish-fold))

Hydras

Activate minor modes

There are many minor modes that I don’t need to be active all the time. And this hydra activates them on my command.

(defhydra sk/hydra-of-activate (:color blue
                                :hint nil)
  "
 _b_: battery   _n_: number       _v_: wrap        _c_: column    _i_: indent        _k_: which-key   _l_: talk       _q_: quit
 _t_: time      _w_: weather      _y_: yasnippet   _m_: margin    _s_: smartparens   _o_: org extras  _j_: jabber
 _f_: flyspell  _a_: auto-comp    _d_: fold        _g_: ggtags    _p_: paradox       _e_: error       _h_: html emmet
"
  ("b" fancy-battery-mode :color red)
  ("t" display-time-mode :color red)
  ("n" linum-mode :color red)
  ("w" wttrin)
  ("f" flyspell-mode)
  ("v" visual-line-mode)
  ("p" list-packages)
  ("c" column-enforce-mode)
  ("y" yas-global-mode)
  ("a" company-mode)
  ("i" highlight-indentation-mode)
  ("m" fci-mode :color red)
  ("j" jabber-connect :color red)
  ("l" jabber-chat-with)
  ("o" sk/org-custom-load :color blue)
  ("g" ggtags-mode)
  ("d" global-origami-mode)
  ("k" which-key-mode)
  ("s" smartparens-strict-mode)
  ("h" emmet-mode)
  ("e" global-flycheck-mode)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m g a" . sk/hydra-of-activate/body))
    
    • Modal binding
      (modalka-define-kbd "g a" "M-m g a")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g a" "global activate modes")
      

Vimish fold

Vimish fold has all these fancy features and I use it much more than Origami. Let’s make a hydra for that.

(defhydra sk/hydra-vimish-fold (:color red
                                :hint nil)
  "
 _f_: fold  _u_: unfold  _r_: refold  _t_: toggle  _d_: delete    _n_: next      _q_: quit
          _U_: Unfold  _R_: Refold  _T_: Toggle  _D_: Delete    _p_: previous
  "
  ("f" vimish-fold)
  ("u" vimish-fold-unfold)
  ("r" vimish-fold-refold)
  ("t" vimish-fold-toggle)
  ("d" vimish-fold-delete)
  ("U" vimish-fold-unfold-all)
  ("R" vimish-fold-refold-all)
  ("T" vimish-fold-toggle-all)
  ("D" vimish-fold-delete-all)
  ("n" vimish-fold-next-fold)
  ("p" vimish-fold-previous-fold)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m _" . vimish-fold-toggle)
      ("M-m |" . sk/hydra-vimish-fold/body))
    
    • Modal binding
      (modalka-define-kbd "_" "M-m _")
      (modalka-define-kbd "|" "M-m |")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "_" "sel-based fold toggle"
        "|" "sel-based fold menu")
      

Writing

Markdown and Pandoc support

Although I use Org for most of my markup documentation and it has an export function to markdown, these packages give better support. So, why not install them.

(use-package markdown-mode
  :ensure t
  :mode ("\\.markdown\\'" "\\.mkd\\'" "\\.md\\'")
  :config
  (use-package pandoc-mode
    :ensure t
    :mode ("\\.markdown\\'" "\\.mkd\\'" "\\.md\\'")))

LaTeX support

I use LaTeX extensively compared to Markdown (although Org is still preferred for shorter reports). A chunk of good stuff is taken from this blog post.

(use-package tex-site
  :ensure auctex
  :ensure auctex-latexmk
  :ensure latex-preview-pane
  :diminish reftex-mode
  :mode (("\\.tex\\'" . LaTeX-mode)
         ("\\.xtx\\'" . LaTeX-mode))
  :init
  (setq reftex-default-bibliography '("~/Dropbox/references/references.bib"))
  (setq TeX-auto-save t)
  (setq TeX-parse-self t)
  (setq-default TeX-master nil)
  (add-hook 'LaTeX-mode-hook 'visual-line-mode)
  (add-hook 'LaTeX-mode-hook 'flyspell-mode)
  (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  (setq reftex-plug-into-AUCTeX t)
  (setq TeX-PDF-mode t)
  :config
  ;; Use Skim as viewer, enable source <-> PDF sync
  ;; make latexmk available via C-c C-c
  ;; Note: SyncTeX is setup via ~/.latexmkrc (see below)
  (add-hook 'LaTeX-mode-hook (lambda ()
                               (push
                                '("latexmk" "latexmk -xelatex -pdf %s" TeX-run-TeX nil t
                                  :help "Run latexmk on file")
                                TeX-command-list)))
  (add-hook 'TeX-mode-hook '(lambda () (setq TeX-command-default "latexmk")))

  ;; use Skim as default pdf viewer
  ;; Skim's displayline is used for forward search (from .tex to .pdf)
  ;; option -b highlights the current line; option -g opens Skim in the background
  (setq TeX-view-program-selection '((output-pdf "PDF Viewer")))
  (setq TeX-view-program-list
        '(("PDF Viewer" "/Applications/Skim.app/Contents/SharedSupport/displayline -b -g %n %o %b"))))

(defun sk/diminish-reftex ()
  (interactive)
  (diminish 'reftex-mode ""))
(add-hook 'auctex-mode-hook 'sk/diminish-reftex)
(add-hook 'latex-mode-hook 'sk/diminish-reftex)
(add-hook 'reftex-mode-hook 'sk/diminish-reftex)

Pick out passive voice and weasel words

Write good.

(use-package writegood-mode
  :ensure t
  :diminish writegood-mode
  :config
  (progn
    (add-hook 'text-mode-hook 'writegood-mode)))

Custom functions

Hard and Soft line wrap

I usually just use the built-in visual line mode which acts like a soft-wrap that is seen in most modern prose editors. However, there are situations where a hard line break is preferable, like in LaTeX. To do that, we make use of the built-in function fill-paragraph. This function, taken from EmacsWiki, does the opposite.

(defun sk/unfill-paragraph (&optional region)
      "Takes a multi-line paragraph and makes it into a single line of text."
      (interactive (progn (barf-if-buffer-read-only) '(t)))
      (let ((fill-column (point-max))
            ;; This would override `fill-column' if it's an integer.
            (emacs-lisp-docstring-fill-column t))
        (fill-paragraph nil region)))
  • Key binding
    (bind-keys*
      ("M-m g Q" . sk/unfill-paragraph))
    
    • Modal binding
      (modalka-define-kbd "g Q" "M-m g Q")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "g Q" "unfill paragraph")
      

Org

Org mode is the reason I switched from Vim to Emacs. It is an amazing piece of software that helps in note-taking, organizing, developing this Emacs starter file, writing reports for my grad school. It has single-handedly replaced at least 3 separate applications for me. Without further ado, let’s start the Org configuration. I’ll try to be as elaborate as possible. Org mode can be used to write books and even learn Mandarin.

Basic settings

Default directory

Org mode operates from a directory. You need a directory to store all your org files and be default. Typically, you can use Org without setting these but it is useful to set this location to help in capturing and archiving org files effectively. I use Dropbox to sync all my notes with the Orgzly app on my phone.

(setq org-directory "~/Dropbox/org")

Markup

Org mode has its own markup syntax but seeing the emphasis markers is distracting. I prefer to hide it.

(setq org-hide-emphasis-markers t)

Minimal outline

If you have read how headlines are written in org, you can notice that it is by the number of “*” before it. To make it look better, let’s indent every heading and remove all the “*” but the last one.

(setq org-startup-indented t
      org-hide-leading-stars t)

Images

The GUI Emacs has the ability to display images. But if the image is pretty large, it displays the whole thing. Let’s restrict it from doing that.

(setq org-image-actual-width '(300))

Source code

Org mode has the ability to syntax highlight source code blocks that are embedded. Let’s explicitly make sure the options we need there.

(setq org-src-fontify-natively t
      org-src-tab-acts-natively t)

Exporting

Org has a powerful exporting feature. Let’s select the various formats to export and also mention how exactly we need it to export to LaTeX with syntax highlighting. I have also taken a good looking CSS configuration from Zhitao Gong and I use it for exporting by putting it in the same folder as my org file and adding #+HTML_HEAD: <link rel="stylesheet" type="text/css" href="org.css"/> to the top of my org file.

(setq org-export-with-smart-quotes t
      org-export-backends '(beamer html latex md))

Syntax highlighting in LaTeX exports

This comes from the oh-my-emacs configuration. Note that this needs the pygments package. So let’s install that first. Note that uncommenting the line - (add-to-list 'org-latex-packages-alist '("" "minted")) will result in Org using that package for every LaTeX conversion and the org-toggle-latex-element might get affected in the process.

pip3 install --user pygments
;; code snippet comes from
;; http://joat-programmer.blogspot.com/2013/07/org-mode-version-8-and-pdf-export-with.html
;; Include the latex-exporter
;; check whether org-mode 8.x is available
;; (require 'ox-latex)
;; Add minted to the defaults packages to include when exporting.
;; (add-to-list 'org-latex-packages-alist '("" "minted"))
;; Tell the latex export to use the minted package for source
;; code coloration.
(setq org-latex-listings 'minted)
;; Let the exporter use the -shell-escape option to let latex
;; execute external programs.
;; This obviously and can be dangerous to activate!
(setq org-latex-minted-options
      '(("mathescape" "true")
        ("linenos" "true")
        ("numbersep" "5pt")
        ("frame" "lines")
        ("framesep" "2mm")))
(setq org-latex-pdf-process
      '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Tagging

Org mode has a tagging system that is very useful to organize the contents and notes. You can select the tags with a single letter (the letter after “?” in the following code).

(setq org-tag-alist (quote (("article"   . ?a) ;; temporary
                            ("books"     . ?b)
                            ("courses"   . ?c) ;; temporary
                            ("code"      . ?C)
                            ("card"      . ?d)
                            ("drill"     . ?D)
                            ("errands"   . ?e)
                            ("films"     . ?f)
                            ("gubby"     . ?g)
                            ("home"      . ?h)
                            ("idea"      . ?i)
                            ("job"       . ?j)
                            ("ledger"    . ?l)
                            ("meeting"   . ?m)
                            ("note"      . ?n)
                            ("online"    . ?o)
                            ("personal"  . ?p)
                            ("project"   . ?P)
                            ("reference" . ?r) ;; temporary
                            ("reveal"    . ?R)
                            ("story"     . ?s)
                            ("technical" . ?t)
                            ("vague"     . ?v)
                            ("work"      . ?w)
                            ("noexport"  . ?x)
                            ("cash"      . ?$))))

Task management

Just like tags, Org mode has a built-in way to help manage tasks.

(setq org-todo-keywords
      '((sequence "TODO(t)" "IN-PROGRESS(i)" "|" "DONE(d!)")
        (sequence "WAITING(w@/!)" "|" "CANCELED(c@)")))

Agenda

The agenda view is an amazing way to search your org files for tags, TODOs, keywords, and even view deadlines. There are a couple of things to configure in the agenda view but first let’s tell Org which files agenda should look in.

(setq org-agenda-files (list
                        "~/Dropbox/org/blog.org"
                        "~/Dropbox/org/errands.org"
                        "~/Dropbox/org/phd.org"
                        "~/Dropbox/org/references/articles.org"
                        "~/Dropbox/org/ledger.org"
                        "~/Dropbox/org/notes.org"
                        "~/Dropbox/org/fun.org"))

Then, a smaller part of org is the way it shows the deadlines of all the TODOs and show them in a view that spans a fortnight. It can be set it weekly, monthly or daily too.

(setq org-deadline-warning-days 7
      org-agenda-span 'fortnight
      org-agenda-skip-scheduled-if-deadline-is-shown t)

Capture

Capture is an amazing tool. It was the final push to Org and I use it many times per day. First, we have to setup the capture templates. Templates is not that vast and can be easily understood by going through the manual.

(setq org-capture-templates '(

        ;; For code snippets
        ("a"               ; key
         "Algo/Code"       ; name
         entry             ; type
         (file+headline "~/Dropbox/org/notes.org" "Code")  ; target
         "* %^{TITLE} %(org-set-tags)  :code:\n:PROPERTIES:\n:Created: %U\n:END:\n%i\#+BEGIN_SRC %^{language}\n%?\n\#END_SRC"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For taking notes on random things
        ("n"               ; key
         "Note"            ; name
         entry             ; type
         (file+headline "~/Dropbox/org/notes.org" "Notes")  ; target
         "* %? %(org-set-tags)  :note:\n:PROPERTIES:\n:Created: %U\n:Linked: %A\n:END:\n%i"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; Ledger is a CLI accounting system
        ("l"               ; key
         "Ledger"          ; name
         entry             ; type
         (file+datetree "~/Dropbox/org/ledger.org" "Ledger")  ; target
         "* %^{expense} %(org-set-tags)  :accounts:\n:PROPERTIES:\n:Created: %U\n:END:\n%i
#+NAME: %\\1-%t
\#+BEGIN_SRC ledger :noweb yes
%^{Date of expense (yyyy/mm/dd)} %^{'*' if cleared, else blank} %\\1
    %^{Account name}                                $%^{Amount}
    %?
\#+END_SRC
"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For notes or something regarding more work
        ("w"               ; key
         "Work"            ; name
         entry             ; type
         (file+headline "~/Dropbox/org/phd.org" "Work")  ; target
         "* TODO %^{Todo} %(org-set-tags)  :work:\n:PROPERTIES:\n:Created: %U\n:END:\n%i\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For capturing some things that are worth reading
        ("r"               ; key
         "Reading"         ; name
         entry             ; type
         (file+headline "~/Dropbox/org/fun.org" "Reading")  ; target
         "* %^{Title} %(org-set-tags)\n:PROPERTIES:\n:Created: %U\n:END:\n%i\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For capturing minutes of the meeting
        ("m"               ; key
         "Meeting"         ; name
         entry             ; type
         (file+datetree "~/Dropbox/org/phd.org" "Meeting")  ; target
         "* %^{Title} %(org-set-tags)  :meeting:\n:PROPERTIES:\n:Created: %U\n:END:\n%i\n** Agenda:\n%?\n\n** Minutes of the meeting:\n"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; To practice for my driving test
        ("d"               ; key
         "Drill driving"   ; name
         entry             ; type
         (file+headline "~/Dropbox/org/drill.org" "Driving")  ; target
         "* Question  :drill:driving:\n%^{Question}\n** Answer\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For taking notes of math/stats stuff that I keep forgetting
        ("s"              ; key
         "Drill math"     ; name
         entry            ; type
         (file+headline "~/Dropbox/org/drill.org" "Stats/Math")  ; target
         "* Question  :drill:stats:math:\n%^{Question}\n** Answer\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For capturing some physics concepts that I need to remember
        ("p"              ; key
         "Drill physics"  ; name
         entry            ; type
         (file+headline "~/Dropbox/org/drill.org" "Physics")  ; target
         "* Question  :drill:physics:\n%^{Question}\n** Answer\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; For capturing details of a job application/details
        ("j"                      ; key
         "Jobs"                   ; name
         table-line               ; type
         (file+headline "~/Dropbox/org/notes.org" "Jobs")  ; target
         "| %u | %^{Company} | [[%^{job link}][%^{position}]] | %^{referrals?} | %^{Experience?} | %^t | %^{Status} | %^{Follow up} | %^{Result} |"  ; template
         :prepend t               ; properties
         ;; :table-line-pos "II-3"   ; properties
         :empty-lines 1           ; properties
         :created t               ; properties
         :kill-buffer t)          ; properties

        ;; To capture movies that I plan to see
        ("f"              ; key
         "films"          ; name
         entry            ; type
         (file+headline "~/Dropbox/org/fun.org" "Movies")  ; target
         "* %^{Movie} %(org-set-tags)  :film:\n:PROPERTIES:\n:Created: %U\n:END:\n%i
Netflix?: %^{netflix? Yes/No}\nGenre: %^{genre}\nDescription:\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; To capture ideas for my blog
        ("b"               ; key
         "Blog"            ; name
         entry             ; type
         (file+headline "~/Dropbox/org/blog.org" "Blog")  ; target
         "* %^{Title} %(org-set-tags)  :blog:\n:PROPERTIES:\n:Created: %U\n:END:\n%i\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; To capture tons of errands
        ("e"               ; key
         "Errands"         ; name
         entry             ; type
         (file+headline "~/Dropbox/org/errands.org" "Errands")  ; target
         "* TODO %^{Todo} %(org-set-tags)  :errands:\n:PROPERTIES:\n:Created: %U\n:END:\n%i\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t)   ; properties

        ;; To capture things regarding my course
        ("c"               ; key
         "Courses"         ; name
         entry             ; type
         (file+headline "~/Dropbox/org/phd.org" "Courses")  ; target
         "* %^{Course} %(org-set-tags)  :courses:\n:PROPERTIES:\n:Created: %U\n:END:\n%i\n%?"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t))) ; properties

Then, we have to choose where to refile and archive too.

(setq org-refile-targets '((nil :maxlevel . 9)
                           (org-agenda-files :maxlevel . 9)))
(setq org-refile-use-outline-path t
      org-outline-path-complete-in-steps nil)

Setup org

Finally, we make sure we bind some keys and add some easy template expansion.

(use-package org
  :ensure org
  :bind* (("M-m o a"   . org-agenda)
          ("M-m o c"   . org-capture)
          ("M-m o i"   . org-insert-link)
          ("M-m o s"   . org-store-link)
          ("M-m o S"   . org-list-make-subtree)
          ("M-m o A"   . org-archive-subtree)
          ("M-m o g"   . org-goto)
          ("M-m o l"   . org-toggle-latex-fragment)
          ("M-m o L"   . org-toggle-link-display)
          ("M-m o I"   . org-toggle-inline-images)
          ("M-m o k"   . org-cut-subtree)
          ("M-m o V"   . org-reveal)
          ("M-m o R"   . org-refile)
          ("M-m o y"   . org-copy-subtree)
          ("M-m o h"   . org-toggle-heading)
          ("M-m o H"   . org-insert-heading-respect-content)
          ("M-m o e"   . org-export-dispatch)
          ("M-m o u"   . org-update-dblock)
          ("M-m o U"   . org-update-all-dblocks)
          ("M-m o O"   . org-footnote)
          ("M-m o ]"   . org-narrow-to-subtree)
          ("M-m o ["   . widen)
          ("M-m o N"   . org-add-note)
          ("M-m o E"   . org-set-effort)
          ("M-m o B"   . org-table-blank-field)
          ("M-m o <"   . org-date-from-calendar)
          ("M-m o >"   . org-goto-calendar)
          ("M-m o d"   . org-todo)
          ("M-m o t"   . org-set-tags-command)
          ("M-m o w"   . org-edit-special)
          ("M-m o q"   . org-edit-src-exit)
          ("M-m o z"   . clone-indirect-buffer-other-window)
          ("M-m a s"   . org-mark-subtree)
          ("M-m o RET" . org-open-at-point))
  :config
  ;; More of those nice template expansion
  (add-to-list 'org-structure-template-alist '("A" "#+DATE: ?"))
  (add-to-list 'org-structure-template-alist '("C" "#+BEGIN_CENTER\n?\n#+END_CENTER\n"))
  (add-to-list 'org-structure-template-alist '("D" "#+DESCRIPTION: ?"))
  (add-to-list 'org-structure-template-alist '("E" "#+BEGIN_EXAMPLE\n?\n#+END_EXAMPLE\n"))
  (add-to-list 'org-structure-template-alist '("H" "#+LATEX_HEADER: ?"))
  (add-to-list 'org-structure-template-alist '("I" ":INTERLEAVE_PDF: ?"))
  (add-to-list 'org-structure-template-alist '("L" "#+BEGIN_LaTeX\n?\n#+END_LaTeX"))
  (add-to-list 'org-structure-template-alist '("M" "#+LATEX_HEADER: \\usepackage{minted}\n"))
  (add-to-list 'org-structure-template-alist '("N" "#+NAME: ?"))
  (add-to-list 'org-structure-template-alist '("P" "#+HTML_HEAD: <link rel=\"stylesheet\" type=\"text/css\" href=\"org.css\"/>\n"))
  (add-to-list 'org-structure-template-alist '("S" "#+SUBTITLE: ?"))
  (add-to-list 'org-structure-template-alist '("T" ":DRILL_CARD_TYPE: twosided"))
  (add-to-list 'org-structure-template-alist '("V" "#+BEGIN_VERSE\n?\n#+END_VERSE"))
  (add-to-list 'org-structure-template-alist '("X" "#+EXCLUDE_TAGS: reveal?"))
  (add-to-list 'org-structure-template-alist '("a" "#+AUTHOR: ?"))
  (add-to-list 'org-structure-template-alist '("c" "#+CAPTION: ?"))
  (add-to-list 'org-structure-template-alist '("d" "#+OPTIONS: ':nil *:t -:t ::t <:t H:3 \\n:nil ^:t arch:headline\n#+OPTIONS: author:t email:nil e:t f:t inline:t creator:nil d:nil date:t\n#+OPTIONS: toc:nil num:nil tags:nil todo:nil p:nil pri:nil stat:nil c:nil d:nil\n#+LATEX_HEADER: \\usepackage[margin=2cm]{geometry}\n#+LANGUAGE: en\n\n#+REVEAL_TRANS: slide\n#+REVEAL_THEME: white\n#+REVEAL_ROOT: file:///Users/sriramkswamy/Documents/workspace/github/reveal.js\n\n?"))
  (add-to-list 'org-structure-template-alist '("e" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
  (add-to-list 'org-structure-template-alist '("f" "#+TAGS: @?"))
  (add-to-list 'org-structure-template-alist '("h" "#+BEGIN_HTML\n?\n#+END_HTML\n"))
  (add-to-list 'org-structure-template-alist '("i" "#+INTERLEAVE_PDF: ?"))
  (add-to-list 'org-structure-template-alist '("k" "#+KEYWORDS: ?"))
  (add-to-list 'org-structure-template-alist '("l" "#+LABEL: ?"))
  (add-to-list 'org-structure-template-alist '("m" "#+BEGIN_SRC matlab\n?\n#+END_SRC"))
  (add-to-list 'org-structure-template-alist '("n" "#+BEGIN_NOTES\n?\n#+END_NOTES"))
  (add-to-list 'org-structure-template-alist '("o" "#+OPTIONS: ?"))
  (add-to-list 'org-structure-template-alist '("p" "#+BEGIN_SRC python\n?\n#+END_SRC"))
  (add-to-list 'org-structure-template-alist '("q" "#+BEGIN_QUOTE\n?\n#+END_QUOTE"))
  (add-to-list 'org-structure-template-alist '("r" ":PROPERTIES:\n?\n:END:"))
  (add-to-list 'org-structure-template-alist '("s" "#+BEGIN_SRC ?\n#+END_SRC\n"))
  (add-to-list 'org-structure-template-alist '("t" "#+TITLE: ?"))
  (add-to-list 'org-structure-template-alist '("v" "#+BEGIN_VERBATIM\n?\n#+END_VERBATIM")))
  • Which key binding explanation
    (which-key-add-key-based-replacements
        "M-m o" "org mode prefix")
    
  • Modal binding
    (modalka-define-kbd "o a"   "M-m o a")
    (modalka-define-kbd "o c"   "M-m o c")
    (modalka-define-kbd "o i"   "M-m o i")
    (modalka-define-kbd "o s"   "M-m o s")
    (modalka-define-kbd "o S"   "M-m o S")
    (modalka-define-kbd "o A"   "M-m o A")
    (modalka-define-kbd "o g"   "M-m o g")
    (modalka-define-kbd "o l"   "M-m o l")
    (modalka-define-kbd "o L"   "M-m o L")
    (modalka-define-kbd "o I"   "M-m o I")
    (modalka-define-kbd "o k"   "M-m o k")
    (modalka-define-kbd "o V"   "M-m o V")
    (modalka-define-kbd "o R"   "M-m o R")
    (modalka-define-kbd "o y"   "M-m o y")
    (modalka-define-kbd "o h"   "M-m o h")
    (modalka-define-kbd "o H"   "M-m o H")
    (modalka-define-kbd "o e"   "M-m o e")
    (modalka-define-kbd "o u"   "M-m o u")
    (modalka-define-kbd "o U"   "M-m o U")
    (modalka-define-kbd "o O"   "M-m o O")
    (modalka-define-kbd "o ]"   "M-m o ]")
    (modalka-define-kbd "o ["   "M-m o [")
    (modalka-define-kbd "o N"   "M-m o N")
    (modalka-define-kbd "o E"   "M-m o E")
    (modalka-define-kbd "o B"   "M-m o B")
    (modalka-define-kbd "o <"   "M-m o <")
    (modalka-define-kbd "o >"   "M-m o >")
    (modalka-define-kbd "o d"   "M-m o d")
    (modalka-define-kbd "o t"   "M-m o t")
    (modalka-define-kbd "o z"   "M-m o z")
    (modalka-define-kbd "o w"   "M-m o w")
    (modalka-define-kbd "o q"   "M-m o q")
    (modalka-define-kbd "a s"   "M-m a s")
    (modalka-define-kbd "o RET" "M-m o RET")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "o"     "org prefix"
      "o a"   "org agenda"
      "o c"   "org capture"
      "o i"   "org insert link"
      "o s"   "org store link"
      "o S"   "org subtree from list"
      "o A"   "org archive subtree"
      "o g"   "org goto"
      "o l"   "org latex preview"
      "o L"   "org toggle link display"
      "o I"   "org image preview"
      "o k"   "org kill subtree"
      "o V"   "org reveal"
      "o R"   "org refile"
      "o y"   "org copy subtree"
      "o h"   "org toggle heading"
      "o H"   "org insert heading"
      "o e"   "org export"
      "o u"   "org update current"
      "o U"   "org update all"
      "o O"   "org footnote"
      "o ]"   "org narrow subtree"
      "o ["   "org widen"
      "o N"   "org note"
      "o F"   "org attach"
      "o E"   "org set effort"
      "o B"   "org table blank field"
      "o <"   "org select from cal"
      "o >"   "org goto cal"
      "o t"   "org tag"
      "o d"   "org todo"
      "o z"   "split and clone"
      "o w"   "org special edit"
      "o q"   "org special edit quit"
      "a s"   "mark org subtree"
      "o RET" "org open link")
    

Org contributed packages

Org plus contrib is a package comprised of contributed packages to make Org a bit better. Although there are lot of packages in it, the one I want to load it for is Org drill to help me repeatedly memorize stuff.

(use-package org-drill
  :defer t
  :commands (org-drill
             org-drill-tree
             org-drill-directory)
  :init
  (setq org-drill-maximum-items-per-session 50
        org-drill-maximum-duration 20   ; 20 minutes
        org-drill-use-visible-cloze-face-p t
        org-drill-add-random-noise-to-intervals-p t
        org-drill-hint-separator "||"
        org-drill-left-cloze-delimiter "<["
        org-drill-right-cloze-delimiter "]>"
        org-drill-learn-fraction 0.25
        org-drill-cram-hours 2
        org-drill-leech-method 'warn)
  :config
  (progn
    (add-to-list 'org-modules 'org-drill)))

Org babel

Org babel is also another facet of Org I use so much. In fact, writing this entire file as a configuration is possible only because of babel.

(use-package babel
  :ensure t
  :init
  (setq org-confirm-babel-evaluate nil)
  :defer t
  :config
  (use-package ob-ipython
    :ensure t
    :defer t))

Deft

Deft is inspired by Notational velocity to quickly filter notes. It is a generic package that works with many files but I use it to quickly filter text files with any extension. I use it for Org mode.

(use-package deft
  :ensure t
  :commands (deft)
  :init
  (setq deft-extensions '("org")
        deft-recursive nil
        deft-use-filename-as-title t))

If you notice, there is no directory specified. This is because I usually want deft to search different directories when invoked differently. So, let’s add a wrapper function similar to the post in pragmatic emacs blog. The function below wraps around deft and sets the directory when called with an argument.

(defun sk/deft (dir)
  "Run deft in directory DIR"
  (setq deft-directory dir)
  (switch-to-buffer "*Deft*")
  (kill-this-buffer)
  (deft))

Let’s define a couple of functions that use this wrapper function.

(defun sk/deft-org ()
  "Uses the sk/deft function to search in the default org directory"
  (interactive)
  (sk/deft "~/Dropbox/org"))
(defun sk/deft-blog ()
  "Uses the sk/deft function to search in the blog posts directory"
  (interactive)
  (sk/deft "~/Dropbox/org/blogposts/posts"))
  • Key binding
    (bind-keys*
      ("M-m o f" . sk/deft-org)
      ("M-m o F" . sk/deft-blog))
    
    • Modal binding
      (modalka-define-kbd "o f" "M-m o f")
      (modalka-define-kbd "o F" "M-m o F")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o f" "filter org files"
        "o F" "filter blog posts")
      

Interleave

Interleaved notes are a thing of the past but you can get that on Org now.

(use-package interleave
  :ensure t
  :bind* (("M-m o n" . interleave))
  :commands (interleave interleave-pdf-mode))
  • Modal binding
    (modalka-define-kbd "o n" "M-m o n")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "o n" "org notes")
    

Org extra exports

Org already has a pretty good exporting system but we can make it better. It can export to a a reveal.js presentation or a HTML page with bootstrap. I write my Org file as a notes file most of the time and hence exporting it directly into a presentation would look horrible. Therefore, I have created a couple of template expansions <x and <X which export only headlines tagged as “reveal” and ignore all headlines tagged as “reveal” respectively. When none of the template expansions are used, the use of tags mean nothing as they won’t interfere with anything. However, when one of them is used, you can export the headlines tailor made to be a PPT or export the ones tailor made to be a notes file. Sweet!

I also have support to export org files to Github Flavored Markdown (now part of the org contributed packages), nikola rst (which is the static site generator I use for my blog), restructured text (mainly for writing Python documentation with sphinx) and twitter bootstrap format. All these extra export engines can be loaded by invoking the custom loading function using the menu to activate minor modes. Why do I do this? I have no interest in learning the hundreds of fragmented syntax out there. I use Org for everything.

(use-package ox-reveal
  :ensure t
  :defer t
  :init
  (setq org-reveal-title-slide-template "<h1>%t</h1>\n<h3>%a</h3>")
  (setq org-reveal-root "file:///Users/sriramkswamy/Documents/workspace/github/reveal.js")
  (use-package htmlize
    :ensure t))
(use-package ox-twbs
  :ensure t
  :defer t)
(use-package ox-nikola
  :ensure t
  :defer t)
(use-package ox-rst
  :ensure t
  :defer t)

Org download

You can drag and drop images in to an org file.

(use-package org-download
  :ensure t
  :defer 2)

Org bullets

We can make Org mode a little more pretty by having utf-8 bullets.

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

Org reference

Org ref is an amazing package. The best introduction to this package is a video by the author himself. Note that this is not loaded in the beginning and has to be manually loaded by invoking the custom loading function using the menu to activate minor modes.

(use-package org-ref
  :ensure t
  :defer t
  :init
  (setq org-ref-completion-library 'org-ref-ivy-bibtex)
  (setq org-ref-notes-directory "~/Dropbox/org/references/notes"
        org-ref-bibliography-notes "~/Dropbox/org/references/articles.org"
        org-ref-default-bibliography '("~/Dropbox/org/references/multiphysics.bib" "~/Dropbox/org/references/chanceconstraints.bib" "~/Dropbox/org/references/tensors.bib")
        org-ref-pdf-directory "~/Dropbox/org/references/pdfs/"))

Custom functions

List the count of items under each tag

Put this piece of code in any org mode buffer and evaluate it. You will get a table containing the tag and number of items in each tag.

(mapcar (lambda (tag)
          (list tag (length (org-map-entries t tag nil))))
        '("article"
          "books"
          "courses"
          "code"
          "card"
          "drill"
          "errands"
          "films"
          "gubby"
          "home"
          "idea"
          "job"
          "ledger"
          "meeting"
          "note"
          "online"
          "personal"
          "project"
          "reference"
          "reveal"
          "story"
          "technical"
          "vague"
          "work"
          "noexport"
          "cash"))

Load extra org goodies

Org mode and packages take a while to load. This command, when invoked, loads a bunch of extra stuff. So, I use it whenever I’m doing serious work in Org mode. Note that the first time you invoke this function, it will install the org-plus-contrib package if it is not installed already.

(defun sk/org-custom-load ()
  (interactive)
  (require 'org)
  (require 'ob)
  (require 'ox)
  ;; Some extra exports
  (require 'ox-reveal)
  (require 'ox-twbs)
  (require 'ox-rst)
  (require 'ox-nikola)
  (require 'ox-gfm)
  ;; JavaScript repl support
  (require 'ob-js)
  ;; Babel load
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     ;; (dot . t)
     ;; (ditaa . t)
     (latex . t)
     ;; (gnuplot . t)
     (sh . t)
     (js . t)
     ;; (C . t)
     (ledger . t)
     ;; (R . t)
     ;; (octave . t)
     (matlab . t)
     (python . t)))
  ;; References and citation
  (require 'org-ref)
  (require 'org-ref-latex)
  (require 'org-ref-pdf)
  (require 'org-ref-url-utils)
  ;; Flash cards
  (require 'org-drill))
;; Add obvious org mode hooks
(add-hook 'org-mode-hook 'visual-line-mode)
(add-hook 'org-mode-hook 'flyspell-mode)

Select inside the subtree

We have a way to select the entire subtree with heading but sometimes I want to select only the stuff inside a subtree and this function takes care of that.

(defun sk/mark-inside-subtree ()
  (interactive)
  (org-mark-subtree)
  (next-line 1))
  • Key bindings
    (bind-keys*
      ("M-m i s" . sk/mark-inside-subtree))
    
    • Modal binding
      (modalka-define-kbd "i s" "M-m i s")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "i s" "mark inside subtree")
      

Org export as OPML

As mentioned previously, Org2OPML is a script that converts Org documents to OPML ones. Let’s make a wrapper around this so that it is easier to call from a buffer. First, let’s download that utility. I have forked it.

git clone https://github.com/sriramkswamy/Org2OPML ~/bin/Org2OPML
(defun sk/org-opml-map ()
  "Uses Org2OPML script to generate OPML files"
  (interactive)
  (async-shell-command (concat "python ~/bin/Org2OPML/org2opml.py " buffer-file-name)))
  • Key bindings
    (bind-keys*
      ("M-m o M" . sk/org-opml-map))
    
    • Modal binding
      (modalka-define-kbd "o M" "M-m o M")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o M" "org export OPML map")
      

Clean OPML auto-generated file

Org2OPML is a neat little script that generates OPML files from Org mode content. This is useful while creating mind maps. However, there is a small nag that it adds an extra outline. This little function, derived from a macro, cleans that.

(defun sk/clean-opml ()
  "Clean that extra outline the org2opml python script puts"
  (interactive)
  (beginning-of-buffer)
  (re-search-forward "=\"\"" nil)
  (set-mark (point))
  (forward-char 1)
  (exchange-point-and-mark)
  (backward-word 2)
  (backward-char 1)
  (if (region-active-p)
      (let ((beg (region-beginning))
            (end (region-end)))
        (kill-region beg end)))
  (end-of-buffer)
  (re-search-backward "outline")
  (set-mark (point))
  (backward-char 2)
  (exchange-point-and-mark)
  (forward-word 1)
  (forward-char 1)
  (if (region-active-p)
      (let ((beg (region-beginning))
            (end (region-end)))
        (kill-region beg end))))
  • Key bindings
    (bind-keys*
      ("M-m o K" . sk/clean-opml))
    
    • Modal binding
      (modalka-define-kbd "o K" "M-m o K")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o K" "org clean OPML")
      

Open article notes

This function open my notes on the various research articles I have read.

(defun sk/open-article-notes ()
  "Opens the article notes from anywhere. Hard coded stuff"
  (interactive)
  (find-file "~/Dropbox/org/references/articles.org"))
  • Key binding
    (bind-keys*
      ("M-m SPC o" . sk/open-article-notes))
    
    • Modal binding
      (modalka-define-kbd "SPC o" "M-m SPC o")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "SPC o" "open article notes")
      

Hydras

There is so much functionality packed into org and those modifier keys are hard to press. Let’s leverage Hydras.

Organize trees

This is to rearrange sub trees, reorder them, promote, demote and cycle through TODOs.

(defhydra sk/hydra-org-organize (:color red
                                 :hint nil)
  "
 ^Meta^    ^Shift^   ^Shift-Meta^ ^Shift-Ctrl^  ^Move^        ^Item^
^^^^^^^^^^^^^--------------------------------------------------------------------
 ^ ^ _k_ ^ ^   ^ ^ _K_ ^ ^   ^ ^ _p_ ^ ^      ^ ^ _P_ ^ ^       _<_: promote  _u_: up     _q_: quit
 _h_ ^+^ _l_   _H_ ^+^ _L_   _b_ ^+^ _f_      _B_ ^+^ _F_       _>_: demote   _d_: down
 ^ ^ _j_ ^ ^   ^ ^ _J_ ^ ^   ^ ^ _n_ ^ ^      ^ ^ _N_ ^ ^
"
  ("h" org-metaleft)
  ("l" org-metaright)
  ("j" org-metadown)
  ("k" org-metaup)
  ("H" org-shiftleft)
  ("L" org-shiftright)
  ("J" org-shiftdown)
  ("K" org-shiftup)
  ("b" org-shiftmetaleft)
  ("f" org-shiftmetaright)
  ("n" org-shiftmetadown)
  ("p" org-shiftmetaup)
  ("B" org-shiftcontrolleft)
  ("F" org-shiftcontrolright)
  ("P" org-shiftcontroldown)
  ("N" org-shiftcontrolup)
  ("<" org-promote)
  (">" org-demote)
  ("d" org-move-item-down)
  ("u" org-move-item-up)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o o" . sk/hydra-org-organize/body))
    
    • Modal binding
      (modalka-define-kbd "o o" "M-m o o")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o o" "organize trees")
      

Hydra for task management

As mentioned before, Org is amazing for TODO lists and has the ability to schedule deadlines too.

(defhydra sk/hydra-org-todo (:color red
                             :hint nil)
  "
 _d_: deadline    _o_: over    _s_: schedule   _c_: check   _q_: quit
"
  ("d" org-deadline :color blue)
  ("o" org-deadline-close :color blue)
  ("s" org-schedule :color blue)
  ("c" org-check-deadlines)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o D" . sk/hydra-org-todo/body))
    
    • Modal binding
      (modalka-define-kbd "o D" "M-m o D")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o D" "deadline menu")
      

Check boxes

Org supports checkboxes and my configuration should too.

(defhydra sk/hydra-org-checkbox (:color pink
                                 :hint nil)
  "
 _t_: toggle   _s_: stats    _r_: reset    _c_: count    _q_: quit
"
  ("t" org-toggle-checkbox)
  ("c" org-update-checkbox-count-maybe)
  ("r" org-reset-checkbox-state-subtree)
  ("s" org-update-statistics-cookies)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o b" . sk/hydra-org-checkbox/body))
    
    • Modal binding
      (modalka-define-kbd "o b" "M-m o b")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o b" "check boxes")
      

Properties

Org properties are really good places to have meta data. Although I personally don’t create many properties manually, it is useful to have this functionality around.

(defhydra sk/hydra-org-property (:color red
                                 :hint nil)
  "
 _i_: insert  _p_: property   _s_: set    _d_: delete    _t_: toggle    _q_: quit
"
  ("i" org-insert-drawer)
  ("p" org-insert-property-drawer)
  ("s" org-set-property)
  ("d" org-delete-property)
  ("t" org-toggle-ordered-property)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o P" . sk/hydra-org-property/body))
    
    • Modal binding
      (modalka-define-kbd "o P" "M-m o P")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o P" "add properties")
      

Clocking functionality

Of course, Org mode has clocking functionality.

(defhydra sk/hydra-org-clock (:color red
                              :hint nil)
  "
 ^Clock^                     ^Timer^     ^Stamp^
^^^^^^^^^^-------------------------------------------------
 _i_: in       _z_: resolve    _b_: begin  _t_: stamp       _q_: quit
 _o_: out      _l_: last       _e_: end    _u_: inactive
 _r_: report   _c_: cancel     _m_: timer
 _d_: display  _g_: goto       _s_: set
"
  ("i" org-clock-in)
  ("o" org-clock-out)
  ("r" org-clock-report)
  ("z" org-resolve-clocks)
  ("c" org-clock-cancel)
  ("d" org-clock-display)
  ("l" org-clock-in-last)
  ("g" org-clock-goto)
  ("m" org-timer)
  ("s" org-timer-set-timer)
  ("b" org-timer-start)
  ("e" org-timer-stop)
  ("t" org-time-stamp)
  ("u" org-time-stamp-inactive)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o C" . sk/hydra-org-clock/body))
    
    • Modal binding
      (modalka-define-kbd "o C" "M-m o C")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o C" "clocking")
      

Table manipulation

Org mode can create tables with spreadsheet capabilities.

(defhydra sk/hydra-org-tables (:color red
                               :hint nil)
  "
 ^Field^   ^Shift^   ^Insert^      ^Delete^         ^Field^     ^Table^      ^Formula^
^^^^^^^^^^^^------------------------------------------------------------------------------
 ^ ^ _k_ ^ ^   ^ ^ _K_ ^ ^   _r_: row      _dr_: del row    _e_: edit   _a_: align   _+_: sum    _q_: quit
 _h_ ^+^ _l_   _H_ ^+^ _L_   _c_: column   _dc_: del col    _b_: blank  _|_: create  _=_: eval
 ^ ^ _j_ ^ ^   ^ ^ _J_ ^ ^   _-_: hline                   _i_: info             _f_: edit
"
  ("a" org-table-align)
  ("l" org-table-next-field)
  ("h" org-table-previous-field)
  ("j" org-table-end-of-field)
  ("k" org-table-beginning-of-field)
  ("r" org-table-insert-row)
  ("c" org-table-insert-column)
  ("-" org-table-insert-hline)
  ("J" org-table-move-row-down)
  ("K" org-table-move-row-up)
  ("H" org-table-move-column-left)
  ("L" org-table-move-column-right)
  ("dr" org-table-kill-row)
  ("dc" org-table-delete-column)
  ("b" org-table-blank-field)
  ("e" org-table-edit-field)
  ("i" org-table-field-info)
  ("+" org-table-sum)
  ("=" org-table-eval-formula)
  ("f" org-table-edit-formulas)
  ("|" org-table-create-or-convert-from-region)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o m" . sk/hydra-org-tables/body))
    
    • Modal binding
      (modalka-define-kbd "o m" "M-m o m")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o m" "manipulate table")
      

Jump

This is a massively useful hydra to move around in an org file

(defhydra sk/hydra-org-jump (:color pink
                             :hint nil)
  "
 ^Outline^          ^Item^   ^Table^   ^Block^   ^Link^
 ^^^^^^^^^^^-------------------------------------------------------------------------------
 ^ ^ _k_ ^ ^   ^ ^ _K_ ^ ^   ^ ^ _u_ ^ ^   ^ ^ ^ ^ ^ ^   ^ ^ _p_ ^ ^   ^ ^ _P_ ^ ^    _q_ quit
 _h_ ^+^ _l_   ^ ^ ^+^ ^ ^   ^ ^ ^+^ ^ ^   _b_ ^+^ _f_   ^ ^ ^+^ ^ ^   ^ ^ ^+^ ^ ^
 ^ ^ _j_ ^ ^   ^ ^ _J_ ^ ^   ^ ^ _d_ ^ ^   ^ ^ ^ ^ ^ ^   ^ ^ _n_ ^ ^   ^ ^ _N_ ^ ^
"
  ("j" outline-next-visible-heading)
  ("k" outline-previous-visible-heading)
  ("l" org-down-element)
  ("h" org-up-element)
  ("J" org-forward-heading-same-level)
  ("K" org-backward-heading-same-level)
  ("u" org-next-item)
  ("d" org-previous-item)
  ("f" org-table-next-field)
  ("b" org-table-previous-field)
  ("n" org-next-block)
  ("p" org-previous-block)
  ("N" org-next-link)
  ("P" org-previous-link)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o j" . sk/hydra-org-jump/body))
    
    • Modal binding
      (modalka-define-kbd "o j" "M-m o j")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o j" "jump around")
      

Agenda view

The agenda menu already has a built-in agenda view but I always forget the keys. So, I need some reminding

(defhydra sk/hydra-org-agenda-view (:color red
                                    :hint nil)
  "
 _d_: day        _g_: time grid    _a_: arch-trees    _L_: log closed clock
 _w_: week       _i_: inactive     _A_: arch-files    _c_: log clock check
 _t_: fortnight  _f_: follow       _r_: report        _l_: log mode toggle
 _m_: month      _e_: entry        _D_: diary         _q_: quit
 _y_: year       _!_: deadlines    _R_: reset
"
  ("R" org-agenda-reset-view)
  ("d" org-agenda-day-view)
  ("w" org-agenda-week-view)
  ("t" org-agenda-fortnight-view)
  ("m" org-agenda-month-view)
  ("y" org-agenda-year-view)
  ("l" org-agenda-log-mode)
  ("L" (org-agenda-log-mode '(4)))
  ("c" (org-agenda-log-mode 'clockcheck))
  ("f" org-agenda-follow-mode)
  ("a" org-agenda-archives-mode)
  ("A" (org-agenda-archives-mode 'files))
  ("r" org-agenda-clockreport-mode)
  ("e" org-agenda-entry-text-mode)
  ("g" org-agenda-toggle-time-grid)
  ("D" org-agenda-toggle-diary)
  ("!" org-agenda-toggle-deadlines)
  ("i"
   (let ((org-agenda-include-inactive-timestamps t))
     (org-agenda-check-type t 'timeline 'agenda)
     (org-agenda-redo)))
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o v" . sk/hydra-org-agenda-view/body))
    
    • Modal binding
      (modalka-define-kbd "o v" "M-m o v")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o v" "view agenda")
      

Org drill

As previously mentioned, I use the drill package from Org drill and I want to invoke it via different commands at different times.

(defhydra sk/hydra-org-drill (:color blue
                              :hint nil)
  "
 _f_: file        _r_: resume   _q_: quit
 _h_: heading     _a_: again
 _d_: directory   _c_: cram
"
  ("f" org-drill)
  ("h" org-drill-tree)
  ("d" org-drill-directory)
  ("r" org-drill-resume)
  ("a" org-drill-again)
  ("c" org-drill-cram)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o p" . sk/hydra-org-drill/body))
    
    • Modal binding
      (modalka-define-kbd "o p" "M-m o p")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o p" "practice")
      

Template expansion

Org has this useful feature of template expansion but I have so many templates that I forget their combinations. This first function is a wrapper to call those template expansions.

(defun hot-expand (str)
  "Expand org template."
  (insert str)
  (org-try-structure-completion))

Then, we have the actual hydra.

(defhydra sk/hydra-org-template (:color blue
                                 :hint nil)
  "
 ^One liners^                                        ^Blocks^                                      ^Properties^
--------------------------------------------------------------------------------------------------------------------------------------------------------
 _a_: author        _i_: interleave  _D_: description    _C_: center      _p_: python src    _n_: notes    _d_: defaults   _r_: properties        _<_: insert '<'
 _A_: date          _l_: label       _S_: subtitle       _e_: elisp src   _Q_: quote                     _L_: latex      _I_: interleave        _q_: quit
 _c_: caption       _N_: name        _k_: keywords       _E_: example     _s_: src                       _x_: export     _T_: drill two-sided
 _f_: file tags     _o_: options     _M_: minted         _h_: html        _v_: verbatim                  _X_: noexport
 _H_: latex header  _t_: title       _P_: publish        _m_: matlab src  _V_: verse
 "
  ("a" (hot-expand "<a"))
  ("A" (hot-expand "<A"))
  ("c" (hot-expand "<c"))
  ("f" (hot-expand "<f"))
  ("H" (hot-expand "<H"))
  ("i" (hot-expand "<i"))
  ("I" (hot-expand "<I"))
  ("l" (hot-expand "<l"))
  ("n" (hot-expand "<n"))
  ("N" (hot-expand "<N"))
  ("P" (hot-expand "<P"))
  ("o" (hot-expand "<o"))
  ("t" (hot-expand "<t"))
  ("C" (hot-expand "<C"))
  ("D" (hot-expand "<D"))
  ("e" (hot-expand "<e"))
  ("E" (hot-expand "<E"))
  ("h" (hot-expand "<h"))
  ("k" (hot-expand "<k"))
  ("M" (hot-expand "<M"))
  ("m" (hot-expand "<m"))
  ("p" (hot-expand "<p"))
  ("Q" (hot-expand "<q"))
  ("s" (hot-expand "<s"))
  ("S" (hot-expand "<S"))
  ("v" (hot-expand "<v"))
  ("V" (hot-expand "<V"))
  ("x" (hot-expand "<x"))
  ("X" (hot-expand "<X"))
  ("d" (hot-expand "<d"))
  ("L" (hot-expand "<L"))
  ("r" (hot-expand "<r"))
  ("I" (hot-expand "<I"))
  ("T" (hot-expand "<T"))
  ("b" (hot-expand "<b"))
  ("<" self-insert-command)
  ("q" nil :color blue))
  • Key binding

    This key binding is a little different. The following function invokes the hydra if the “<” character (which is the key for template expansion) is pressed while in a new line else just inserts “<”.

    (defun sk/org-template-hook ()
      (define-key org-mode-map "<"
        (lambda () (interactive)
          (if (looking-back "^")
              (sk/hydra-org-template/body)
            (self-insert-command 1)))))
    (add-hook 'org-mode-hook 'sk/org-template-hook)
    

Org ref

These are hydras for all the functionality of org ref. Although the first 3 ones have similar versions defined in the actual package, there were a few inconsistencies when I first installed it. So, I made these hydras. I’m keeping them. The first 3 pieces of code are hydras that have alternatives in the package. The last one is the one that will be invoked.

(defhydra sk/org-ref-bibtex-file (:color blue
                                  :hint nil)
  "
_v_: validate     _s_: sort     _r_: reformat     _c_: count     _p_: PDF      _q_: quit
  "
  ("v" bibtex-validate)
  ("s" bibtex-sort-buffer)
  ("r" bibtex-reformat)
  ("c" bibtex-count-entries)
  ("p" org-ref-build-full-bibliography)
  ("q" nil :color blue))
(defhydra sk/org-ref-bibtex-new-entry (:color blue
                                       :hint nil)
  "
_a_: article                 _b_: book      _p_: in proceedings   _M_: Manual      _u_: unpublished
_c_: article in collection   _i_: in book   _P_: proceedings      _t_: PhD thesis  _q_: quit
_r_: report                  _l_: booklet   _m_: Misc             _T_: MS thesis
  "
  ("a" bibtex-Article)
  ("c" bibtex-InCollection)
  ("r" bibtex-TechReport)
  ("b" bibtex-Book)
  ("i" bibtex-InBook)
  ("l" bibtex-Booklet)
  ("p" bibtex-InProceedings)
  ("P" bibtex-Proceedings)
  ("m" bibtex-Misc)
  ("M" bibtex-Manual)
  ("t" bibtex-PhdThesis)
  ("T" bibtex-MastersThesis)
  ("u" bibtex-Unpublished)
  ("q" nil :color blue))
(defhydra sk/org-ref-bibtex-hydra (:color blue
                                   :hint nil)
  "
_p_: Open pdf     _y_: Copy key               _n_: New entry     _w_: WOS
_b_: Open url     _f_: Copy formatted entry   _o_: Copy entry    _c_: WOS citing
_r_: Refile entry _k_: Add keywords           _d_: delete entry  _a_: WOS related
_e_: Email entry  _K_: Edit keywords          _L_: clean entry   _P_: Pubmed
_U_: Update entry _N_: Open notes             _R_: Crossref      _g_: Google Scholar
_s_: Sort entry   _A_: Remove nonascii        _C_: Cite entry    _q_: quit
_u_: Update field _F_: file funcs
"
  ("p" org-ref-open-bibtex-pdf)
  ("b" org-ref-open-in-browser)
  ("r" (lambda () (interactive)
         (bibtex-beginning-of-entry)
         (bibtex-kill-entry)
         (find-file (ido-completing-read
                     "Bibtex file: "
                     (f-entries "." (lambda (f) (f-ext? f "bib")))))
         (goto-char (point-max))
         (bibtex-yank)
         (save-buffer)
         (kill-buffer)))
  ("e" org-ref-email-bibtex-entry)
  ("U" (doi-utils-update-bibtex-entry-from-doi (org-ref-bibtex-entry-doi)))
  ("s" org-ref-sort-bibtex-entry)
  ("u" doi-utils-update-field)
  ("y" (kill-new  (bibtex-autokey-get-field "=key=")))
  ("f" bibtex-copy-summary-as-kill)
  ("k" helm-tag-bibtex-entry)
  ("K" (lambda ()
         (interactive)
         (org-ref-set-bibtex-keywords
          (read-string "Keywords: "
                       (bibtex-autokey-get-field "keywords"))
          t)))
  ("N" org-ref-open-bibtex-notes)
  ("A" org-ref-replace-nonascii)
  ("F" sk/org-ref-bibtex-file/body)
  ("n" sk/org-ref-bibtex-new-entry/body)
  ("o" bibtex-copy-entry-as-kill)
  ("d" bibtex-kill-entry)
  ("L" org-ref-clean-bibtex-entry)
  ("R" org-ref-bibtex-crossref)
  ("w" org-ref-bibtex-wos)
  ("c" org-ref-bibtex-wos-citing)
  ("a" org-ref-bibtex-wos-related)
  ("P" org-ref-bibtex-pubmed)
  ("g" org-ref-bibtex-google-scholar)
  ("C" sk/org-ref-cite-hydra/body)
  ("q" nil :color blue))
(defhydra sk/hydra-org-ref (:color blue
                            :hint nil)
  "
 _e_: bib new entry     _r_: ref link     _b_: bib file options    _q_: quit
 _t_: crossref entry    _l_: label link   _f_: file format
 _d_: doi entry         _c_: cite link    _k_: keyword set
  "
  ("e" sk/org-ref-bibtex-new-entry/body)
  ("t" crossref-add-bibtex-entry)
  ("d" doi-add-bibtex-entry)
  ("b" sk/org-ref-bibtex-hydra/body)
  ("k" org-ref-set-bibtex-keywords)
  ("r" org-ref-ivy-insert-ref-link)
  ("l" org-ref-ivy-insert-label-link)
  ("c" org-ref-ivy-insert-cite-link)
  ("f" sk/org-ref-bibtex-file/body)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m o r" . sk/hydra-org-ref/body))
    
    • Modal binding
      (modalka-define-kbd "o r" "M-m o r")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "o r" "org ref")
      

Version control

Magit

The best interface to Git ever. Enough said.

(use-package magit
  :ensure t
  :bind* (("M-m SPC e" . magit-status)
          ("M-m g b"   . magit-blame)))
  • Modal binding
    (modalka-define-kbd "SPC e" "M-m SPC e")
    (modalka-define-kbd "g b"   "M-m g b")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "SPC e" "explore git"
      "g b"   "git blame")
    

Highlight diffs

Highlight git diffs on the fly.

(use-package diff-hl
  :ensure t
  :commands (global-diff-hl-mode
             diff-hl-mode
             diff-hl-next-hunk
             diff-hl-previous-hunk
             diff-hl-mark-hunk
             diff-hl-diff-goto-hunk
             diff-hl-revert-hunk)
  :bind* (("M-m ] h" . diff-hl-next-hunk)
          ("M-m [ h" . diff-hl-previous-hunk)
          ("M-m i h" . diff-hl-mark-hunk)
          ("M-m a h" . diff-hl-mark-hunk)
          ("M-m g h" . diff-hl-diff-goto-hunk)
          ("M-m g H" . diff-hl-revert-hunk))
  :config
  (global-diff-hl-mode)
  (diff-hl-flydiff-mode)
  (diff-hl-margin-mode)
  (diff-hl-dired-mode))
  • Modal binding
    (modalka-define-kbd "] h" "M-m ] h")
    (modalka-define-kbd "[ h" "M-m [ h")
    (modalka-define-kbd "g h" "M-m g h")
    (modalka-define-kbd "g H" "M-m g H")
    (modalka-define-kbd "i h" "M-m i h")
    (modalka-define-kbd "a h" "M-m a h")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "] h" "next git hunk"
      "[ h" "previous git hunk"
      "g h" "goto git hunk"
      "g H" "revert git hunk"
      "i h" "select git hunk"
      "a h" "select a git hunk")
    

Git time machine

The ability to move to past versions of the current file, like a time machine.

(use-package git-timemachine
  :ensure t
  :commands (git-timemachine-toggle
             git-timemachine-switch-branch)
  :bind* (("M-m g l" . git-timemachine-toggle)
          ("M-m g L" . git-timemachine-switch-branch)))
  • Modal binding
    (modalka-define-kbd "g l" "M-m g l")
    (modalka-define-kbd "g L" "M-m g L")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g l" "git time machine"
      "g L" "time machine switch branch")
    

Gists

Gists is a nice feature of GitHub to share code easily. This package makes it easy to post code into a Gist.

(use-package yagist
  :ensure t
  :commands (yagist-region-or-buffer
             yagist-region-or-buffer-private)
  :bind* (("M-m g p" . yagist-region-or-buffer)
          ("M-m g P" . yagist-region-or-buffer-private))
  :init
  (setq yagist-encrypt-risky-config t))
  • Modal binding
    (modalka-define-kbd "g p" "M-m g p")
    (modalka-define-kbd "g P" "M-m g P")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g p" "gist public"
      "g P" "gist private")
    

Browse remote files

browse-at-remote is a very handy package to view the file/region on the actual Github/Gitlab/Bitbucket page.

(use-package browse-at-remote
  :ensure t
  :bind* (("M-m g i" . browse-at-remote)
          ("M-m g I" . browse-at-remote-kill)))
  • Modal binding
    (modalka-define-kbd "g i" "M-m g i")
    (modalka-define-kbd "g I" "M-m g I")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g i" "browse file/region remote"
      "g I" "copy remote URL")
    

Programming

Editorconfig

Editorconfig is a small utility that is helpful in keeping the code clean as it takes care of the necessary indentation and can be used across editors.

(use-package editorconfig
  :ensure t
  :demand t
  :config
  (editorconfig-mode 1))

YAML mode

YAML is a plain text file format used by many places to specify meta data. This provides some syntax highlighting for that.

(use-package yaml-mode
  :ensure t
  :mode "\\.yml$")

Start services

I often need to start some service/program in the background and instead of dropping down to the terminal every time, prodigy provides a better alternative to define a service, start and stop from within Emacs.

(use-package prodigy
  :ensure t
  :commands (prodigy)
  :bind* (("M-m s b" . prodigy))
  :init
  (prodigy-define-tag
    :name 'blog
    :ready-message "Serving blog. Ctrl-C to shutdown server")
  (prodigy-define-service
    :name "Nikola build"
    :command "nikola"
    :args '("build")
    :cwd "/Users/sriramkswamy/Dropbox/org/blogposts"
    :tags '(blog)
    :kill-signal 'sigkill)
  (prodigy-define-service
    :name "Nikola serve"
    :command "nikola"
    :args '("serve" "--browser")
    :cwd "/Users/sriramkswamy/Dropbox/org/blogposts"
    :tags '(blog)
    :kill-signal 'sigkill
    :kill-process-buffer-on-stop t)
  (prodigy-define-service
    :name "Nikola deploy"
    :command "nikola"
    :args '("github_deploy")
    :cwd "/Users/sriramkswamy/Dropbox/org/blogposts"
    :tags '(blog)
    :kill-signal 'sigkill))
  • Modal binding
    (modalka-define-kbd "s b" "M-m s b")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "s" "send code prefix"
      "s b" "background code")
    

Information/documentation at point

These are a set of programming major mode specific bindings. This will check for documentation/information about the symbol/variable/function at point and try to get information if possible. Else, it will default to a Google search. This depends on all the specific packages used for Emacs lisp, C/C++, Python, Statistics, MATLAB, Web and Dash. Also, just like auto-completion, I bind this functionality to C-c and not M-m. For more functionality, use the respective language specific Hydras.

Source/symbol at point

These are a set of programming major mode specific bindings that will check for the definition/source of the function/variable at point and try to go to it. It will default to a project wide search for other unsupported programming languages. This depends on all the specific packages used for Emacs Lisp, C/C++, Python, Statistics, MATLAB, Web and Helm projectile. For more functionality, use the respective language specific Hydras.

Emacs Lisp

I haven’t started programming seriously in Emacs Lisp to know the ways around yet but this is what I have at present. First let’s configure the Emacs Lisp mode to give us the auto-loaded bindings for I and S as mentioned the previous two sections about Source/symbol at point and Information/documentation at point.

(use-package emacs-lisp-mode
  :mode ("\\.el$" . emacs-lisp-mode)
  :bind (:map emacs-lisp-mode-map
              ("C-c I" . describe-function)
              ("C-c S" . find-function-at-point)))

Macro step

Emacs has provisions for a macro and this package interactively expands them.

(use-package macrostep
  :ensure t
  :commands (macrostep-expand
             macrostep-mode))

Auto compile

Auto compile byte recompiles files if they are byte-compiled already.

(use-package auto-compile
  :ensure t)

C/C++

I use C++ a lot while programming and this section tries to convert Emacs into a good environment for coding in C++. For a good introduction, take a look at this video. Let’s make sure we have some default bindings before we start. Note that these bindings depend on Rtags package.

Consider header files to be C++

I usually don’t code in C that much. Therefore, let’s make sure Emacs considers “.h” files as C++ header files.

(add-to-list 'auto-mode-alist '("\\.h\\'" . c++-mode))

Consider header files to be C++

I usually don’t code in C that much. Therefore, let’s make sure Emacs considers “.h” files as C++ header files.

(add-to-list 'auto-mode-alist '("\\.h\\'" . c++-mode))

Compile commands

The compile command bundled with Emacs is super useful. I have defined a few functions that help me compile things quickly.

(defun sk/compile-cpp-omp-math ()
  "Compiles the file with OpenMP and math libraries"
  (interactive)
  (compile
   (concat "g++ -Wall -fopenmp -lgsl -lcblas -llapack -O2 -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-omp-simple ()
  "Compiles the file with OpenMP"
  (interactive)
  (compile
   (concat "g++ -Wall -fopenmp -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-mpi-math ()
  "Compiles the file with MPI and math libraries"
  (interactive)
  (compile
   (concat "/usr/local/openmpi/bin/mpic++ -Wall -lgsl -lcblas -llapack -larmadillo -O2 -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-mpi-simple ()
  "Compiles the file with MPI"
  (interactive)
  (compile
   (concat "/usr/local/openmpi/bin/c++ -Wall -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-hybrid-math ()
  "Compiles the file with OpenMP, MPI and math libraries"
  (interactive)
  (compile
   (concat "/usr/local/openmpi/bin/c++ -Wall -fopenmp -lgsl -lcblas -llapack -larmadillo -O2 -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-hybrid-simple ()
  "Compiles the file with OpenMP and MPI"
  (interactive)
  (compile
   (concat "/usr/local/openmpi/bin/c++ -Wall -fopenmp -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-math ()
  "Compiles the file with math libraries"
  (interactive)
  (compile
   (concat "g++ -Wall -lgsl -lcblas -llapack -larmadillo -O2 -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))
(defun sk/compile-cpp-simple ()
  "Compiles the file"
  (interactive)
  (compile
   (concat "g++ -Wall -g -std=c++11 " (buffer-file-name) " -o " (file-name-sans-extension buffer-file-name) ".out")))

Rtags

Rtags is an industrial grade indexer based on clang and its integration with Emacs is quite amazing.

(use-package rtags
  :ensure t
  :defer 2
  :bind (:map c++-mode-map
              ("C-c I" . rtags-print-symbol-info)
              ("C-c S" . rtags-find-symbol-at-point))
  :init
  (setq rtags-autostart-diagnostics t)
  (setq rtags-completions-enabled t))

Cmake IDE

This is a package that sets up all the necessary configuration to work with C/C++.

(use-package cmake-ide
  :ensure t
  :defer 2
  :config
  (cmake-ide-setup))

Python

This is my python configuration. Similar to Emacs Lisp and C++, let’s create some nice bindings that depend on Anaconda mode.

Let’s use use-package to first manage python mode itself.

(use-package python
  :ensure t
  :mode ("\\.py\\'" . python-mode)
  :config
  (setq python-shell-interpreter "ipython"
        python-shell-interpreter-args "-i"))

Anaconda mode

Anaconda mode provides python code navigation and completion but you need to install a few dependencies first from pip first or it will install on its own.

(use-package anaconda-mode
  :ensure t
  :defer 2
  :diminish anaconda-mode
  :diminish anaconda-eldoc-mode
  :bind (:map python-mode-map
              ("C-c I" . anaconda-mode-show-doc)
              ("C-c S" . anaconda-mode-find-definitions))
  :config
  (progn
    (add-hook 'python-mode-hook 'anaconda-mode)))

Virtual environment support

This is a companion package in case I need to work with virtual environments.

(use-package pyenv-mode
  :ensure t
  :commands (pyenv-mode
             pyenv-mode-set
             pyenv-mode-unset))

Python formatting

I format python occasionally using py-yapf which uses Google’s yapf formatter.

(use-package py-yapf
  :ensure t
  :commands (py-yapf-buffer
             py-yapf-enable-on-save))

Python formatting

I use sphinx for documentation and this tiny package creates a sphinx appropriate docstring

(use-package sphinx-doc
  :ensure t
  :diminish sphinx-doc-mode
  :commands (sphinx-doc
             sphinx-doc-mode))

Testing

I haven’t started using this package but it helps in writing tests which I intend to.

(use-package pytest
  :ensure t
  :commands (pytest-all
             pytest-directory
             pytest-failed
             pytest-module
             pytest-one
             pytest-pdb-all
             pytest-pdb-directory
             pytest-pdb-module
             pytest-pdb-one))

Statistics

I rarely use R and occasionally julia but since this is just one package, I might as well install it. It might come in handy. For now, I have set it up only for Julia.

(use-package ess
  :ensure t
  :mode (("\\.r$" . R-mode)
         ("\\.R$" . R-mode)
         ("\\.jl$" . julia-mode))
  :commands (R-mode
             julia-mode
             sk/julia-shell-here
             ess-eval-function
             ess-eval-line
             ess-eval-buffer
             ess-switch-to-ESS)
  :config
  (require 'ess-site))

The following is a helper function to open Julia shell in a vertical split.

;; Vertical split julia REPL
(defun sk/julia-shell-here ()
  "opens up a new julia REPL in the directory associated with the current buffer's file."
  (interactive)
  (require 'ess-site)
  (split-window-right)
  (julia)
  (other-window 1))

MATLAB

I use MATLAB pretty often when I want to quickly type out some code and I use a OS X computer in my lab and have to manually set the path.

(use-package matlab-mode
  :ensure t
  :mode ("\\.m$" . matlab-mode)
  :bind (:map matlab-shell-mode-map
              ("C-c C-c" . term-interrupt-subjob))
  :init
  (setq matlab-shell-command "/Applications/MATLAB_R2016a.app/bin/matlab"
        matlab-indent-function t)
  (eval-after-load 'matlab
    '(add-to-list 'matlab-shell-command-switches "-nosplash")))

The following is a helper function to open a MATLAB command line in a vertical split

(defun sk/matlab-shell-here ()
  "opens up a new matlab shell in the directory associated with the current buffer's file."
  (interactive)
  (split-window-right)
  (other-window 1)
  (matlab-shell))

Web

I have just recently started dabbling in HTML/CSS and JavaScript. So, this is an initial configuration I have come up with. The keybindings for Information/documentation at point and Source/symbol at point are left untouched for this mode as of now. If it has to be added in the future, it will depend on Tern for navigation.

Web mode

This is a fully featured, supposedly awesome, package to edit HTML in Emacs.

(use-package web-mode
  :ensure t
  :mode ("\\.html$" . web-mode))

JavaScript syntax highlighting

This improves on the built-in JavaScript syntax highlighting.

(use-package js3-mode
  :ensure t
  :mode ("\\.js$" . js3-mode))

Coffeescript syntax highlighting

I use coffeescript only to edit my ubersicht configuration files.

(use-package coffee-mode
  :ensure t
  :mode "\\.coffee$")

SCSS syntax highlighting

Occasionally I’m forced to open SCSS files.

(use-package scss-mode
  :ensure t
  :mode "\\.scss$")

JSON mode

Syntax highlighting for json files.

(use-package json-mode
  :ensure t
  :mode "\\.json$")

Nginx syntax highlighting

I haven’t used nginx much but useful to have it.

(use-package nginx-mode
  :ensure t
  :commands (nginx-mode))

Write fast HTML

Emmet is very useful while writing HTML. Look in to the guide for more details as to how to use it.

(use-package emmet-mode
  :ensure t
  :diminish (emmet-mode . "ε")
  :bind* (("C-)" . emmet-next-edit-point)
          ("C-(" . emmet-prev-edit-point))
  :commands (emmet-mode
             emmet-next-edit-point
             emmet-prev-edit-point))

Render HTML pages real time

This package renders HTML in the web browser as you type.

(use-package impatient-mode
  :ensure t
  :diminish (impatient-mode . "ι")
  :commands (impatient-mode))

JavaScript navigation

Tern describes itself as a code analysis engine and this is an implementation to bring it into Emacs.

(use-package tern
  :ensure t
  :diminish tern-mode
  :defer 2
  :config
  (progn
    (add-hook 'js-mode-hook '(lambda () (tern-mode t)))))

Skewer JS REPL

Skewer is a JS REPL for JavaScript evaluation.

(use-package skewer-mode
  :ensure t
  :diminish skewer-mode
  :commands (skewer-mode
             skewer-html-mode
             skewer-css-mode
             run-skewer
             skewer-repl
             list-skewer-clients
             skewer-eval-defun
             skewer-eval-last-expression
             skewer-eval-print-last-expression
             skewer-load-buffer
             skewer-bower-load
             skewer-bower-refresh
             skewer-run-phantomjs
             skewer-phantomjs-kill))

Node JS REPL

Bring node js to Emacs.

(use-package nodejs-repl
  :ensure t
  :commands (nodejs-repl
             nodejs-repl-send-buffer
             nodejs-repl-switch-to-repl
             nodejs-repl-send-region
             nodejs-repl-send-last-sexp
             nodejs-repl-execute
             nodejs-repl-load-file))

Managing node versions with nvm.el.

(use-package nvm
  :ensure t
  :commands (nvm-use
             nvm-use-for))

Snatch JSON

Get the path to JSON element in Emacs.

(use-package json-snatcher
  :ensure t
  :commands (jsons-print-path))

Beautify

I’m not sure how useful this is but just like I have formatters for others, I’m keeping this.

(use-package web-beautify
  :ensure t
  :commands (web-beautify-css
             web-beautify-css-buffer
             web-beautify-html
             web-beautify-html-buffer
             web-beautify-js
             web-beautify-js-buffer))

Error checking

Flycheck is awesome.

(use-package flycheck
  :ensure t
  :diminish flycheck-mode
  :defer 2
  :bind* (("M-m ] l"   . flycheck-next-error)
          ("M-m [ l"   . flycheck-previous-error)
          ("M-m SPC l" . flycheck-list-errors))
  :config
  (global-flycheck-mode))
  • Modal binding
    (modalka-define-kbd "] l"   "M-m ] l")
    (modalka-define-kbd "[ l"   "M-m [ l")
    (modalka-define-kbd "SPC l" "M-m SPC l")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "] l"   "next error"
      "[ l"   "previous error"
      "SPC l" "list errors")
    

Auto completion

Company mode standing for “complete any” is an auto-completion framework with a lot of third part packages and backends. Although I usually trigger manual completion, this is useful to have sometimes. This is a huge definition. Also, this is the one of the functionality I bind to C-c and not M-m.

(use-package company
  :ensure t
  :commands (company-mode
             company-complete
             company-complete-common
             company-complete-common-or-cycle
             company-files
             company-dabbrev
             company-ispell
             company-c-headers
             company-jedi
             company-tern
             company-web-html
             company-auctex)
  :init
  (setq company-minimum-prefix-length 2
        company-require-match 0
        company-selection-wrap-around t
        company-dabbrev-downcase nil
        company-tooltip-limit 20                      ; bigger popup window
        company-tooltip-align-annotations 't          ; align annotations to the right tooltip border
        company-idle-delay .4                         ; decrease delay before autocompletion popup shows
        company-begin-commands '(self-insert-command)) ; start autocompletion only after typing
  (eval-after-load 'company
    '(add-to-list 'company-backends '(company-files
                                      company-capf)))
  :bind (("M-t"   . company-complete)
         ("C-c f" . company-files)
         ("C-c a" . company-dabbrev)
         ("C-c d" . company-ispell)
         :map company-active-map
              ("C-n"    . company-select-next)
              ("C-p"    . company-select-previous)
              ([return] . company-complete-selection)
              ("C-w"    . backward-kill-word)
              ("C-c"    . company-abort)
              ("C-c"    . company-search-abort))
  :diminish (company-mode . "ς")
  :config
  (global-company-mode)
  ;; C++ header completion
  (use-package company-c-headers
    :ensure t
    :bind (("C-c c" . company-c-headers))
    :config
    (add-to-list 'company-backends 'company-c-headers))
  ;; Python auto completion
  (use-package company-jedi
    :ensure t
    :bind (("C-c j" . company-jedi))
    :config
    (add-to-list 'company-backends 'company-jedi))
  ;; Tern for JS
  (use-package company-tern
    :ensure t
    :bind (("C-c t" . company-tern))
    :init
    (setq company-tern-property-marker "")
    (setq company-tern-meta-as-single-line t)
    :config
    (add-to-list 'company-backends 'company-tern))
  ;; HTML completion
  (use-package company-web
    :ensure t
    :bind (("C-c w" . company-web-html))
    :config
    (add-to-list 'company-backends 'company-web-html))
  ;; LaTeX autocompletion
  (use-package company-auctex
    :ensure t
    :bind (("C-c l" . company-auctex))
    :config
    (add-to-list 'company-backends 'company-auctex)))

REPL

This is to interact with various shells.

Eshell

Eshell is a built-in shell that is written in Lisp. It’s pretty good.

(use-package eshell
  :commands (eshell)
  :bind* (("M-m SPC s" . sk/eshell-vertical)
          ("M-m SPC S" . sk/eshell-horizontal))
  :init
  (setq eshell-glob-case-insensitive t
        eshell-scroll-to-bottom-on-input 'this
        eshell-buffer-shorthand t
        eshell-history-size 1024
        eshell-cmpl-ignore-case t
        eshell-aliases-file (concat user-emacs-directory ".eshell-aliases")
        eshell-last-dir-ring-size 512)
  :config
  (add-hook 'shell-mode-hook 'goto-address-mode))

Let’s also define a couple of functions to open it in a vertical or horizontal split.

;; Vertical split eshell
(defun sk/eshell-vertical ()
  "opens up a new shell in the directory associated with the current buffer's file."
  (interactive)
  (let* ((parent (if (buffer-file-name)
                     (file-name-directory (buffer-file-name))
                   default-directory))
         (name (car (last (split-string parent "/" t)))))
    (split-window-right)
    (other-window 1)
    (eshell "new")
    (rename-buffer (concat "*eshell: " name "*"))
    (eshell-send-input)))

;; Horizontal split eshell
(defun sk/eshell-horizontal ()
  "opens up a new shell in the directory associated with the current buffer's file."
  (interactive)
  (let* ((parent (if (buffer-file-name)
                     (file-name-directory (buffer-file-name))
                   default-directory))
         (name (car (last (split-string parent "/" t)))))
    (split-window-below)
    (other-window 1)
    (eshell "new")
    (rename-buffer (concat "*eshell: " name "*"))
    (eshell-send-input)))
  • Key binding
    (bind-keys*
      ("M-m SPC s" . sk/eshell-vertical)
      ("M-m SPC S" . sk/eshell-horizontal))
    
    • Modal binding
      (modalka-define-kbd "SPC s" "M-m SPC s")
      (modalka-define-kbd "SPC S" "M-m SPC S")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "SPC s" "eshell vertical split"
        "SPC S" "eshell horizontal split")
      

Multi term

Multi term doesn’t provide any additional commands to built-in Emacs term and ansi-term but helps in managing multiple terminal buffers.

(use-package multi-term
  :ensure t
  :commands (multi-term)
  :bind* (("M-m SPC t" . sk/multi-term-vertical)
          ("M-m SPC T". sk/multi-term-horizontal)))

Let’s also define a couple of wrapper functions to open this in splits.

;; Vertical split multi-term
(defun sk/multi-term-vertical ()
  "opens up a new terminal in the directory associated with the current buffer's file."
  (interactive)
  (split-window-right)
  (other-window 1)
  (multi-term))

;; Horizontal split multi-term
(defun sk/multi-term-horizontal ()
  "opens up a new terminal in the directory associated with the current buffer's file."
  (interactive)
  (split-window-below)
  (other-window 1)
  (multi-term))
  • Key binding
    (bind-keys*
      ("M-m SPC t" . sk/multi-term-vertical)
      ("M-m SPC T" . sk/multi-term-horizontal))
    
    • Modal binding
      (modalka-define-kbd "SPC t" "M-m SPC t")
      (modalka-define-kbd "SPC T" "M-m SPC T")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "SPC t" "terminal vertical split"
        "SPC T" "terminal horizontal split")
      

Interacting with Tmux

Although I am not using tmux as much as I used to when I was using Vim, this package helps in integrating with it.

(use-package emamux
  :ensure t
  :commands (emamux:send-command
             emamux:run-command
             emamux:run-last-command
             emamux:zoom-runner
             emamux:inspect-runner
             emamux:close-runner-pane
             emamux:close-panes
             emamux:clear-runner-history
             emamux:interrupt-runner
             emamux:copy-kill-ring
             emamux:yank-from-list-buffers))

Compilation buffer

Whenever I run compile, the buffer stays even after a successful compilation. Let’s make it close automatically if the compilation is successful.

(setq compilation-finish-functions
      (lambda (buf str)
        (if (null (string-match ".*exited abnormally.*" str))
            ;;no errors, make the compilation window go away in a few seconds
            (progn
              (run-at-time "0.4 sec" nil
                           (lambda ()
                             (select-window (get-buffer-window (get-buffer-create "*compilation*")))
                             (switch-to-buffer nil)))
              (message "No Compilation Errors!")))))

Quickrun

This package helps you quickly run little pieces of code.

(use-package quickrun
  :ensure t
  :commands (quickrun
             quickrun-region
             quickrun-with-arg
             quickrun-shell
             quickrun-compile-only
             quickrun-replace-region))

Debugging

Emacs has a built-in debugger interface but this is supposed to provide additional functions. I haven’t tested it yet.

(use-package realgud
  :ensure t
  :commands (realgud:gdb
             realgud:ipdb
             realgud:pdb))

Hydras

Tmux integration

This menu helps in sending code and commands to tmux.

(defhydra sk/hydra-for-emamux (:color red
                               :hint nil)
 "
 ^Command^    ^Runner^                          ^Clipboard^
^^^^^^^^^^-----------------------------------------------------------------
 _s_: send    _r_: run        _c_: close          _y_: copy kill    _q_: quit
            _l_: last cmd   _C_: close other    _p_: paste tmux
            _z_: zoom       _h_: clear hist
            _i_: inspect    _I_: interrupt
"
 ("s" emamux:send-command)
 ("r" emamux:run-command)
 ("l" emamux:run-last-command)
 ("z" emamux:zoom-runner)
 ("i" emamux:inspect-runner)
 ("c" emamux:close-runner-pane)
 ("C" emamux:close-panes)
 ("h" emamux:clear-runner-history)
 ("I" emamux:interrupt-runner)
 ("y" emamux:copy-kill-ring)
 ("p" emamux:yank-from-list-buffers)
 ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s t" . sk/hydra-for-emamux/body))
    
    • Modal binding
      (modalka-define-kbd "s t" "M-m s t")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s t" "tmux code")
      

Emacs lisp

We define two hydras for Elisp. One for the macro step package and another for just regular editing.

(defhydra sk/hydra-macro-step (:pre (macrostep-expand)
                               :color red
                               :hint nil)
"
 _e_: expand    _j_: next macro    _C_: collapse all
 _c_: collapse  _k_: prev macro    _q_: quit
"
  ("e" macrostep-expand)
  ("c" macrostep-collapse)
  ("j" macrostep-next-macro)
  ("k" macrostep-prev-macro)
  ("C" macrostep-collapse-all)
  ("q" nil :color blue))

Then, the hydra for helping with Emacs Lisp code.

(defhydra sk/hydra-for-elisp (:color red
                              :hint nil)
  "
 ^Send^                         ^Navigate^                       ^Documentation^
^^^^^^^^^^-----------------------------------------------------------------------------------
 _r_: region    _e_: expression   _i_: library    _m_: macrostep     _D_: defun     _q_: quit
 _f_: func      _l_: last sexp    _d_: defun      _n_: find dired    _V_: variable
 _b_: buffer    _s_: ielm         _v_: variable   _g_: grep          _I_: library
"
  ("r" eval-region)
  ("f" eval-defun)
  ("b" eval-buffer)
  ("e" eval-expression :color blue)
  ("l" eval-last-sexp)
  ("s" ielm :color blue)
  ("i" find-library)
  ("d" find-function-at-point)
  ("v" find-variable)
  ("m" sk/hydra-macro-step/body :exit t)
  ("n" find-dired)
  ("g" find-grep-dired)
  ("D" describe-function)
  ("V" describe-variable)
  ("I" describe-library)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s e" . sk/hydra-for-elisp/body))
    
    • Modal binding
      (modalka-define-kbd "s e" "M-m s e")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
          "s e" "elisp code")
      

C/C++

C/C++ code navigation and compilation demands a hydra.

(defhydra sk/hydra-for-cpp (:color blue
                            :hint nil)
  "
 ^Send^                                                    ^Navigate^
^^^^^^^^^^----------------------------------------------------------------------------------------------------------------------------------
 _B_: build            _o_: omp math      _O_: omp simple      _s_: sym-at-pt   _r_: ref-at-pt    _D_: diag    _f_: for-stack    _n_: next    _i_: info
 _c_: compile math     _m_: mpi math      _M_: mpi simple      _S_: symbol      _R_: references   _F_: fixit   _b_: back-stack   _p_: prev    _t_: type
 _C_: compile simple   _h_: hybrid math   _H_: hybrid simple   _v_: vir-at-pt   _N_: rename       _P_: preproc _d_: depends      _e_: enum    _q_: quit
"
  ("B" compile)
  ("o" sk/compile-cpp-omp-math)
  ("O" sk/compile-cpp-omp-simple)
  ("m" sk/compile-cpp-mpi-math)
  ("M" sk/compile-cpp-mpi-simple)
  ("h" sk/compile-cpp-hybrid-math)
  ("H" sk/compile-cpp-hybrid-simple)
  ("c" sk/compile-cpp-math)
  ("C" sk/compile-cpp-simple)
  ("s" rtags-find-symbol-at-point :color red)
  ("S" rtags-find-symbol :color red)
  ("r" rtags-find-references-at-point :color red)
  ("R" rtags-find-references :color red)
  ("v" rtags-find-virtuals-at-point :color red)
  ("D" rtags-diagnostics :color red)
  ("F" rtags-fixit :color red)
  ("P" rtags-preprocess-file :color red)
  ("f" rtags-location-stack-forward :color red)
  ("b" rtags-location-stack-back :color red)
  ("n" rtags-next-match :color red)
  ("p" rtags-previous-match :color red)
  ("d" rtags-print-dependencies :color red)
  ("i" rtags-print-symbol-info :color red)
  ("t" rtags-symbol-type :color red)
  ("e" rtags-print-enum-value-at-point :color red)
  ("N" rtags-rename-symbol :color red)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s c" . sk/hydra-for-cpp/body))
    
    • Modal binding
      (modalka-define-kbd "s c" "M-m s c")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s c" "c/cpp code")
      

Python

Hydra for Python code navigation, REPL evaluation, testing and formatting.

(defhydra sk/hydra-for-py (:color blue
                           :hint nil)
  "
 ^Send^                     ^Navigate^                    ^Virtualenv^      ^Testing^                                        ^Format^
^^^^^^^^^^--------------------------------------------------------------------------------------------------------------------------------
 _r_: region    _s_: start    _d_: definition    _F_: file    _V_: pyenv        _ta_: test all    _tm_: test mod    _pd_: pdb dir    _c_: create-doc
 _b_: buffer    _S_: switch   _a_: assignment    _B_: back    _u_: pyenv set    _td_: test dir    _to_: test one    _pm_: pdb mod    _y_: yapf
 _f_: func                  _v_: reference     _D_: doc     _U_: pyenv unset  _tf_: test fail   _pa_: pdb all     _po_: pdb one    _q_: quit
"
  ("r" python-shell-send-region)
  ("b" python-shell-send-buffer)
  ("f" python-shell-send-defun)
  ("s" run-python)
  ("S" python-shell-switch-to-shell)
  ("d" anaconda-mode-find-definitions :color red)
  ("D" anaconda-mode-show-doc :color red)
  ("a" anaconda-mode-find-assignments :color red)
  ("v" anaconda-mode-find-references :color red)
  ("F" anaconda-mode-find-file :color red)
  ("B" anaconda-mode-go-back :color red)
  ("V" pyenv-mode :color red)
  ("u" pyenv-mode-set)
  ("U" pyenv-mode-unset)
  ("ta" pytest-all)
  ("td" pytest-directory)
  ("tf" pytest-failed)
  ("tm" pytest-module)
  ("to" pytest-one)
  ("pa" pytest-pdb-all)
  ("pd" pytest-pdb-directory)
  ("pm" pytest-pdb-module)
  ("po" pytest-pdb-one)
  ("c" sphinx-doc)
  ("y" py-yapf-buffer)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s p" . sk/hydra-for-py/body))
    
    • Modal binding
      (modalka-define-kbd "s p" "M-m s p")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s p" "python code")
      

Statistics

Since both R and Julia are supported by ESS, one hydra is enough.

(defhydra sk/hydra-for-ess (:pre (require 'ess-site)
                            :color blue
                            :hint nil)
  "
 _f_: func     _l_: line    _s_: start    _q_: quit
 _r_: region   _b_: buffer  _S_: switch
"
  ("f" ess-eval-function)
  ("l" ess-eval-line)
  ("r" ess-eval-region)
  ("b" ess-eval-buffer)
  ("s" sk/julia-shell-here)
  ("S" ess-switch-to-ESS)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s s" . sk/hydra-for-ess/body))
    
    • Modal binding
      (modalka-define-kbd "s s" "M-m s s")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s s" "stats code")
      

MATLAB

There is no facility for code navigation in MATLAB, and so this is just sending stuff to the MATLAB commandline.

(defhydra sk/hydra-for-matlab (:color blue
                               :hint nil)
  "
 _c_: cell   _r_: region    _s_: start    _m_: interrupt
 _l_: line   _C_: command   _S_: switch   _q_: quit
"
  ("c" matlab-shell-run-cell)
  ("l" matlab-shell-run-region-or-line)
  ("r" matlab-shell-run-region)
  ("C" matlab-shell-run-command)
  ("s" sk/matlab-shell-here)
  ("S" matlab-show-matlab-shell-buffer)
  ("m" term-interrupt-subjob)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s m" . sk/hydra-for-matlab/body))
    
    • Modal binding
      (modalka-define-kbd "s m" "M-s m m")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s m" "matlab code")
      

Web

There are two hydras we use for Web editing - one for JavaScript specifically and one for web mode.

(defhydra sk/hydra-for-js (:color blue
                           :hint nil)
  "
 ^Node^                                ^Tern^                                  ^Json^
^^^^^^^^^^-----------------------------------------------------------------------------------------
 _r_: region    _s_: start    _l_: load    _d_: definition    _h_: highlight refs    _j_: path
 _b_: buffer    _S_: switch              _n_: def by name   _u_: use-server        _q_: quit
 _x_: sexp      _e_: exec                _t_: type          _D_: doc
"
  ("r" nodejs-repl-send-region)
  ("b" nodejs-repl-send-buffer)
  ("x" nodejs-repl-send-last-sexp)
  ("s" nodejs-repl)
  ("S" nodejs-repl-switch-to-repl)
  ("e" nodejs-repl-execute)
  ("l" nodejs-repl-load-file :color red)
  ("d" tern-find-definition :color red)
  ("n" tern-find-definition-by-name :color red)
  ("t" tern-get-type :color red)
  ("D" tern-get-docs :color red)
  ("u" tern-use-server :color red)
  ("h" tern-highlight-refs)
  ("R" tern-rename-variable)
  ("j" jsons-print-path)
  ("q" nil :color blue))
(defhydra sk/hydra-for-web (:color red
                            :hint nil)
  "
 ^Server^           ^HTML^                ^Skewer^
^^^^^^^^^^------------------------------------------------------------------------------------------------------------------------------
 _w_: httpd start   _i_: html real-time   _j_: js eval     _r_: run            _f_: eval func              _l_: load buffer      _p_: phantomjs
 _W_: httpd stop                        _h_: html eval   _s_: start          _e_: eval last exp          _b_: bower load       _P_: phantomjs kill
                                      _c_: css eval    _S_: list clients   _E_: eval print last exp    _B_: bower refresh    _q_: quit
"
  ("w" httpd-start :color blue)
  ("W" httpd-stop :color blue)
  ("i" impatient-mode :color blue)
  ("j" skewer-mode)
  ("h" skewer-html-mode)
  ("c" skewer-css-mode)
  ("r" run-skewer)
  ("s" skewer-repl :color blue)
  ("S" list-skewer-clients :color blue)
  ("f" skewer-eval-defun :color blue)
  ("e" skewer-eval-last-expression :color blue)
  ("E" skewer-eval-print-last-expression :color blue)
  ("l" skewer-load-buffer :color blue)
  ("b" skewer-bower-load :color blue)
  ("B" skewer-bower-refresh)
  ("p" skewer-run-phantomjs :color blue)
  ("P" skewer-phantomjs-kill)
  ("q" nil :color blue))
(defhydra sk/hydra-for-format (:color red
                               :hint nil)
  "
 ^Beautify^
^^^^^^^^^^--------------------------------------
 _h_: html        _c_: css       _j_: js        _q_: quit
 _H_: html buf    _C_: css buf   _J_: js buf
"
  ("h" web-beautify-html)
  ("H" web-beautify-html-buffer)
  ("c" web-beautify-css)
  ("C" web-beautify-css-buffer)
  ("j" web-beautify-js)
  ("J" web-beautify-js-buffer)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s j" . sk/hydra-for-js/body)
      ("M-m s w" . sk/hydra-for-web/body)
      ("M-m s f" . sk/hydra-for-format/body))
    
    • Modal binding
      (modalka-define-kbd "s j" "M-m s j")
      (modalka-define-kbd "s w" "M-m s w")
      (modalka-define-kbd "s f" "M-m s f")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s j" "javscript code"
        "s w" "web code"
        "s f" "format code")
      

Quickrun

This is for Quickrun functionality.

(defhydra sk/hydra-quickrun (:color blue
                             :hint nil)
  "
 _s_: quickrun     _a_: with arg    _c_: compile only       _q_: quit
 _r_: run region   _S_: shell       _R_: replace region
"
  ("s" quickrun)
  ("r" quickrun-region)
  ("a" quickrun-with-arg)
  ("S" quickrun-shell)
  ("c" quickrun-compile-only)
  ("R" quickrun-replace-region)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s q" . sk/hydra-quickrun/body))
    
    • Modal binding
      (modalka-define-kbd "s q" "M-m s q")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s q" "quickrun code")
      

Debugging

Emacs has a built-in debugger in edebug and, apparently, it’s pretty good. Let’s make a small hydra for that.

A small hydra to start appropriate debuggers

(defhydra sk/hydra-debug (;; :pre (load-library "realgud")
                          :color blue
                          :hint nil)
  "
 _g_: c-gdb         _p_: py-pdb        _i_: py-ipdb        _q_: quit
 _G_: realgud-gdb   _P_: realgud-pdb   _I_: realgud-ipdb
"
  ("g" gdb)
  ("G" realgud:gdb)
  ("p" pdb)
  ("P" realgud:pdb)
  ("i" ipdb)
  ("I" realgud:ipdb)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m s d" . sk/hydra-debug/body))
    
    • Modal binding
      (modalka-define-kbd "s d" "M-m s d")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "s d" "debug code")
      

Helm

I am still on the fence about helm and ivy but org-ref requires this package. Since I have to install it anyway, I might as well make it a little better to suit my needs. Also, helm, for now, has many more features such as multiple sources, multiple selection, etc. It also has many plugins that take advantage of these features. I find ivy much simpler and I like it. Here, I have taken the help of this article to make it a little more usable (for me). Note that helm already has nice integration with spaceline and this configuration adds (and requires) support for Flx and Smex.

For now, I have made it look and act the way I want it too. The additional packages also help significantly. Therefore, I’m going to stick with Helm for now. This is a huge configuration. It has tons of additional packages that make helm act fuzzier, improves Flx support, adds project wide search and replace, adds buffer wide search and replace, integrates with projectile, searches through bindings, selecting snippets, manages AWS instances, adds IRC support, integrates with error checking, helps in correcting spelling mistakes and selecting a Makefile target.

(use-package helm
  :ensure t
  :demand t
  :diminish helm-mode
  :bind (("M-x"     . helm-M-x)
         ("M-y"     . helm-show-kill-ring)
         ("C-x C-f" . helm-find-files)
         ("C-x 8"   . helm-ucs))
  :bind* (("M-m SPC h r" . helm-resume)
          ("M-m SPC r"   . helm-for-files)
          ("M-m SPC x"   . helm-apropos)
          ("M-m SPC C" . helm-colors)
          ("M-m SPC h R" . helm-regexp)
          ("M-m SPC h u" . helm-surfraw)
          ("M-m SPC h t" . helm-top)
          ("M-m SPC h p" . helm-list-emacs-process)
          ("M-m SPC F"   . helm-find)
          ("M-m SPC h k" . helm-calcul-expression)
          ("M-m SPC h i" . helm-info-at-point)
          ("M-m SPC h d" . helm-man-woman)
          ("M-m SPC h h" . helm-documentation)
          ("M-m SPC h e" . helm-run-external-command)
          ("M-m ;"       . helm-all-mark-rings)
          ("M-m SPC h x" . helm-select-xfont)
          ("M-m t"       . helm-semantic-or-imenu))
  :bind (:map helm-map
              ("<return>"   . helm-maybe-exit-minibuffer)
              ("RET"        . helm-maybe-exit-minibuffer)
              ("<tab>"      . helm-select-action)
              ("C-i"        . helm-select-action)
              ("S-<return>" . helm-maybe-exit-minibuffer)
              ("S-RET"      . helm-maybe-exit-minibuffer)
              ("C-S-m"      . helm-maybe-exit-minibuffer))
  :bind (:map helm-find-files-map
              ("<return>"    . helm-execute-persistent-action)
              ("RET"         . helm-execute-persistent-action)
              ("<backspace>" . dwim-helm-find-files-up-one-level-maybe)
              ("DEL"         . dwim-helm-find-files-up-one-level-maybe)
              ("<tab>"       . helm-select-action)
              ("C-i"         . helm-select-action)
              ("S-<return>"  . helm-maybe-exit-minibuffer)
              ("S-RET"       . helm-maybe-exit-minibuffer)
              ("C-S-m"       . helm-maybe-exit-minibuffer))
  :bind (:map helm-read-file-map
              ("<return>"    . helm-execute-persistent-action)
              ("RET"         . helm-execute-persistent-action)
              ("<backspace>" . dwim-helm-find-files-up-one-level-maybe)
              ("DEL"         . dwim-helm-find-files-up-one-level-maybe)
              ("<tab>"       . helm-select-action)
              ("C-i"         . helm-select-action)
              ("S-<return>"  . helm-maybe-exit-minibuffer)
              ("S-RET"       . helm-maybe-exit-minibuffer)
              ("C-S-m"       . helm-maybe-exit-minibuffer))
  :commands (helm-mode
             helm-M-x
             helm-smex
             helm-find-files
             helm-buffers
             helm-recentf)
  :config
  ;; require basic config
  (require 'helm-config)
  (helm-mode 1)

  ;; use silver searcher when available
  (when (executable-find "ag-grep")
    (setq helm-grep-default-command "ag-grep -Hn --no-group --no-color %e %p %f"
          helm-grep-default-recurse-command "ag-grep -H --no-group --no-color %e %p %f"))

  ;; Fuzzy matching for everything
  (setq helm-M-x-fuzzy-match t
        helm-recentf-fuzzy-match t
        helm-buffers-fuzzy-matching t
        helm-locate-fuzzy-match nil
        helm-mode-fuzzy-match t)

  ;; set height and stuff
  (helm-autoresize-mode 1)
  (setq helm-autoresize-max-height 20
        helm-autoresize-min-height 20)

  ;; Work with Spotlight on OS X instead of the regular locate
  (setq helm-locate-command "mdfind -name -onlyin ~ %s %s")

  ;; Make sure helm always pops up in bottom
  (setq helm-split-window-in-side-p t)

  (add-to-list 'display-buffer-alist
               '("\\`\\*helm.*\\*\\'"
                 (display-buffer-in-side-window)
                 (inhibit-same-window . t)
                 (window-height . 0.2)))

  ;; provide input in the header line and hide the mode lines above
  (setq helm-echo-input-in-header-line t)

  (defvar bottom-buffers nil
    "List of bottom buffers before helm session.
      Its element is a pair of `buffer-name' and `mode-line-format'.")

  (defun bottom-buffers-init ()
    (setq-local mode-line-format (default-value 'mode-line-format))
    (setq bottom-buffers
          (cl-loop for w in (window-list)
                   when (window-at-side-p w 'bottom)
                   collect (with-current-buffer (window-buffer w)
                             (cons (buffer-name) mode-line-format)))))

  (defun bottom-buffers-hide-mode-line ()
    (setq-default cursor-in-non-selected-windows nil)
    (mapc (lambda (elt)
            (with-current-buffer (car elt)
              (setq-local mode-line-format nil)))
          bottom-buffers))

  (defun bottom-buffers-show-mode-line ()
    (setq-default cursor-in-non-selected-windows t)
    (when bottom-buffers
      (mapc (lambda (elt)
              (with-current-buffer (car elt)
                (setq-local mode-line-format (cdr elt))))
            bottom-buffers)
      (setq bottom-buffers nil)))

  (defun helm-keyboard-quit-advice (orig-func &rest args)
    (bottom-buffers-show-mode-line)
    (apply orig-func args))

  (add-hook 'helm-before-initialize-hook #'bottom-buffers-init)
  (add-hook 'helm-after-initialize-hook #'bottom-buffers-hide-mode-line)
  (add-hook 'helm-exit-minibuffer-hook #'bottom-buffers-show-mode-line)
  (add-hook 'helm-cleanup-hook #'bottom-buffers-show-mode-line)
  (advice-add 'helm-keyboard-quit :around #'helm-keyboard-quit-advice)

  ;; remove header lines if only a single source
  (setq helm-display-header-line nil)

  (defvar helm-source-header-default-background (face-attribute 'helm-source-header :background))
  (defvar helm-source-header-default-foreground (face-attribute 'helm-source-header :foreground))
  (defvar helm-source-header-default-box (face-attribute 'helm-source-header :box))

  (defun helm-toggle-header-line ()
    (if (> (length helm-sources) 1)
        (set-face-attribute 'helm-source-header
                            nil
                            :foreground helm-source-header-default-foreground
                            :background helm-source-header-default-background
                            :box helm-source-header-default-box
                            :height 1.0)
      (set-face-attribute 'helm-source-header
                          nil
                          :foreground (face-attribute 'helm-selection :background)
                          :background (face-attribute 'helm-selection :background)
                          :box nil
                          :height 0.1)))

  (add-hook 'helm-before-initialize-hook 'helm-toggle-header-line)

  ;; hide the minibuffer when helm is active
  (defun helm-hide-minibuffer-maybe ()
    (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 'helm-hide-minibuffer-maybe)

  ;; Proper find file behavior
  (defun dwim-helm-find-files-up-one-level-maybe ()
    (interactive)
    (if (looking-back "/" 1)
        (call-interactively 'helm-find-files-up-one-level)
      (delete-backward-char 1)))

  (defun dwim-helm-find-files-navigate-forward (orig-fun &rest args)
    "Adjust how helm-execute-persistent actions behaves, depending on context"
    (if (file-directory-p (helm-get-selection))
        (apply orig-fun args)
      (helm-maybe-exit-minibuffer)))

  (advice-add 'helm-execute-persistent-action :around #'dwim-helm-find-files-navigate-forward)

  ;; better smex integration
  (use-package helm-smex
    :ensure t
    :bind* (("M-x" . helm-smex)
            ("M-X" . helm-smex-major-mode-commands)))

  ;; Make helm fuzzier
  (use-package helm-fuzzier
    :ensure t
    :config
    (helm-fuzzier-mode 1))

  ;; Add support for flx
  (use-package helm-flx
    :ensure t
    :config
    (helm-flx-mode 1))

  ;; Add helm-bibtex
  (use-package helm-bibtex
    :ensure t
    :bind* (("M-m SPC b" . helm-bibtex))
    :init
    (setq bibtex-completion-bibliography '("~/Dropbox/org/references/multiphysics.bib" "~/Dropbox/org/references/chanceconstraints.bib" "~/Dropbox/org/references/tensors.bib"))
    (setq bibtex-completion-library-path "~/Dropbox/org/references/pdfs")
    (setq bibtex-completion-notes-path "~/Dropbox/org/references/articles.org"))

  ;; to search in projects - the silver searcher
  (use-package helm-ag
    :ensure t
    :bind* (("M-m g s" . helm-do-ag-project-root)
            ("M-m g e" . helm-do-ag)))

  ;; to search in files
  (use-package helm-swoop
    :ensure t
    :bind (("C-s" . helm-swoop-without-pre-input))
    :bind* (("M-m #"   . helm-swoop)
            ("M-m g /" . helm-multi-swoop)
            ("M-m o /" . helm-multi-swoop-org)
            ("M-m g E" . helm-multi-swoop-all))
    :init
    (setq helm-swoop-split-with-multiple-windows nil
          helm-swoop-split-direction 'split-window-vertically
          helm-swoop-split-window-function 'helm-default-display-buffer))

  ;; to help with projectile
  (use-package helm-projectile
    :ensure t
    :bind* (("M-m SPC d" . helm-projectile))
    :init
    (setq projectile-completion-system 'helm))

  ;; to describe bindings
  (use-package helm-descbinds
    :ensure t
    :bind* (("M-m SPC ?" . helm-descbinds)))

  ;; Control AWS via helm
  (use-package helm-aws
    :ensure t
    :bind* (("M-m SPC h w" . helm-aws)))

  ;; Control circe with helm
  (use-package helm-circe
    :ensure t
    :bind* (("M-m SPC h j" . helm-circe)
            ("M-m SPC h J" . helm-circe-new-activity)))

  ;; List errors with helm
  (use-package helm-flycheck
    :ensure t
    :bind* (("M-m SPC l" . helm-flycheck)))

  ;; Flyspell errors with helm
  (use-package helm-flyspell
    :ensure t
    :bind* (("M-m SPC h s" . sk/helm-correct-word))
    :config
    (defun sk/helm-correct-word ()
      (interactive)
      (save-excursion
        (sk/flyspell-goto-previous-error 1)
        (helm-flyspell-correct))))

  ;; Select snippets with helm
  (use-package helm-c-yasnippet
    :ensure t
    :bind (("C-o" . helm-yas-complete))
    :bind* (("C-,"        . helm-yas-create-snippet-on-region)
            ("C-<escape>" . helm-yas-visit-snippet-file)))

  ;; Helm integration with make
  (use-package helm-make
    :ensure t
    :init
    (setq helm-make-build-directory "build")
    :bind* (("M-m SPC m" . helm-make-projectile)
            ("M-m SPC M" . helm-make))))

Fun

Emacs is fun!

Weather

And while we are it, why not go ahead and get some weather status too.

(use-package wttrin
  :ensure t
  :commands (wttrin)
  :init
  (setq wttrin-default-cities '("Gainesville"
                                "Albuquerque"
                                "Chennai"
                                "Hyderabad"
                                "Columbus"
                                "Hillsboro")))

The author of Pragmatic Emacs noticed and improved it a bit (achievement unlocked!).

How do I do something?

Interface to the awesome command line utility.

(use-package howdoi
  :ensure t
  :bind* (("M-m g Y"   . howdoi-query)
          ("M-m SPC y" . howdoi-query-line-at-point)
          ("M-m SPC Y" . howdoi-query-insert-code-snippet-at-point)))
  • Modal binding
    (modalka-define-kbd "g Y" "M-m g Y")
    (modalka-define-kbd "SPC y" "M-m SPC y")
    (modalka-define-kbd "SPC Y" "M-m SPC Y")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g Y" "howdoi query"
      "SPC y" "howdoi query line"
      "SPC Y" "howdoi query insert code")
    

XKCD

Read xkcd in Emacs.

(use-package xkcd
  :ensure t
  :commands (xkcd)
  :bind* (("M-m g X" . xkcd)))
  • Modal binding
    (modalka-define-kbd "g X" "M-m g X")
    

    Which key modal explanation

    (which-key-add-key-based-replacements
      "g X" "xkcd")
    

Keep track of accounts

Ledger is a command-line utility to keep track of finances. I’m still experimenting with it.

(use-package ledger-mode
  :ensure t
  :mode "\\.dat$")

Gtalk

You can use the jabber protocol to chat from within Emacs.

;; Jabber
(use-package jabber
  :ensure t
  :commands (jabber-connect)
  :init
  (setq jabber-history-enabled t
        jabber-activity-mode nil
        jabber-use-global-history nil
        jabber-backlog-number 40
        jabber-backlog-days 30)
  (setq jabber-alert-presence-message-function
        (lambda (who oldstatus newstatus statustext) nil)))

IRC

I don’t really hang around any IRC channels but planning to do so some day.

(use-package circe
  :ensure t
  :commands (circe))

Touch typing

Learn touch typing from within Emacs.

(use-package typit
  :ensure t
  :commands (typit))

Learn the most frequently used keys

To track habits, maybe?

(use-package keyfreq
  :ensure t
  :init
  (setq keyfreq-excluded-commands
        '(self-insert-command
          org-self-insert-command
          company-ignore
          abort-recursive-edit
          forward-char
          modalka-mode
          backward-char
          previous-line
          next-line))
  :config
  (keyfreq-mode 1)
  (keyfreq-autosave-mode 1))

Google stuff

This is a pretty useful package that lets you Google stuff from within Emacs.

(use-package google-this
  :ensure t
  :commands (google-this-word
             google-this-region
             google-this-symbol
             google-this-clean-error-string
             google-this-line
             google-this-search
             google-this-cpp-reference))

Functions

This function googles the current symbol or the region if the region is active and the line when called with a prefix argument.

(defun sk/google-this ()
  "Google efficiently"
  (interactive)
  (if (region-active-p)
      (google-this-region 1)
    (google-this-symbol 1)))
  • Key bindings
    (bind-keys*
      ("M-m A" . sk/google-this))
    
    • Modal binding
      (modalka-define-kbd "A" "M-m A")
      

      Which key modal explanation

      (which-key-add-key-based-replacements
        "A" "ask google")
      

Hydra

A hydra for googling things!

(defhydra sk/hydra-google (:color blue
                           :hint nil)
  "
 _w_: word   _r_: region    _v_: symbol   _l_: line
 _g_: google _c_: cpp       _s_: string   _q_: quit
 "
  ("w" google-this-word)
  ("r" google-this-region)
  ("v" google-this-symbol)
  ("s" google-this-clean-error-string)
  ("l" google-this-line)
  ("g" google-this-search)
  ("c" google-this-cpp-reference)
  ("q" nil :color blue))
  • Key binding
    (bind-keys*
      ("M-m g G" . sk/hydra-google/body))
    
    • Modal binding
      (modalka-define-kbd "g G" "M-m g G")
      

      Which key modal explanation

      (which-key-add-key-based-replacements "g G" "google now")
      

Local configuration

There is provision to put and load a local configuration. For example, account and password details for Gtalk can be stored here. Here is the code that provides that support.

(when (file-exists-p (concat user-emacs-directory "local.el"))
  (load-file (concat user-emacs-directory "local.el")))

Also, note that you can redefine any of the functions that have been defined here. For example, if you use the following code for Org capture, it will overwrite the one that is already supplied with this configuration. This changes the name of the capture template and the directory in which it’s stored. Similarly, the directory in which org stores files or the agenda files can also be changed.

(setq org-capture-templates '(

        ;; For taking notes on random things
        ("n"               ; key
         "My Notes"            ; name
         entry             ; type
         (file+headline "~/OneDrive/org/notes.org" "Notes")  ; target
         "* %? %(org-set-tags)  :note:\n:PROPERTIES:\n:Created: %U\n:Linked: %A\n:END:\n%i"  ; template
         :prepend t        ; properties
         :empty-lines 1    ; properties
         :created t        ; properties
         :kill-buffer t))) ; properties

Key bindings

This section documents the key bindings that help to find other key-bindings. Note that when you search the keybindings, the Emacs ones are shown by default. These tend to be of the form M-m g g. The corresponding Modalka mode binding is just g g. So Pressing M-m or pressing Esc to toggle Modalka mode produces similar results (except, of course, you need not press Esc every time you want to use a binding in Modalka mode). Also note that Snippets is one place where modal bindings are not enforced because I want to write fast and not switch often when I’m writing.

Meta

Modalka state binding Emacs state binding Function
? M-m ? Show top level bindings
g ? C-h k Check the command for the given key
SPC ? C-h b search bindings
M C-u universal argument
E M-m E Deactivate region
ESC ESC Toggle Modalka mode
C-z C-z Toggle Modalka mode

Quitting and restarting Emacs

Modalka state binding Emacs state binding Function
: q C-x C-c Prompt to save and quit
: r C-x M-c Quit and restart-emacs
: t M-m : t Emacs startup time

Navigation

Modalka state binding Emacs state binding Function
j C-n next line
k C-p previous line
h C-b previous character
l C-f next character
g g M-< top of buffer
G M-> bottom of buffer
} M-} next para/org element
{ M-{ previous para/org element
J C-v scroll down
K M-v scroll up
H C-x < scroll left
L C-x > scroll right
e M-f next word
b M-b previous word
0 C-a start of line
$ C-e end of line
f M-m f find anything on screen with timer Avy
F M-m F goto line using Avy
g / M-m g / multi file search using helm-swoop
g s M-m g s search in project
g e M-m g e extract word from project
n M-n smart next item
N M-p smart previous item
; M-m ; see previous changes
+ M-m + Bookmark navigation
B M-m B switch to the buffer edited last
z C-l recenter screen
# M-m # helm - swoop = with cursor word
g j M-m g j next PDF page
g k M-m g k previous PDF page
SPC u M-m SPC u Window navigation hydra
w C-x o switch to other window
W M-m W undo window configuration
Z M-m Z zoom in to the current window
g w C-x 3 split window vertically
g W C-x 2 split window horizontally
t M-m t tags in buffer
T C-x T tags based navigation in project
g t M-m g t create tags for code
g T M-m g T update tags for code
g z M-m g z blink cursor
SPC p M-m SPC p switch perspective
SPC A M-m SPC A switch to buffer in perspective
SPC P M-m SPC P kill perspective
g r M-m g r rename perspective
] ] C-x n n narrow to region
[ [ C-x n w widen narrowed region
[ s M-m [ s previous spelling error
] s M-m ] s next spelling error
[ d M-m [ d turn the PDF previous page
] d M-m ] d Turn the adjoining PDF next page
g [ M-m g [ Scroll the adjoining window up
g ] M-m g ] Scroll the adjoining window down
g F M-m g F Synchronize splits
g m M-m g m make frame
g M M-m g M close frame
g n M-m g n select frame by name
g N M-m g N name frame
g f M-m g f find file/url under cursor
g ; M-m g ; goto-last-change
g , M-m g , goto-last-change-reverse
m a M-m m a Smartparens beginning of block
m b M-m m b smartparens backward expression
m c M-m m c smartparens change surroundings
m d M-m m d smartparens delete expression
m e M-m m e smartparens end of block
m f M-m m f smartparens forward expression
m h M-m m h smartparens backward down expression
m j M-m m j smartparens down expression
m k M-m m k smartparens backward up expression
m l M-m m l smartparens up expression
m n M-m m n smartparens next block
m p M-m m p smartparens previous block
m q M-m m q cycle quotes
m s M-m m s surroundings unwrap
m S M-m m S surroundings backward unwrap
m x M-m m x smartparens transpose expressions
m y M-m m y smartparens copy expression
SPC f C-x C-f find file browsing by directory
SPC d M-m SPC d find files based on Projectile or helm-projectile
SPC D M-m SPC D switch projects
SPC g M-g g goto line number
SPC n M-m SPC n directory navigation
SPC q C-x 0 close current window
SPC z M-m SPC z Non native full screen
SPC a C-x b switch to open buffer
SPC k C-x k kill buffer
SPC R M-m SPC R use locate command
SPC v M-m SPC v view configuration file
SPC j M-x find command
SPC J M-X find command in current mode
SPC i M-m SPC i browse code documentation with docset
SPC I M-m SPC I browse code docs immediately

Editing

Modalka state binding Emacs state binding Function
y M-w copy selected region or line
Y M-m Y copy to end of line
p C-y paste
P M-y paste history
x C-d delete character
d C-w kill region or backward word
D C-k delete to end of line
q C-x ( start macro
Q C-x ) end macro
@ M-m @ macros play menu
= M-m = indent region
O C-o Open line above
g S C-j split line
g J M-m g J join line
~ M-m ~ Toggle case
g u M-m g u lower case region
g q M-q Hard and Soft line wrap
g c M-m g c comment line or region
v C-SPC start selecting region
V M-m V rectangle edit menu
X C-x C-x exchange mark or reselect region
a a M-m a a expand region hydra
a b M-m a b select around pairs ()
a c M-m a c select around comment
a e M-m a e select around latex environment
a f M-m a f select around function
a h M-m a h select a git hunk
a j M-m a j select around julia/ruby block
a l M-m a l select around the current line
a m M-m a m select around python method
a o M-m a o select around org code block
a p M-m a p select around the para
a q M-m a q select around quotes
a s M-m a s select around org subtree
a t M-m a t select around latex math
a u M-m a u select around url
a v M-m a v select around variable
a w M-m a w select around word
i a C-x h select whole buffer
i b M-m i b select inside pairs ()
i c M-m i c select inside comment
i e M-m i e select inside latex environment
i f M-m i f select inside function
i h M-m i h select git hunk
i j M-m i j select around julia/ruby block
i l M-m i l select the current line
i m M-m i m select around python method
i o M-m i o select inside org code block
i p M-m i p select the para
i q M-m i q select inside quotes
i s M-m i s select inside org subtree
i t M-m i t select inside latex math
i u M-m i u select inside url
i v M-m i v select inside variable
i w M-m i w select inside word
. M-m . Multiple cursors each line
, M-m , multiple cursors skip
> M-m > multiple cursors next
< M-m < multiple cursors previous
* M-m * Interactive edit all instances
\ C-c C-c simulate C-c C-c
SPC w C-x C-s save
SPC SPC M-m SPC SPC Visually replace words
g R M-m g R rename current file
g K M-m g K delete current file
g y M-m g y copy current file path
g d M-m g d Duplicate line or region
g D M-m g D Insert date or date and time
[ w M-m [ w exchange with previous word
] w M-m ] w exchange with next word
[ c M-m [ c exchange with previous character
] c M-m ] c exchange with next character
[ e M-m [ e move line/region up
] e M-m ] e move line/region down
g C M-m g C Change from snake case to camel case
g _ M-m g _ Make current word snake case
g = M-m g = Add for auto correction
g + M-m g + Increase number at point
g - M-m g - Decrease number at point
M-m “ Registers menu
g u C-c C-e simulate C-c C-e - toggle edit modes
g U C-c C-k simulate C-c C-k - kill current action

Visual aids

Modalka state binding Emacs state binding Function
SPC c M-m SPC c load color themes
<Bar> M-m <Bar> syntax based fold
- M-m - fold toggle
_ M-m _ fold menu
g a M-m g a Activate minor modes
g * M-m g * Highlight thing at point

Org

Modalka state binding Emacs state binding Function
o < M-m o < org date from calendar
o > M-m o > org goto calendar
o / M-m o / org file helm-swoop
o A M-m o A org archive
o B M-m o B org table blank field
o C M-m o C org clocking
o D M-m o D org deadlines
o E M-m o E org set effort
o F M-m o F org attach
o H M-m o H org heading respect content
o I M-m o I org toggle inline images
o K M-m o K org bibtex add keyword
o L M-m o L org toggle link display
o N M-m o N org note
o P M-m o P org properties
o R M-m o R org refile
o S M-m o S org make subtree
o U M-m o U update all dblocks
o V M-m o V org reveal
o [ M-m o [ org widen
o ] M-m o ] org narrow to subtree
o a M-m o a org agenda
o b M-m o b org check box
o c M-m o c org capture
o d M-m o d org todo
o e M-m o e org export
o f M-m o f org filter
o g M-m o g org goto
o h M-m o h org toggle heading
o i M-m o i org insert link
o j M-m o j org jump
o k M-m o k org kill subtree
o l M-m o l org latex toggle
o m M-m o m org manipulate table
o n M-m o n org interleave
o o M-m o o org organize
o p M-m o p org practice
o q M-m o q org quit special buffer
o r M-m o r org ref
o s M-m o s org store link
o t M-m o t org tags command
o u M-m o u update dblock
o v M-m o v org agenda view
o w M-m o w org edit in special buffer
o y M-m o y org copy subtree
o z M-m o z org clone indirect buffer
o M M-m o M Org export as OPML
o K M-m o K Clean OPML auto-generated file
SPC o M-m SPC o Open article notes

Programming

Modalka state binding Emacs state binding Function
SPC e M-m SPC e explore git repository
g i M-m g i Browse remote files
g I M-m g I copy remote file URL
g b M-m g b git blame
] h M-m ] h next git hunk
[ h M-m [ h previous git hunk
g h M-m g h goto git hunk
g H M-m g H revert git hunk
g l M-m g l git log time machine
g L M-m g L git log switch branch
g p M-m g p gist post public
g P M-m g P gist post private
g o C-x C-e emacs lisp evaluate
g O C-M-x Evaluate function according to mode
I C-c I get info about thing at point (Dash fallback)
S C-c S get source of thing at point (Dumb jump fallback)
s b M-m s b start background processes
s e M-m s e emacs lisp code menu
s c M-m s c c/c++ code menu
s p M-m s p python code menu
s s M-m s s statistics code menu
s m M-m s m MATLAB code menu
s j M-m s j JavaScript code menu
s w M-m s w web mode code menu
s f M-m s f format code menu
s d M-m s d Debugging code menu
s t M-m s t Tmux code menu
] l M-m ] l next error
[ l M-m [ l previous error
SPC l M-m SPC l list errors
SPC s M-m SPC s Eshell vertical split
SPC S M-m SPC S Eshell horizontal split
SPC t M-m SPC t Terminal vertical split
SPC T M-m SPC T Terminal horizontal split

Helm

Modalka state binding Emacs state binding Function
; M-m ; all marks
SPC b M-m SPC b helm bibtex
SPC r M-m SPC r helm-for-files
SPC x M-m SPC x helm apropos
SPC m M-m SPC m select a Makefile target
SPC M M-m SPC M select a Makefile target in current dir
SPC C M-m SPC C color picker
SPC F M-m SPC F use the find utility
SPC h r M-m SPC h r helm resume
SPC h e M-m SPC h e run external command
SPC h s M-m SPC h s helm correct spelling
SPC h w M-m SPC h w manage AWS via aws cli
SPC h j M-m SPC h j manage IRC buffers
SPC h J M-m SPC h J checkout new IRC activity
SPC h d M-m SPC h d man pages
SPC h h M-m SPC h h read the helm documentation
SPC h i M-m SPC h i information at point
SPC h k M-m SPC h k calculate expression
SPC h p M-m SPC h p emacs processes
SPC h R M-m SPC h R build regexp interactively
SPC h t M-m SPC h t system top utility
SPC h u M-m SPC h u use the surfraw utility
SPC h x M-m SPC h x select the xfont

Fun

Modalka state binding Emacs state binding Function
A M-m A Google variable or region
g G M-m g G Google menu
g X M-m g X XKCD
g Y M-m g Y howdoi query
SPC y M-m SPC y howdoi query line
SPC Y M-m SPC Y howdoi query insert code
g x M-m g x open with the built-in browser
g : M-m g : open in external browser
g # M-m g # eww history
g { M-m g { browser back url
g } M-m g } browser forward url

Playground

This is a small section that I keep to put some random thoughts or packages that I need to try.

Setup Scala

Check out Ensime for that.

State

g > and g < to switch between state.

Automatically invoke tangle on save

This portion of comments let Emacs know that it has to invoke tangle on save. Since the actual code is in comment form, I have included the code to be included in verbatim form. Also, note that Emacs might prompt you if you are OK with the local variables being executed. Press “!” to tell Emacs that is OK and to ask it to save it to the Custom file.

# Local Variables:
# eval: (when (fboundp #'tangle-if-init) (add-hook 'after-save-hook #'tangle-if-init))
# End:

Author: Sriram Krishnaswamy

Created: 2016-07-20 Wed 02:16

Validate