We use melpa, an Emacs Lisp Package Archive, to get use-package package which provides a macro to isolate package configuration.
Before going any further, we first make sure we have an up-to-date description of the packages archive available.
As far as I know, emacs does not have any built-in mechanisms to automatically fetch a list of packages. There are several way to achieve that, one is Cask. I mention it here because it looks like a pretty good tool, but it is not available in the Archlinux repositories I am using (if you exclude the AUR package). Fortunately, I’ve quickly found out it is easy to define a function which does pretty much what I need:
(defun ensure-package-installed (&rest packages) "Assure every PACKAGES is installed, ask for installation if it’s not. Return a list of installed packages or nil for every skipped package." (mapcar (lambda (package) (if (package-installed-p package) nil (package-install package)) package) packages))
In the first version of my configuration file, I was using the
ensure-package-installed function to list all the packages I was using. Now, as said before, I use the
As stated in the use-package README, the macro is not required to be available at runtime, so lets require it as they say:
Look & Feel
I use my homegrown collection of themes, based on
colorless. It is not yet on Melpa, for no good reason. So instead I have to load it manually.
For a reason I do not fully understand,
emacsclient does not work very well with changing the color of the cursor. I have found a solution on StackOverflow.
The default status line on Emacs does the job, but sometimes it is good to just be a bit fancy.
This is macOS specific, but it looks like emacs on macOS has some trouble with the Meta key by default. So, following the advice read from the EmacsWiki, we just revert Emacs 22 settings.
I am a former vim user and I have switch to emacs mostly for Proof General. The only reason I ever consider making this move is because I ran into a blogpost which describes such a migration through the evil package, a way to get the vim bindings and modal functioning into emacs.
Note that, before loading evil, we need to tell it not to expand abbreviation on insert exit, see this Github issue if you want to know why.
Now, the think is I am a bépo happy user. It is quite different from qwerty and therefore I need to rebind a lot of things manually.
l rather than
r to replace, because
r is dedicated to moving inside a buffer.
I then remap
hjkl and because I like to complicate things, I use this opportunity to fix an issue I had with vim for quite some times now: when you think about it, they really should be
jklm instead (my index finger is always on the
j, not the
For this to work, the mapping needs to be done both for the normal mode and the visual mode.
(define-key evil-normal-state-map "t" 'evil-backward-char) (define-key evil-visual-state-map "t" 'evil-backward-char) (define-key evil-normal-state-map "s" 'evil-next-visual-line) (define-key evil-visual-state-map "s" 'evil-next-visual-line) (define-key evil-normal-state-map "r" 'evil-previous-visual-line) (define-key evil-visual-state-map "r" 'evil-previous-visual-line) (define-key evil-normal-state-map "n" 'evil-forward-char) (define-key evil-visual-state-map "n" 'evil-forward-char)
Because of this choice,
n (next research result) is already used so I need to find something else. Its neighbor
m is a perfect candidate.
I split my buffers a lot, so jumping from one buffer to another should be easy. The thing is,
w is not the most easy letter to hit in bépo (probably because we do not have a lot of words which are using it), so I use
(define-key evil-motion-state-map (kbd "à t") 'evil-window-left) (define-key evil-motion-state-map (kbd "à s") 'evil-window-down) (define-key evil-motion-state-map (kbd "à r") 'evil-window-up) (define-key evil-motion-state-map (kbd "à n") 'evil-window-right) (define-key evil-motion-state-map (kbd "à à") 'evil-window-next) (define-key evil-motion-state-map (kbd "à q") 'kill-this-buffer)
And because I really use this feature often, I decided it shall get its own mappings.
w is also used in vim to jump at the begin of the next word (or to select a word in operator mode. Lets use
(define-key evil-normal-state-map "é" 'evil-forward-word-begin) (define-key evil-visual-state-map "é" 'evil-forward-word-begin) (define-key evil-normal-state-map "É" 'evil-forward-WORD-begin) (define-key evil-visual-state-map "É" 'evil-forward-WORD-begin) (define-key evil-operator-state-map "é" 'evil-a-word) (define-key evil-operator-state-map "É" 'evil-a-WORD)
M-v to copy and paste from X. To do that, we first need to define the following functions, found on one emacs configuration online.
(defun copy-to-clipboard () (interactive) (if (display-graphic-p) (progn (message "Yanked region to x-clipboard!") (call-interactively 'clipboard-kill-ring-save) ) (if (region-active-p) (progn (shell-command-on-region (region-beginning) (region-end) "xsel -i -b") (message "Yanked region to clipboard!") (deactivate-mark)) (message "No region active; can't yank to clipboard!")))) (setq x-select-enable-clipboard nil) (defun paste-from-clipboard () (interactive) (setq x-select-enable-clipboard t) (yank) (setq x-select-enable-clipboard nil))
The mapping can be made both in normal and insert mode.
Did I mention I am a former vim user? I need the escape key to work everywhere. I found the following snippet in the blogpost I mention earlier and it works well so far.
(defun minibuffer-keyboard-quit () "Abort recursive edit. In Delete Selection mode, if the mark is active, just deactivate it; then it takes a second \\[keyboard-quit] to abort the minibuffer." (interactive) (if (and delete-selection-mode transient-mark-mode mark-active) (setq deactivate-mark t) (when (get-buffer "*Completions*") (delete-windows-on "*Completions*")) (abort-recursive-edit))) (define-key evil-normal-state-map [escape] 'keyboard-quit) (define-key evil-visual-state-map [escape] 'keyboard-quit) (define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit) (define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit) (define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit) (define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit) (define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit) (global-set-key [escape] 'evil-exit-emacs-state)
We should not forget the
dired mode, because it can be useful to be able to move inside the directories content.
I want to be able to zoom and zoom at.
Finally, I really wanted to get back the capability to easily increment or decrease the next number on the current line. To do that, the evil-numbers package.
First, lets simplify the look and feel of emacs. We do not need neither the scroll bar, nor the tool bar, nor the menu bar. Also, we can safely disable the startup screen.
In a similar manner, we do not need the cursor to blink. Lets keep the UI as simple as possible.
We can also change the way the scrolling works to get a behaviour a bit more smooth.
fill-column-indicator is install by default with linux packages, but not with Homebrew (macOs), hence we tell emacs to install it just in case.
I like to have vertical indicator of the indentation level, and the
highlight-indent-guides appeared to be exactly what I was locking for.
The only fancy thing I want is to highlight the line on which my cursor is. It can be very useful when I am lost.
Since its version 26, Emacs comes with a nice (and built-in)
global-display-line-numbers-mode that I use in place of
It is easy to make emacs highlights the matching parentheses (and to highlight mismatching ones too).
Rather than the built-in one, I use
helm as my completion system. Especially, I really like the fuzzy match feature, even if sometimes it is annoying to hit
space to specify two characters don’t have to be neighbors.
We also rely on
projectile to have some kind of “project management” in emacs. The most useful feature of projectile is maybe to use the
.gitignore file to filter the list of files available. And, it can be used with
There is something I liked in vim and missed even more in emacs, it is the way the current directory is set. Basically, in vim, the current directory is the directory wherein vim has been started. In emacs, by default, it is the directory of the file of the current buffer! So it is quite different. Because I always start vim at the root of my projects, I can find something similar by using some feature of
projectile so I can set the current directory to be the root directory of the project. Pretty useful!
And because I like binding, I now can search and open a file by hitting ~T— in normal mode.
(defun unified-find-file (&optional args) "Choose between projectile and helm to explore file and pass ARGS to the choice." (interactive) (if (projectile-project-p) (projectile-find-file args) (helm-find-files args))) (define-key evil-normal-state-map "T" 'unified-find-file) (add-hook 'find-file-hook (lambda () (if (and (projectile-project-p) (projectile-project-root) ) (setq default-directory (projectile-project-root)))))))
I have never taken the time to learn
magit, so my emacs/git integration is pretty simple.
First, I rely on
diff-hl to add in the fringe a diff indicator (is it a change? an addition? a deletion?). It is not always accurate, but it can be pretty convenient if like me you like to know what you did since your last commit in a glance.
git-commit package is a minor mode for when you write a commit message with emacs.
Parentheses Auto Pairing
electric-pair-mode is pretty neat as it closes for you parentheses, brackets, etc. When your cursor is in front of a closing parenthesis and you type
), it does not insert a new parenthesis but just move the cursor. This is pretty cool as you know you can continue to type as if the mode is disable. From my point of view, it is the best of both worlds.
For now, I stick to 2-spaces over tabs when I can. But there are some folks out there who do not think the way I do and I want to be able to work with them. The dtrt-indent package provides a way for emacs to guess which indentation rules to follow based on the current file.
Following leading white spaces, we can deal quite easily with trailing ones. I never found myself in a situation where I wanted tailing white spaces. I know markdown relies on them to insert newlines within a paragraph, but I find this feature quite useless to say the least. So, I can safely delete them.
Now comes the funny part. It is probably one of the part on which I have worked the most. Basically, I wanted emacs to display spaces, tabs and newlines with dedicated symbols. I also wanted it to be discrete and I mostly succeeded for that last one.
Just for the record, you can do that pretty easily with vim:
set list set listchars=eol:¬,tab:>,space:·
whitespace-mode, we can basically do the same thing, but with some restrictions. First, do not change the background of any face, because it does not work very well with
whitespace-* faces (basically, there is a good chance the default background will be applied to spaces…). Second, we cannot use
global-whitespace-mode, because once again it does not play very well with space styling. There is a hack in this configuration file to disable
whitespace-mode when the
company tool tip appears (and enable it again when it disappears). It is similar to another hack to disable/enable
fci-mode. Might as well say the way company handles the completion tool tip is broken.
Anyway, we configure
whitespace-mode here, then enable it when we need it (for
text-mode, and also
coq-mode because it looks like the latter does not use
I’ve chosen to use
company as my completion engine.
(use-package company :ensure t :config (progn (add-hook 'company-mode-hook (lambda () (setq company-idle-delay 0) (define-key evil-insert-state-map (kbd "<tab>") 'company-indent-or-complete-common) (define-key company-active-map (kbd "C-j") 'company-select-next) (define-key company-active-map (kbd "C-d") 'company-select-previous)))
Out of the box,
company does not work well with the
fill-column-indicator package. Therefore, I had to find a workaround (yet another one, I would add). Basically, it makes
company disables the fci when it needs to.
(defvar-local company-fci-mode-on-p nil) (defun company-turn-off-fci (&rest ignore) (when (boundp 'fci-mode) (setq company-fci-mode-on-p fci-mode) (when fci-mode (fci-mode -1)))) (defun company-maybe-turn-on-fci (&rest ignore) (when company-fci-mode-on-p (fci-mode 1))) (add-hook 'company-completion-started-hook 'company-turn-off-fci) (add-hook 'company-completion-finished-hook 'company-maybe-turn-on-fci) (add-hook 'company-completion-cancelled-hook 'company-maybe-turn-on-fci) (defvar my-prev-whitespace-mode nil) (make-variable-buffer-local 'my-prev-whitespace-mode) (defun pre-popup-draw () "Turn off whitespace mode before showing company complete tooltip" (if whitespace-mode (progn (setq my-prev-whitespace-mode t) (whitespace-mode -1) (setq my-prev-whitespace-mode t)))) (defun post-popup-draw () "Restore previous whitespace mode after showing company tooltip" (if my-prev-whitespace-mode (progn (whitespace-mode 1) (setq my-prev-whitespace-mode nil)))) (advice-add 'company-pseudo-tooltip-unhide :before #'pre-popup-draw) (advice-add 'company-pseudo-tooltip-hide :after #'post-popup-draw)))
In addition, I use
(use-package flycheck :ensure t :config (progn (define-fringe-bitmap 'flycheck-fringe-bitmap-ball (vector #b00000000 #b00000000 #b00000000 #b00000000 #b00000000 #b00111000 #b01111100 #b11111110 #b11111110 #b01111100 #b00111000 #b00000000 #b00000000 #b00000000 #b00000000 #b00000000 #b00000000))) (flycheck-define-error-level 'error :severity 100 :compilation-level 2 :overlay-category 'flycheck-error-overlay :fringe-bitmap 'flycheck-fringe-bitmap-ball :fringe-face 'flycheck-fringe-error :error-list-face 'flycheck-error-list-error) (flycheck-define-error-level 'warning :severity 100 :compilation-level 1 :overlay-category 'flycheck-warning-overlay :fringe-bitmap 'flycheck-fringe-bitmap-ball :fringe-face 'flycheck-fringe-warning :warning-list-face 'flycheck-warning-list-warning) (flycheck-define-error-level 'info :severity 100 :compilation-level 0 :overlay-category 'flycheck-info-overlay :fringe-bitmap 'flycheck-fringe-bitmap-ball :fringe-face 'flycheck-fringe-info :info-list-face 'flycheck-info-list-info))
A lot of major modes I use derived from
prog-mode, so I use
prog-mode-hook to avoid code duplication.
In a similar manner, I use
Irony is a pretty solid tool to work with C project. It needs additional packages to work with
(use-package irony :ensure t :config (progn (add-hook 'irony-mode-hook (lambda () (add-to-list 'company-backends 'company-irony) (define-key irony-mode-map [remap completion-at-point] 'irony-completion-at-point-async) (define-key irony-mode-map [remap complete-symbol] 'irony-completion-at-point-async) (irony-cdb-autosetup-compile-options) (flycheck-irony-setup))) (add-hook 'c-mode-hook (lambda () (irony-mode))))) (use-package company-irony :ensure t) (use-package flycheck-irony :ensure t)
I kinda like Common Lisp, and the
slime mode for Emacs is awesome. The following code snippet assumes
quicklisp (sort of package manager for CL) has been installed.
Until recently, I was using the archlinux package to get coq, but I had poor experiences with the coq-equations package. To fix that, I switch to an opam-based setup. It works well, and in particular I do not need to be root anymore to install packages. Unfortunately, this does not work out of the box with emacs + systemd.
We load proof general and we are ready to go.
(use-package proof-general :ensure t :init (custom-set-variables '(proof-splash-enable nil) '(coq-compile-before-require t) '(proof-disappearing-proofs t) '(coq-prog-name (concat (getenv "HOME") "/.opam/system/bin/coqtop")) '(coq-compiler (concat (getenv "HOME") "/.opam/system/bin/coqc")) '(coq-dependency-analyzer (concat (getenv "HOME") "/.opam/system/bin/coqdep"))) :config (progn (add-hook 'coq-mode-hook 'highlight-indent-guides-mode)))
I also use
company-coq, but I disable prettification of operators, types etc. And, of course! The
coq-mode is not using
flycheck supports coq but does not look into the
And that should be all for now.
The Elixir typical package is called
So would have guessed? I wrote at least one emacs package, in addition to this configuration. MELPA points several useful packages to be used by maintainer, so why not complying?
Cool kids use
intero now. For this to work as expected, you will need:
You can install them easily using
stack install, but this means your
PATH environment variable needs to contains the
(use-package hlint-refactor :ensure t :diminish "") (use-package intero :after hlint-refactor :ensure t :config (add-hook 'haskell-mode-hook (lambda () (intero-mode) (flycheck-mode) (hlint-refactor-mode) (flycheck-add-next-checker 'intero '(warning . haskell-hlint)) (define-key evil-normal-state-map (kbd "]") 'intero-goto-definition) (define-key evil-normal-state-map (kbd "[") 'xref-pop-marker-stack) (setq-default haskell-stylish-on-save t))))
(use-package markdown-mode :ensure t :config (progn (add-hook 'markdown-mode-hook (lambda () (autoload 'markdown-mode "markdown-mode" "Major mode for editing Markdown files" t) (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))))))
The Ocaml typical package is called
The ogmarkup is a markup language I have created for the ogma-project. It is intended to be used by storytellers to write their stories. I first define a major mode for ogmarkup.
(use-package writeroom-mode :ensure t) (defvar ogmarkup-mode-hook nil) (defun ogmarkup-mode () "Major mode for editing ogmarkup document" (interactive) (kill-all-local-variables) (setq major-mode 'ogmarkup) (setq mode-name "ogmarkup") (visual-line-mode) (text-scale-set 4) (writeroom-mode) (writeroom-adjust-width -100) ;(load-theme 'monotropic) (set-default-font "ETBookOT") (global-display-line-numbers-mode nil) (run-hooks 'ogmarkup-mode-hook)) (add-to-list 'auto-mode-alist '("\\.om\\'" . ogmarkup-mode))
To have syntax highlighting with org-mode export, we need to install
htmlize. Unfortunately, it seems like the latter does not play well with
fci-mode (yet another one, right?), but as usual, there is a hack somewhere in the internet.
(use-package htmlize :ensure t :config (progn (defun fci-mode-override-advice (&rest args)) (advice-add 'org-html-fontify-code :around (lambda (fun &rest args) (advice-add 'fci-mode :override #'fci-mode-override-advice) (let ((result (apply fun args))) (advice-remove 'fci-mode #'fci-mode-override-advice) result)))))
Cool kids use Rust too. Unfortunately, not all programming languages have there own
intero and here the setup is a bit more complicated than Haskell or C, because we rely on more packages.
The major mode is
Then we need
cargo to get an integration with the rust packages manager.
Racer is the completion engine of rust, lets configure that. The trick here is I have found a way to use
rustup to decide which toolchain to use.
(use-package racer :ensure t :config (progn (defvar rustup-default (replace-regexp-in-string "/bin/rustc\n*\\'" "" (shell-command-to-string "rustup which rustc")) "The rust toolchain currently installed according to rustup") (setq racer-cmd (executable-find "racer")) (setq racer-rust-src-path (concat rustup-default "/lib/rustlib/src/rust/src")) (add-hook 'rust-mode-hook 'racer-mode) (add-hook 'racer-mode-hook 'company-mode) (add-hook 'racer-mode-hook 'turn-on-eldoc-mode)))
company-racer builds the bruige between
We use the
ssass-mode to edit sass files.
Emacs as a RSS Reader
You know what vim users say. “Emacs is a great operating system, it only lacks a decent text editor.” Well, recently, I found myself willing to start using RSS again. lobsters is a great website and my primary source of blogposts to read, but not everything ends up there (and I like submitting links from time to time).
And that is pretty much all!