Emacs Configuration

Emacs, the editor of a lifetime?

Of course I tried multiple editors over the years, from the TurboPascal, TurboC, Microsoft Visual editions, Atom, Notepad, Notepad++, Sublime, etc, and I can’t remember when the trigger happened to stick to Emacs.

I suspect, after a while, one is fade-up to always learn a new interface every couple of years and Emacs has been there for decades, is multi-platform, quite light with the daemon-mode, and does the job perfectly.

Sooner or later, we quickly find comfort in being able to perform most of the common actions by just starting a new buffer to read RSS, code, send an email, manage a project status or simply take notes.

Like every new beginner I started with M-x customize and slowly after becoming confident, I moved to the setup of my dot Emacs file.

Could it be that Emacs is the Editor of a/my lifetime?

My dot file ~/.emacs.d/init.el

Let me share with you a part of my personal config and the customization of my top packages. It is published in the hope it might help someone to start or enhance his/her personal setup.

I prefer to use (setq ...) rather than Customize to change the value of a variable as I find it easier to replicate changes to a different machine.

Basic interface changes

(global-font-lock-mode t)             ;; add colors
(setq font-lock-maximum-decoration t) ;; add maximum coloration
(setq column-number-mode t)           ;; add the column number
(transient-mark-mode t)
(setq tab-width 8)                    ;; set tab to 8 spaces
(menu-bar-mode -1)                    ;; remove the menu bar
(scroll-bar-mode -1)                  ;; remove the right scroll bar
(tool-bar-mode -1)                    ;; remove the tool bar
(setq inhibit-splash-screen t)        ;; Remove splash screen
(set-default 'indicate-empty-lines t) ;; Explicitly show the end of a buffer

;;to display time
(setq display-time-24hr-format t)
(display-time)


;; Calendar
(setq calendar-week-start-day 1)        ; Weeks start on monday
(setq calendar-date-style 'european)

;; enable the copy-paste between X applications
(setq x-select-enable-clipboard t)

;; suppress emacs backup files
(setq make-backup-files nil)

;; yes or no replaced by y or n as typing 3 letters each time is annoying
(defalias 'yes-or-no-p 'y-or-n-p)

Package manager

Define the repositories for our packages and install use-package if not already present (see: use-package).

;; package manager
(require 'package)
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

;;; Bootstrapping use-package
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

Imenu

Quickly jump in a file to the right chapter, section, title or function …. Most of the Major mode work with Imenu.

;; remap imenu
(global-set-key (kbd "M-i") 'imenu)

Make buffer unique

Sometimes working with files which have the same name makes changing buffer confusing, so we add uniqueness

(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward)

Theme

These days I like to use the Modus-operandi from Protesilaos Stavrou https://protesilaos.com/emacs/modus-themes. The color contract is really very pleasant on the eyes.

(use-package modus-themes
  :ensure t
  :config
  (load-theme 'modus-operandi t)

Default font

I prefer the Inconsolata monospaced font when reading code. And the monospace style makes table alignment always perfect for text files because all characters have the same width.

More info available at the following sources

(add-to-list 'default-frame-alist
             '(font . "Inconsolata-12"))

Magit to manage GIT repository.

This application is really amazing to manage GIT repositories.

(use-package magit
  :defer t
  :ensure t
  :bind ("C-x g" . magit-status))

(use-package magit-todos
   :ensure t)

Highlight TODOs in code.

Very convenient to highlight comments in the code for any part which needs enhancement or as to be reviewed.

(use-package hl-todo
  :defer t
  :ensure t
  :config
  (add-hook 'c-mode-hook #'hl-todo-mode))

On-the-fly syntax check

(use-package flycheck
  :ensure t
  :defer 2
  :config
  (setq flycheck-highlighting-mode 'lines)
  (add-hook 'after-init-hook #'global-flycheck-mode)
  (setq flycheck-global-modes '(not dir-locals-mode
                                    text-mode
                                    org-mode
                                    vterm-mode))
  :custom
  (flycheck-display-errors-delay .3))

Markdown

Useful to update Markdown files.

(use-package markdown-mode
  :defer t
  :ensure t
  :config
  (add-hook 'markdown-mode-hook 'turn-on-flyspell))

Read ebook from within Emacs

;; read .epub file.
(use-package nov
  :defer t
  :ensure t
  :mode
  ("\\.epub$" . nov-mode))

GPG Encryption

When opening a gpg file, asks for the password in the minibuffer

(setf epa-pinentry-mode 'loopback)

Undo-tree to undo/redo changes

Enable easy visual rollback. This is a really powerful add-on which allows you to recover not only the previous state of a document, but also any previous ones.

;; enable undo-tree mode
(use-package undo-tree
  :ensure t
  :config
  (global-undo-tree-mode)
  (setq undo-tree-auto-save-history t)
  (setq undo-tree-visualizer-diff t)
  :bind (("C-z" . undo-tree-undo)
         ("C-S-z" . undo-tree-redo)))

And it can be done visually.

Auto complete with company

(use-package company
  :ensure t
  :config
  (setq company-idle-delay 0)
  (setq company-minimum-prefix-length 3)

  ;; 2022-Jun-28 try to set company-backends manually to include company-yasnippet completion
  ;; especially for org and emails.  
  (setq company-backends
        '((company-files :with company-yasnippet)
          (company-capf :with company-yasnippet)
          (company-dabbrev-code company-gtags company-etags company-keywords :with company-yasnippet)
          (company-dabbrev :with company-yasnippet)))

  
  (global-company-mode t))

Coding in C

(use-package cc-mode
  :ensure nil
  :defer
  :config
  (setq c-default-style "gnu")
  (setq indent-tabs-mode t)
  (setq show-trailing-whitespace t)
  (setq display-line-numbers 'relative)

  (setq-default c-doc-comment-style
                '((c-mode . doxygen)))
  
  (font-lock-add-keywords 'c-mode
                          '(("\\(\\w+\\)\\s-*\("
                             (1 'scouppey-C-function-name-face)))
                          t)

  (add-hook 'c-mode-common-hook #'hs-minor-mode)

  (add-hook 'c-mode-hook
          (lambda ()
            (local-set-key (kbd "RET") 'newline-and-indent)
            ))
)

(global-set-key [f9] 'compile)

And the famous package to quickly jump to referenced functions with the standard key M-. and M-,

(use-package ggtags
  :ensure t
  :config
  (add-hook 'c-mode-hook #'ggtags-mode)
)

Clean the extra spaces

Used to clean the extra spaces at the end of the line and on new “empty” lines.

(use-package whitespace-cleanup-mode
      :ensure t
      :config
      (add-hook 'LaTeX-mode-hook 'whitespace-cleanup-mode)
      (add-hook 'org-mode-hook 'whitespace-cleanup-mode)
      (add-hook 'c-mode-hook 'whitespace-cleanup-mode))

Checking and Correcting Spelling

Useful to check the spelling of a single highlighted word with C-M-i

(use-package ispell
  :config
  (setq ispell-program-name "aspell")
  (setq ispell-list-command "list") ; for flyspell
  (setq ispell-extra-args '("--sug-mode=fast")) ; can be fast or ultra
  (setq ispell-local-dictionary "english"))

Here is an example of `Hello’ with a typo. typo in hello

Ibuffer as default

Replaces the default list-buffer with the new ibuffer

(defalias 'list-buffers 'ibuffer)

Org-mode configuration

It is said that lots of users switch to Emacs for Org-mode. It is true this is an amazing add-on for daily production. I use it to track my TO-DOs, time spend on a task and take notes.

(use-package orgalist
  :ensure t)
  
(use-package org
  :ensure t
  :mode ("\\.org\\'" . org-mode)
  :bind (("C-c a" . org-agenda)
         ("C-c c" . org-capture))
  :config
  ;; org-mode agenda file for todo
  (setq org-agenda-files (list "~/file-to-agenda/agenda.org"))
  (setq org-log-done 'time) ;; capture timestamp when done
  (setq org-todo-keywords
        '((sequence "TODO(t)" "INPROGRESS(p)" "DONE(d)")))
  (setq org-todo-keyword-faces
        '(("INPROGRESS" . (:foreground "light blue" :weight bold))))
  (setq org-src-fontify-natively t)  ;; fontify code in code blocks

  (setq org-clock-persist 'history)
  (org-clock-persistence-insinuate)

  (setq org-agenda-archives-mode t)
  (setq org-use-speed-commands t)

  (setq org-capture-templates
        '(("t" "todo" entry (file "~/Documents/refile.org")
           "* TODO %?\nSCHEDULED: %t" :empty-lines 1 :prepend t)

          ("a" "activity" entry (file+olp+datetree  "~/Documents/journal.org")
           "* INPROGRESS %^{Description} %^G\nSCHEDULED: %t\nAdded: %U\n%?"
           :empty-lines 1
           :clock-in t
           :clock-keep t
           :tree-type week)
          ))


  ;; when using org Motion C-c C-j quickly jump to headline
  ;; C-u C-c C-j will use the other interface
  (setq org-outline-path-complete-in-steps nil)
  (setq org-goto-interface 'outline-path-completion)

  (setq org-clock-idle-time 15)
  (setq org-agenda-clock-consistency-checks
        '(:max-duration "4:00" :min-duration 0 :max-gap 0 :gap-ok-around ("4:00")))

  (setq org-agenda-span 'day)
  (setq org-agenda-archives-mode t)
  (setq org-use-speed-commands t)

  (setq org-refile-targets '((nil :maxlevel . 9)
                             (org-agenda-files :maxlevel . 2))))

Org time computation

Rather than the absolute time between 2 dates, I prefer to compute the working time in working hours. Of course one would adjust on what is the number of hours composing a working day.

(use-package org-duration
  :config
    (setq org-duration-units
        `(("min" . 1)
          ("h" . 60)
          ("d" . ,(* 60 8))
          ("w" . ,(* 60 8 5))
          ("m" . ,(* 60 8 5 4))
          ("y" . ,(* 60 8 5 4 10))))
  (org-duration-set-regexps))

Note taking with Org-roam

Used for my plain-text knowledge management system, which is of course a GIT repository synced between my different devices.

So far, with the few notes I have, the research is fast and it always comes handy to look for information.

(use-package org-roam
  :ensure t
  :after org
  :init
  (setq org-roam-v2-ack t)
  :config
  (setq org-roam-directory "~/roam-notes")
  (setq org-roam-completion-everywhere t)

  (setq org-roam-capture-templates
        '(("d" "default" plain
           "* Summary\n\n%?\n\n* References:\n\n"
           :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
           :unnarrowed t)))

  (org-roam-setup)
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n t" . org-roam-tag-add)
         :map org-mode-map
         ("C-M-i" . completion-at-point)))

File management with dired

a will open the file or the directory in the same buffer

(put 'dired-find-alternate-file 'disabled nil)

Parenthesis highlighting

Highlights the parent parenthesis when inside of a group.

(show-paren-mode 1)

And automatically insert the matching pair for (), [], {}

(setq skeleton-pair t) 

Latex with AucTex

Latex is a powerful markup language used to publish amazing professional documentation or publications.

I find it indispensable for large projects as it allows, similarly with writing a program, a split of the documentation into sections, references and style.

An excellent way to focus on the content rather than the style or fighting with lists or numbering, compared to other word processor applications.

(use-package reftex
  :ensure t
  :defer t
  :config
  (setq reftex-plug-into-AUCTeX t
        reftex-toc-split-windows-horizontally t
        reftex-toc-include-file-boundaries t))

(use-package tex-site
  :ensure auctex
  :config
  ;; some-config-here
  (setq TeX-auto-save t)
  (setq TeX-parse-self t)

  (use-package latex
    :defer t
    :bind
    (:map LaTeX-mode-map
          ("<C-tab>" . outline-toggle-children))
    :config
    (setq TeX-PDF-mode t)
    (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
    (add-hook 'LaTeX-mode-hook 'turn-on-flyspell)
    (add-hook 'LaTeX-mode-hook 'outline-minor-mode)))

Highlight the line my cursor is on

It is easy to loose the small blinking cursor and the highlight of the full line always helps me.

(global-hl-line-mode 1)

Quickly insert date

When not in org-mode and especially for quick updates to be added to a changelog, I have the convenient lisp function to insert the date in 3 different formats.

;; Usage:
;;   ‘C-c d’: 2004-Apr-13
;;   ‘C-u C-c d’: 13.04.2004
;;   ‘C-u C-u C-c d’: Sunday, 13. April 2004

(defun insert-date (prefix)
  "Insert the current date. With prefix-argument, use ISO format. With
   two prefix arguments, write out the day and month name."
  (interactive "P")
  (let ((format (cond
                 ((not prefix) "%Y-%b-%d")
                 ((equal prefix '(4)) "%d.%m.%Y")
                 ((equal prefix '(16)) "%A, %d. %B %Y")))
        (system-time-locale "en_GB"))
    (insert (format-time-string format))))

(global-set-key (kbd "C-c d") 'insert-date)

Snippets

As soon as I have to provide similar content or generate a custom boilerplate text multiple times, I create a new snippet for the major mode I am working with.

(use-package yasnippet
  :ensure t
  :diminish yas-minor-mode
  ;; (add-hook 'message-mode-hook 'yas-minor-mode)
  :config
  (use-package yasnippet-snippets
    :ensure t)

Quickly switch between windows

A handy shortcut to switch directly to a specific window without having to use the command C-x o or M-x other-window multiple times in a cyclic order.

M-[ will display the window number in the top left corner of each window.

(use-package ace-window
  :ensure t
  :bind ("M-[" . 'ace-window))

But it does way more if needed …
You can find more info on the official site of abo-abo https://github.com/abo-abo/ace-window.

Quickly move the cursor to a word

Rather than clicking with the mouse or using multiple times C-n, C-p … the avy package helps to jump right at the work position.

(use-package avy
  :ensure t
  :bind ("C-:" . avy-goto-char-2))

The front-end completion

A completion framework with some sort of fuzzy matching is for me a big plus. I suspect using Emacs without any of them would be tedious.

(use-package ivy
  :ensure t
  :defer 0.1
  :diminish
  :bind (("C-c C-r" . ivy-resume)
         ("C-x B" . ivy-switch-buffer-other-window))
  :config
  (ivy-mode)
  (setq ivy-count-format "(%d/%d) ")
  (setq ivy-use-virtual-buffers t))
(use-package counsel
  :ensure t
  :after (ivy org org-agenda)
  :bind (("C-c g" . counsel-git)
         ("C-c j" . counsel-git-grep)
         ("C-c k" . counsel-ag)
         ("C-x l" . counsel-locate)
         :map org-mode-map
         ("C-c C-q" . counsel-org-tag)
         :map org-agenda-mode-map
         ("C-c C-q" . counsel-org-tag))
  :init (counsel-mode)
  :config
  (setq counsel-find-file-ignore-regexp
        (regexp-opt (append completion-ignored-extensions
                            '(".~undo-tree~")))))

To display more info in the ivy buffer. Screenshots

(use-package ivy-rich
  :ensure t
  :after ivy
  :init (ivy-rich-mode 1))

The alternative to isearch, which allows to quickly filter text in a buffer.

(use-package swiper
  :ensure t
  :after ivy
  :bind (("C-s" . swiper)
         ("C-r" . swiper)))

Select the right snippet with ivy.

(use-package ivy-yasnippet
  :ensure t
  :after (ivy)
  :bind ("C-c y" . ivy-yasnippet))

RSS new reader

Elfeed is perfect to quickly read news like newsgroups without having to switch application or go on the web.

(use-package elfeed
  :ensure t
  :defer t
  :config
  (setq elfeed-use-curl t)
  (setq elfeed-sort-order 'ascending)
  (setq elfeed-feeds '(("https://xkcd.com/atom.xml" comics)
                       ("https://planet.emacslife.com/atom.xml" news))))

Quickly highlight a text block

(use-package boxquote
  :ensure t)

Example:

 ,----[ title ]
 | Suspendisse metus libero, tempus id luctus in, 
 | lobortis elementum ipsum. Sed erat leo, 
 | porttitor eu maximus sit amet, rutrum eu dolor.
 | Mauris ac neque facilisis augue maximus placerat.
 `----

tree-sitter

Emacs29+ integrates tree-sitter support, a powerful library to enhance major mode with more precise syntax highlighting, indentation, Imenu function jump.

Emacs29+ ships already defined major modes for tree-sitter, such as: C, C++, Java, Rust, Go, Python, Javascript, Typescript, JSON, YAML, TOML, CSS, Bash, Dockerfile, …

(use-package treesit
  :init
  (setq treesit-language-source-alist
        '((bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
          (c . ("https://github.com/tree-sitter/tree-sitter-c"))
          (css . ("https://github.com/tree-sitter/tree-sitter-css"))
          (html . ("https://github.com/tree-sitter/tree-sitter-html"))  ; emacs 30.1
          (toml . ("https://github.com/tree-sitter/tree-sitter-toml"))
          (json . ("https://github.com/tree-sitter/tree-sitter-json"))))

  :config
  (add-to-list 'major-mode-remap-alist '(bash-mode . bash-ts-mode))
  (add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode))
  (add-to-list 'major-mode-remap-alist '(css-mode . css-ts-mode))
  (add-to-list 'major-mode-remap-alist '(json-mode . json-ts-mode))
  (add-to-list 'major-mode-remap-alist '(toml-mode . toml-ts-mode)))

Once defined, the language grammars can be installed with

M-x treesit-install-language-grammar

or

(mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))

The compiled libraries then get installed in the .emacs/tree-sitter folder:

.emacs.d/tree-sitter/
|-- libtree-sitter-bash.so
|-- libtree-sitter-c.so
[...]
|-- libtree-sitter-html.so
`-- libtree-sitter-toml.so

It is possible to check the grammar was correctly installed with

(treesit-language-available-p 'c)
t

External references

  1. https://www.gnu.org/software/emacs/download.html