Emacs Tips and Tricks 2: Sessions and Packages
This is the continuation of the series on Emacs tips and tricks. In this article, we explore session management, packages, managing indents, and other nice little things.
Table of contents
- Line numbers
- Newline sans indent
- Cursor movement
- Git status in dired
- Key bindings
- Closing remarks
An indispensable tool that I use now is desktop. It saves the state of my Emacs session, so that in the event of crash, power outage, or anything that will make me lose my session, I can back to it. Desktop comes built-in with the recent versions of GNU Emacs. Here's my snippet:
(require 'desktop) (desktop-save-mode) (setq desktop-dirname "~/.emacs.d" desktop-base-file-name "desktop" desktop-base-lock-name "desktop.lock" desktop-restore-frames t desktop-restore-reuses-frames t desktop-restore-in-current-display t desktop-restore-forces-onscreen t) (defun desktop-save () (interactive) (if (eq (desktop-owner) (emacs-pid)) (desktop-save desktop-dirname)))
Another important functionality that I use is savehist. It saves the minibuffer history. It’s roughly similar to saving the command line history. Here’s my snippet
(savehist-mode t) (setq savehist-file "~/.emacs.d/savehist")
There were a lot of times, when I want to manually save the state of as much session information that I could save. I’d want to save the buffers, minibuffer history, bookmarks, and comint mode histories. To do that, I have the following:
(defun save-defaults () (desktop-save desktop-dirname) (savehist-save) (bookmark-save)) (defun save-histories () (let ((buf (current-buffer))) (save-excursion (dolist (b (buffer-list)) (switch-to-buffer b) (save-history))) (switch-to-buffer buf))) (defun save () (interactive) (save-desktop) (save-defaults) (save-histories))
This gives you a nice:
M-x save RET
ELPA is the package management system of Emacs. If you aren’t using the package system yet, use it now. All you need to get started is the following:
(require 'package) (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("marmalade" . "http://marmalade-repo.org/packages/") ("melpa" . "http://melpa.milkbox.net/packages/"))) (package-initialize) (defalias 'pi 'package-install) (defalias 'pl 'package-list-packages) (defalias 'pr 'package-refresh-contents)
To list all the available packages, hit:
M-x pl REV
If you know the name of package, hit:
M-x pi RET package RET
To update your local database, hit:
M-x pr RET
This one is a real gem. It’s like
require, but on steroids. When require-ing a package, you have the option to specify to install that package, if it does not exist, yet. It also enables you to configure that package, in a unified expression. But unlike
use-package does not come built-in with Emacs. You need to install it via
M-x pi RET use-package RET
You can then require it, on subsequent uses:
markdown-mode, for example, even if it doesn’t exist yet, and configure its related options, after loading it, have:
(use-package markdown-mode :ensure t :config (progn (push '("\\.text\\'" . markdown-mode) auto-mode-alist) (push '("\\.markdown\\'" . markdown-mode) auto-mode-alist) (push '("\\.md\\'" . markdown-mode) auto-mode-alist)))
I really like to have the line numbers displayed at the left margin. It gives me a rough idea how big the file is, and where am I currently. Turning on
linum-mode achieves it:
(setq linum-format "%5d │ ") (defun my-linum-mode-hook () (linum-mode t)) (add-hook 'find-file-hook 'my-linum-mode-hook)
I frequently find the need to insert timestamps, especially when I'm editing my daily log file. Here are some snippets to help you with it:
(defun format-date (format) (let ((system-time-locale "en_US.UTF-8")) (insert (format-time-string format)))) (defun insert-date () (interactive) (format-date "%A, %B %d %Y")) (defun insert-date-and-time () (interactive) (format-date "%Y-%m-%d %H:%M:%S"))
Change the value of
system-time-locale as appropriate.
When your key bindings are not organized, it’s not easy to find what key did you bind to what. Fortunately, you have
bind-key, which comes as part of
An example usage of
bind-key would look like:
(bind-keys :map global-map ("C-;" . eval-expression) ("C-j" . delete-indentation))
This command creates a newline, then moves the cursor. It simulates a behavior wherein the new line doesn’t indent.
(defun newline-and-no-indent (&optional arg) (interactive "p") (open-line arg) (next-line arg))
This snippet works great when working with plain text. It indent a paragraph or the current paragraph context. If there is a mark, the region becomes filled.
(defun fill-region-or-paragraph () (interactive) (if (region-active-p) (fill-region) (fill-paragraph)))
move-to-window-line-top-bottom, bound by default to M-r is great when you want to move the cursor to the center, top, and bottom positions, relative to the window, similar to Vim’s H, M, and L commands.
However, it’s not very efficient when specifically targetting areas of the screen. The commands below position point, specifically to the top, center, and bottom window positions, respectively.
(defun move-to-window-line-top () (interactive) (move-to-window-line 0)) (defun move-to-window-line-center () (interactive) (move-to-window-line nil)) (defun move-to-window-line-bottom () (interactive) (move-to-window-line -1))
This small snippet gives visual indications of the status of git-managed files in a Dired buffer. Pressing g reloads the buffer, then updates the status.
(use-package dired-k :ensure t :config (progn (add-hook 'dired-initial-position-hook 'dired-k)))
The key bindings for the commands above, are listed below:
(bind-keys :map global-map ("C-c d" . insert-date) ("C-c C-d" . insert-date-and-time) ("S-<return>" . newline-and-no-indent) ("M-q" . fill-region-or-paragraph) ("C-M-y" . yank-primary) ("M-1" . move-to-window-line-top) ("M-2" . move-to-window-line-center) ("M-3" . move-to-window-line-bottom)) (bind-keys :map dired-mode-map ("K" . dired-k) ("g" . dired-k))
In this post, I demonstrated that small tweaks can generate huge benefits. The rest of the configuration can be found here. If you have an Emacs hack to share, send a pull request!