Emacs configuration file
Table of Contents
- Basics
- Defaults
- Personal information
- Custom file
- Packages
- Startup
- Backups
- Auto saving
- File encoding system
- Error message
- Truncating lines
- Large file warning
- Lazier prompting
- Expand some words and auto-correct
gdb
ediff
tramp
- Move correctly over camelCased words
- Understand the more common
sentence
- Recenter screen
- Better wild cards in search
- Persistent history
- Narrow to region
- PDF files
- Window management
- Recent files
- Fullscreen
- Fonts
- Use package macro
- Tangle on save
- Key hints
- Modal states
- Navigation
- Flx
- Smex
- Beacon mode
- Undo tree
- Goto the last change
- Avy
- Highlight symbol
- Projectile
- Ztree
- Neotree
- Tags based navigation
- Dumb jump
- Perspective
- Toggle zoom
- Code documentation
- Custom functions
- Go to the closest number
- Open the
config.org
file - Flyspell go to the previous spell error
- Non native full screen
- Split window and move
- Turn the adjoining PDF (only with 2 windows)
- Turn the adjoining window (only with 2 windows)
- Smarter start of line
- Rotate the windows
- Open the current HTML file in browser
- Hydras
- Editing
- Expand region
- Commenting
- Smartparens
- Interactive edit
- Multiple cursors
- Shrink white space
- Visual replace
- Snippets
- Cycle quotes
- Custom functions
- Toggle case
- Copy region on line
- Kill region or backward word
- Change region/till end of line
- Increase or decrease number at point
- Deactivate region
- Align blocks
- Insert date or date and time
- Rename the current buffer and the file associated with it
- Delete the current buffer and the file associated with it
- Copy the current file path
- Transposing words
- Transposing characters
- Copy to the end of the line
- Duplicate line or region
- Open line
- Join line
- Select the current line
- Correct those annoying DOuble capitals
- Move lines
- Change from snake case to camel case
- Make current word snake case
- Transpose paranthesis
- Add for auto correction
- Disable modalka mode when overwriting
- Hydras
- Visual
- Writing
- Org
- Version control
- Programming
- Helm
- Fun
- Local configuration
- Key bindings
- Playground
- Automatically invoke tangle on save
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
modeI 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
modeauto-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
modeEldoc 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
modesubword-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
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 ?")
- Numbers
- 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")
- Numbers
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))
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))
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)))
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)))
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))
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))
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))
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))
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))
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))
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)))
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)))
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))))
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")))
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)))))
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)))))
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))
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))
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))
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))))
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)))))
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))
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))
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)))
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)))
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)))
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)))
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)))
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")))))
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)))
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)))
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))
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)))
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)))
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")
- Modal binding
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))))
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)))))))
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)))))
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))
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))
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))
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"))
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))
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)))
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))
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))
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)))
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)))))
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)))
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)))
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"))))
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))
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))
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))
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))
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))
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)))
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
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)))
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))
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))
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
(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)))
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"))
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))
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))
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)))
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))))
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"))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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)))
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)))
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))
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)))
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))
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
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
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
(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))
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)))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))))
- Modal binding
(modalka-define-kbd "t" "M-m t") (modalka-define-kbd "#" "M-m #") (modalka-define-kbd ";" "M-m ;") (modalka-define-kbd "SPC J" "M-X") (modalka-define-kbd "g E" "M-m g E") (modalka-define-kbd "g s" "M-m g s") (modalka-define-kbd "g /" "M-m g /") (modalka-define-kbd "o /" "M-m o /") (modalka-define-kbd "g e" "M-m g e") (modalka-define-kbd "g u" "C-c C-e") (modalka-define-kbd "SPC r" "M-m SPC r") (modalka-define-kbd "SPC b" "M-m SPC b") (modalka-define-kbd "SPC x" "M-m SPC x") (modalka-define-kbd "SPC F" "M-m SPC F") (modalka-define-kbd "SPC C" "M-m SPC C") (modalka-define-kbd "SPC m" "M-m SPC m") (modalka-define-kbd "SPC M" "M-m SPC M") (modalka-define-kbd "SPC h r" "M-m SPC h r") (modalka-define-kbd "SPC h e" "M-m SPC h e") (modalka-define-kbd "SPC h w" "M-m SPC h w") (modalka-define-kbd "SPC h i" "M-m SPC h i") (modalka-define-kbd "SPC h R" "M-m SPC h R") (modalka-define-kbd "SPC h u" "M-m SPC h u") (modalka-define-kbd "SPC h t" "M-m SPC h t") (modalka-define-kbd "SPC h p" "M-m SPC h p") (modalka-define-kbd "SPC h k" "M-m SPC h k") (modalka-define-kbd "SPC h d" "M-m SPC h d") (modalka-define-kbd "SPC h h" "M-m SPC h h") (modalka-define-kbd "SPC h x" "M-m SPC h x") (modalka-define-kbd "SPC h j" "M-m SPC h j") (modalka-define-kbd "SPC h J" "M-m SPC h J") (modalka-define-kbd "SPC h s" "M-m SPC h s")
Which key modal explanation
(which-key-add-key-based-replacements "t" "tags/func in buffer" "#" "swoop at point" ";" "previous edit points" "g E" "extract word from buffers" "g s" "search project" "g /" "multi file search" "o /" "org swoop" "g e" "extract word from dir" "SPC r" "find any file" "SPC C" "color picker" "g u" "simulate C-c C-e" "SPC b" "bibliography" "SPC x" "helm apropos" "SPC J" "helm major mode cmds" "SPC F" "find command" "SPC h" "helm prefix" "SPC h r" "resume last helm " "SPC h e" "external command" "SPC h w" "AWS instances" "SPC h i" "information at point" "SPC h R" "build regexp" "SPC h u" "surfraw" "SPC h t" "system processes" "SPC h p" "emacs processes" "SPC h k" "calc expression" "SPC h d" "manual docs" "SPC h h" "helm docs" "SPC h x" "select font" "SPC h j" "circe chat" "SPC h J" "circe new activity" "SPC h s" "helm spelling" "SPC m" "make in project" "SPC M" "make in current dir")
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)))
XKCD
Read xkcd in Emacs.
(use-package xkcd :ensure t :commands (xkcd) :bind* (("M-m 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)))
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))
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: