Emacs Tutorial Series - episode 4
Look at a few interesting features that comes by default
Introduction
Emacs has a great ecosystem of plugins. Whether you’re doing web stuff, python or put the language you want here, you need tools and that’s when emacs plugins becomes handy. Autocompletion, jump to defintion and other fancy features are some of the features you can expect for most of the programming languages using the plugin ecosystem.
This post will be about tailoring emacs to make it behave more like an IDE tailored for your programming needs and install things that will make emacs even better
How do we install plugins
There’s 2 way to install things on emacs:
- rely on emacs package manager
- install things manually
Emacs package manager
That’s the generally accepted way to do it. To start installing packages with the package manager:
M-x package-refresh-contents
M-x package-install => then tab to see all the available packages and select the one you want
Tips:
If you’re behind a proxy, open your configuration (if you’re using the configuration given in episode 1), uncomment the very first lines (F4 or M-x uncomment-region) and fill in your company proxy details:
;; SETUP YOUR PROXY IF NEEDED
;; (setq url-proxy-services
;; '(("no_proxy" . "^\\(localhost\\|10.*\\)")
;; ("http" . "company.com:8080")
;; ("https" . "company.com:8080")))
Install things manually
Why you would ever do that
It’s not the general way of doing things as most package advise you to go with the package manager but despite this, I usually install plugins by hand. Here is why:
- I prefer to keep control over what to load, when and how. That way, I won’t ever be one of those many people:
- I prefer to have a one line copy paste install approach that will also configure the plugin in the way I want and keep those separate so that I can quickly get rid of stuff and add more on the fly.
- I always have issues with the package manager while I’m at work behind our very restrictive company proxy. Yes it happens, my company even put a warning message when I downloaded emacs saying this software could be dangerous (no joke).
How to do it
Installing a plugin by hand usually boils down to:
- get the code of your plugin and extract it somewhere
- If needed, get any dependency code and extract it somewhere
- setup the plugin so that you can actually use it.
Installing a plugin usually end up following those steps:
- make emacs know where to lookup for the plugin and its dependencies:
(add-to-list 'load-path "~/.emacs.d/plugins/my-great-plugin")
- load the plugin so that you can use it. There’s 2 approach to load it:
# approach 1: instantiate the plugin right away
(require 'my-super-plugin)
# approach 2: lazy load plugin instantiation
(autoload 'my-super-plugin "my-super-plugin" "super login mode" t)
Avoid the slow booting curse
-
The thumb rule
There’s way too many horror stories where people claim to have emacs booting up in 30s (I even saw someone who were claiming an amazing 30 minutes!). It won’t happen to you if you follow this simple rule of thumb: Is your plugin absolutely necessary when emacs starts? => yes: use require => no: use lazyloading
If you use require, you pay the plugins loading time upfront when emacs boots up. With lazy loading, you only pay for it when you need it. It might seem obvious but it’s very easy to endup with a bloated configuration. Personally my emacs typical booting time is 0.6 second (‘M-x emacs-init-time’). Still I got quite a lot of stuff in there. How I do it is simple, everytime I download something, I just apply the rule of thumb above.
-
Lazy loading
If I found out I can lazy load something I just do it like this:
(autoload 'cool-plugin-for-org-mode "cool-plugin-for-org-mode" "cool plugin for org mode" t) (add-hook 'org-mode-hook 'cool-plugin-for-org-mode)
Essentially, it means that when org mode is loaded, my plugin is getting load and I pay for its loading cost only when I need it.
There’s another class of plugin that you would want to use only when you press certain keys (think like project navigation, a git client, multiple cursors or any plugins not related to what your actual major mode is):
(autoload 'cool-mode "cool-mode" "cool mode" t) (global-set-key (kbd "C-c C-z") 'cool-mode)
How do I structure my emacs
To keep things separate, my emacs configuration is separate from the plugin configuration. Basically the .emacs.d has this structure:
.emacs.d
|- init.el
|- conf
|- conf_1.el (configuration something very specific)
|- conf_2.el (configuration for a plugin)
|- ...
|- plugins
|- deps (contains all dependencies used by all plugins)
|- plugin 1 (source code of plugin 1)
|- plugin 2 (source code of plugin 2)
|- ...
You might be worry that all dependencies are shared among all plugins (especially if you have some background in node) but I never had any issues with this structure. Maybe not having dependencies which called hundreds of dependencies which also have their own set of dependencies isn’t what you’ll typically see with emacs.
Assumptions
If you don’t want to start with the configuration given in episode 1 but want to keep going with this series of posts, you’ll have to at least execute those commands:
mkdir -p ~/.emacs.d/plugins/dep ~/.emacs.d/conf
# replace init.el by what your actual emacs configuration file if needed
cat > ~/.emacs.d/init.el <<EOF
(mapc
(lambda(path) (load-file path))
(directory-files "~/.emacs.d/conf/" t "\.el$"))
EOF
What this does is:
- create a folder to store all your plugins and their dependencies.
- create a configuration folder called ‘conf’ where you can drag and drop all your plugin related configuration or anything else that you think isn’t related to your core emacs conf. You’ll end up with a clear separation between the configuration of plugins and emacs core config.
Plugins I use
Git client: Magit
We’ll assume you’re using git for version control. Magit is the best solution in the emacs ecosystem to manage your git repository directly from emacs:
To install:
mkdir -p ~/.emacs.d/plugins/ ~/.emacs.d/plugins/deps && cd ~/.emacs.d/plugins/
git clone -b '2.10.3' --single-branch --depth 1 https://github.com/magit/magit
cd ./deps
git clone -b '2.13.0' --single-branch --depth 1 https://github.com/magnars/dash.el
git clone -b 'v2.5.10' --single-branch --depth 1 https://github.com/magit/with-editor
cd ~/.emacs.d/plugins/magit
cat > config.mk <<EOF
LOAD_PATH = -L ~/.emacs.d/plugins/deps/dash.el
LOAD_PATH += -L ~/.emacs.d/plugins/deps/with-editor
LOAD_PATH += -L ~/.emacs.d/plugins/magit/lisp
EOF
make
cat > ~/.emacs.d/conf/magit.el <<EOF
;; begin(magit)
(autoload 'magit-status "magit" "Magit mode" t)
(add-to-list 'load-path "~/.emacs.d/plugins/magit/lisp")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/dash.el")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/with-editor")
(global-set-key (kbd "C-c g") 'magit-status)
;; end(magit)
EOF
Reference: the repo
Undo/redo done right: Undo tree
On our everydays regular applications, undo redo is linear but it shouldn’t be. The actual change flow comes to navigate in a tree of change. That’s what undo tree is all about:
Installation:
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins/
git clone --single-branch --depth 1 https://github.com/emacsmirror/undo-tree
cat > ~/.emacs.d/conf/undo-tree.el <<EOF
;; begin(undo-tree)
(add-to-list 'load-path "~/.emacs.d/plugins/undo-tree")
(global-set-key (kbd "C-c C-z") 'undo-tree-visualize)
(autoload 'undo-tree-visualize "undo-tree" "undo-tree mode" t)
;; end(undo-tree)
EOF
you now have fine control of the undo/redo operation with C-c z
Reference: the repo
Project navigation: Projectile
Porjectile makes it easy to navigate in a project:
Installation:
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins/
git clone -b 'v0.14.0' --single-branch --depth 1 https://github.com/bbatsov/projectile
cat > ~/.emacs.d/conf/projectile.el <<EOF
;; begin(projectile)
(add-to-list 'load-path "~/.emacs.d/plugins/projectile")
(autoload 'projectile-find-file "projectile" "Projectile mode" t)
(autoload 'projectile-grep "projectile" "Projectile mode" t)
(setq projectile-indexing-method 'alien)
(global-unset-key (kbd "C-c C-f"))
(global-unset-key (kbd "C-c C-g"))
(global-set-key (kbd "C-c C-f") 'projectile-find-file)
(global-set-key (kbd "C-c C-g") 'projectile-grep)
;; end(projectile)
EOF
If you’re inside a project, it can be pretty boring to open a file with the typical C-x C-f
. Introducing C-c C-f
to find a file in your project more efficiently and C-c C-g
to find files based on a string in this file.
Reference: the repo
Programming environment I have used in emacs
Clojure: Cider
Reference: the repo
PHP
Reference: the repo
Elixir
mkdir -p ~/.emacs.d/plugins/ ~/.emacs.d/plugins/deps && cd ~/.emacs.d/plugins/
git clone -b 'v1.8.1' --single-branch --depth 1 https://github.com/tonini/alchemist.el
git clone -b 'v2.3.1' --single-branch --depth 1 https://github.com/elixir-lang/emacs-elixir
cd deps/
git clone -b '0.6' --single-branch --depth 1 https://github.com/lunaryorn/pkg-info.el
git clone -b '0.8' --single-branch --depth 1 https://github.com/cask/epl
cat > ~/.emacs.d/conf/elixir.el <<EOF
;; begin(elixir)
(add-to-list 'load-path "~/.emacs.d/plugins/alchemist.el")
(add-to-list 'load-path "~/.emacs.d/plugins/emacs-elixir")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/pkg-info.el")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/epl")
(autoload 'alchemist "alchemist" "alchemist mode" t)
(add-to-list 'auto-mode-alist '("\.ex$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\.exs$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\.html$" . html-mode))
;; end(elixir)
EOF
Reference: the repo
Other Web stuff
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins/
git clone -b 'v14.1' --single-branch --depth 1 https://github.com/fxbois/web-mode
cat > ~/.emacs.d/conf/web-mode.el <<EOF
;; begin(web-mode)
(add-to-list 'load-path "~/.emacs.d/plugins/web-mode")
(autoload 'web-mode "web-mode" "Web mode" t)
(add-to-list 'auto-mode-alist '("\.scss$" . css-mode))
(add-to-list 'auto-mode-alist '("\.less$" . css-mode))
(add-hook 'html-mode-hook 'web-mode)
;; end(web-mode)
EOF
Reference: the repo
Edit Markdown: Markdown mode
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins/
git clone -b 'v2.1' --single-branch --depth 1 https://github.com/jrblevin/markdown-mode
cat > ~/.emacs.d/conf/markdown-mode.el <<EOF
;; begin(markdown-mode)
(autoload 'markdown-mode "markdown-mode" "Markdown mode" t)
(add-to-list 'load-path "~/.emacs.d/plugins/markdown-mode")
(add-to-list 'auto-mode-alist '("\.markdown$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\.md$" . markdown-mode))
(add-hook 'text-mode-hook 'flyspell-mode)
;; end(tern)
EOF
Reference: the repo
Autocomplete
Autocomplete: Company mode
Company mode is a completion framework for emacs. It ships with different backend that let you autocomplete on different things by default. Of course you can extend it to work with the language you want, at least if you succeed to make it work.
mkdir -p ~/.emacs.d/plugins/ ~/.emacs.d/plugins/deps && cd ~/.emacs.d/plugins
git clone -b '0.9.2' --single-branch --depth 1 https://github.com/company-mode/company-mode
cat > ~/.emacs.d/conf/company-mode.el <<EOF
;; begin(company-mode)
(add-to-list 'load-path "~/.emacs.d/plugins/company-mode")
(autoload 'global-company-mode "company" "Company mode" t)
(add-hook 'after-init-hook 'global-company-mode)
(add-hook 'company-mode-hook 'on-company-mode-loaded)
(defun on-company-mode-loaded ()
(local-set-key (kbd "M-SPC") 'completion-at-point))
;; end(company-mode)
EOF
Reference: the repo
Javascript: Tern
Tern is a great autocompletion for js
mkdir -p ~/.emacs.d/plugins/ ~/.emacs.d/plugins/deps && cd ~/.emacs.d/plugins
git clone -b '0.12.0' --single-branch --depth 1 https://github.com/ternjs/tern/
cd tern && npm install
cd ~/.emacs.d/plugins/deps
git clone -b 'v0.5.3' --single-branch --depth 1 https://github.com/auto-complete/
cat > ~/.emacs.d/conf/tern.el <<EOF
;; begin(tern)
(defun load-tern ()
(setenv "PATH" (concat "/usr/local/bin:" (getenv "PATH")))
(add-to-list 'load-path "~/.emacs.d/plugins/tern/emacs")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/popup-el")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/auto-complete")
(autoload 'tern-mode "tern.el" nil t)
(eval-after-load 'tern
'(progn
(require 'auto-complete-config)
(ac-config-default)
(require 'tern-auto-complete)
(tern-ac-setup)))
(add-hook 'js-mode-hook (lambda () (tern-mode t))))
(add-hook 'after-init-hook 'load-tern)
;; end(tern)
EOF
cat > ~/.tern-config <<EOF
{
"plugins": {
"node": {},
"es_modules": {}
},
"libs": [
"ecma5",
"ecma6"
],
"ecmaVersion": 6
}
EOF
Note, there’s a autocomple plugin for tern too but I couldn’t make it work so I went with autocomplete instead.
Reference: the repo
Email: wanderlust
An email client for emacs:
Emacs comes with a builtin mode for email called gnus but I have found wanderlust to do better job at it. Because the proxy won’t let me use email at work, i use the package manager to install wanderlust
M-x package-refresh-contents
M-x package-install wanderlust
Setup:
cat > ~/.folders <<EOF
# -*- conf-unix -*-
GMAIL{
%INBOX:"mickael.kerjean"/clear@imap.gmail.com:993! "In"
%[Gmail]/Sent Mail:"mickael.kerjean"/clear@imap.gmail.com:993! "Sent"
}
PERSO{
%INBOX:"mickael@kerjean.me"@SSL0.OVH.NET "In"
}
EOF
cat > ~/.wl <<EOF
;; SMTP
(setq wl-smtp-connection-type 'starttls)
(setq wl-smtp-posting-port 587)
(setq wl-smtp-authenticate-type "plain")
(setq wl-smtp-posting-user "mickael@kerjean.me")
(setq wl-smtp-posting-server "SSL0.OVH.NET")
(setq wl-local-domain "kerjean.me")
(setq wl-from "mickael@kerjean.me")
;; conf
(setq wl-folder-check-async t)
(setq elmo-imap4-use-modified-utf7 t)
(autoload 'wl-user-agent-compose "wl-draft" nil t)
(if (boundp 'mail-user-agent)
(setq mail-user-agent 'wl-user-agent))
(if (fboundp 'define-mail-user-agent)
(define-mail-user-agent
'wl-user-agent
'wl-user-agent-compose
'wl-draft-send
'wl-draft-kill
'mail-send-hook))
(autoload 'wl "wl" "Wanderlust" t)
(autoload 'wl-other-frame "wl" "Wanderlust on new frame." t)
(autoload 'wl-draft "wl-draft" "Write draft with Wanderlust." t)
(add-hook 'wl-init-hook (lambda () (linum-mode nil)))
EOF
- Key links: https://www.emacswiki.org/emacs/WanderLust, http://www.gohome.org/wl/doc/wl_35.html
- Trick: Once you have entered in wl, it should ask all your password. If you’re too lazy to retype then everytime you connect:
M-x elmo-passwd-alist-save
will save those and reuse them. It will store those in ‘~/.elmo/passwd’
Reference: the repo
Other Stuff I like
Smooth scroll
The default scroll in emacs feels weird to me. Let’s fix this:
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins
git clone -b 'v2.0.0' --depth 1 --single-branch https://github.com/aspiers/smooth-scrolling
cat > ~/.emacs.d/conf/smooth-scroll.el <<EOF
;; begin(smooth-scroll)
(add-to-list 'load-path "~/.emacs.d/plugins/smooth-scrolling")
(require 'smooth-scrolling)
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time
(setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse
(setq scroll-step 1)
;; end(smooth-scroll)
EOF
Reference: the repo
Window centered mode
To makes your text centered instead of being stuck on the left side of your window
Install:
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins/
git clone --depth 1 https://github.com/anler/centered-window-mode
cd ./deps
git clone -b '1.11.0' --depth 1 --single-branch https://github.com/magnars/s.el
cat > ~/.emacs.d/conf/window-centered-mode.el <<EOF
;; begin(window-centered-mode)
(add-to-list 'load-path "~/.emacs.d/plugins/centered-window-mode")
(add-to-list 'load-path "~/.emacs.d/plugins/deps/s.el")
(require 'centered-window-mode)
(centered-window-mode t)
;; end(window-centered-mode)
EOF
Reference: the repo
Ace Jump
To quickly jump your cursor to some other position, ace jump is pretty nice:
Install:
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins
git clone -b 'v2.0' --single-branch --depth 1 https://github.com/winterTTr/ace-jump-mode
cat > ~/.emacs.d/conf/ace-jump.el <<EOF
;; begin(ace-jump)
(add-to-list 'load-path "~/.emacs.d/plugins/ace-jump-mode")
(autoload 'ace-jump-mode "ace-jump-mode" "ace jump mode" t)
(define-key global-map (kbd "C-c C-SPC") 'ace-jump-mode)
;; end(ace-jump)
EOF
C-c C-SPC
and type the first letter of the location you’re trying to go and let you guide!
Reference: the repo
Powerline
Just to make the status bar looks a bit more fancy:
Install:
mkdir -p ~/.emacs.d/plugins/ && cd ~/.emacs.d/plugins
git clone -b '2.4' --depth 1 --single-branch https://github.com/milkypostman/powerline
cat > ~/.emacs.d/conf/powerline.el <<EOF
;; begin(powerline)
(add-to-list 'load-path "~/.emacs.d/plugins/powerline")
(require 'powerline)
(powerline-default-theme)
;; end(powerline)
EOF
Reference: the repo
Mutliple cursors
Some other features some people like multiple-cursor
Get tabs in emacs Tabbar
I don’t use it anymore as I much prefer projectile and change buffer directly but if you can’t live without tabs, checkout tabbar
What next?
Be aware that’s there’s a lot of other emacs plugins in the market (one of the missing but very well known is helm but because I don’t use it doesn’t mean it isn’t worth looking into it).
This was the fourth episode of this emacs tutorial series and we’re already approaching the end. Until now we’ve been a typical user of emacs without really digging inside emacs internals which is the topic of the last episode.