Skip to content

dominicusin/my-emacs

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

396 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

My Emacs Configuration

Introduction

The idea generating a new configuration file using org-mode and literate programming is taken from Harry Schwartz’s talk about org-mode. When Emacs is booted, all the source blocks from this file are used to generate the configuration file.

To do this, the only thing needed is to have the configuration file parsed and evaluated on startup. This can be done by moving init.el to the emacs.d directory. This can be done by executing cp init.el =/.emacs.d/init.el on the terminal. Note: this file assumes that this repository has been cloned to your home, if you are going to install it elsewhere please edit init.el to find this file. Also, please notice that this is the route assumed in all systems except for my NixOS configuration, where the location of init.el is automatically inferred by the system.

Basic Emacs Setup

My information

All the relevant and personal information that Emacs needs. If you are going to use it, needless to say to use your own information.

(setq user-full-name "Diego Vicente"
      user-mail-address "mail@diego.codes"
      calendar-latitude 40.33
      calendar-longitude -3.76
      calendar-location-name "Madrid, Spain")

Add MELPA

The main package repository for Emacs. Must have, probably all the packages that you need are already in MELPA. Also, check that it is HTTPS! We’ll elaborate on it in a bit.

;; Add MELPA
(when (>= emacs-major-version 24)
  (require 'package)
  (add-to-list
   'package-archives
   '("MELPA" . "https://melpa.org/packages/") t))

Making Emacs secure

Some safety tips regarding your editor are covered in the excellent article Your Text Editor is Malware. So, before going on with the configuration, let’s get some things straight. First of all, enable checking trust on TLS connections.

(setq tls-checktrust t)

Now that we have enabled this feature, Emacs can’t fetch HTTPS anymore: Emacs does not distribute trust root certificates. So let’s try to put a remedy for it. First, there are some things you need to install outside of Emacs: the certifi package for PyPI using python3 -m pip install --user certifi and gnutls by running brew install gnutls or apt-get install gnutls-bin. Once we have everything set, we just need to tell Emacs where to search for the tools: setting the tls-program variable.

;; This snippet is ready to work in both UNIX-like and Windows OS
(let ((trustfile
       (replace-regexp-in-string
        "\\\\" "/"
        (replace-regexp-in-string
         "\n" ""
         (shell-command-to-string (concat "python3 -m certifi"))))))
  (setq tls-program
        (list
         (format "gnutls-cli%s --x509cafile %s -p %%p %%h"
                 (if (eq window-system 'w32) ".exe" "") trustfile)))
  ;; (setq gnutls-verify-error t)
  (setq gnutls-trustfiles (list trustfile)))

And, just in case (specially since the blog post provided it), we can create a function to check if this setup is properly done:

(defun check-tls-config ()
  "Check for correctness in the TLS configuration for Emacs."
  (interactive)
  (let ((bad-hosts
         (cl-loop for bad
               in `("https://wrong.host.badssl.com/"
                    "https://self-signed.badssl.com/")
               if (condition-case e
                      (url-retrieve
                       bad (lambda (retrieved) t))
                    (error nil))
               collect bad)))
    (if bad-hosts
        (error (format "TLS misconfigured; retrieved %s ok" bad-hosts))
      (url-retrieve "https://badssl.com"
                    (lambda (retrieved) t)))))

Please note that it is crucial to have added MELPA as an HTTPS for this secure configuration to work.

Disable yes-or-no messages

For some reason, there are different types of confirmation prompts in Emacs. One of them forces you to write “yes” and the other one only to press “y”, so I disable the first type.

(fset 'yes-or-no-p 'y-or-n-p)

Disable startup message

I simply prefer to have a scratch buffer ready when I boot up, with org-mode running. Not a great Lisp developer myself (yet).

(setq inhibit-splash-screen t
      initial-scratch-message nil
      initial-major-mode 'org-mode)

Disable the warning when killing a buffer with process

When a buffer (i.e something.py) has an associated process (in that case, Python for completion), Emacs will prompt you when trying to kill it, asking for confirmation. I think it just interferes in my way, so I disable it as well.

(setq kill-buffer-query-functions
  (remq 'process-kill-buffer-query-function
         kill-buffer-query-functions))

Disable the bell

It is incredibly annoying after 20 minutes.

(setq ring-bell-function 'ignore)

Disable abbreviations prompt

Make Emacs not ask about when to save abbreviations. It is smart enough to figure it out on its own, and I don’t really rely on abbreviations that much.

(setq save-abbrevs 'silent)

Disable tabs forever and fix indentation offset

I know, everyone has a strong opinion about it. I just hate them.

(setq-default indent-tabs-mode nil)

Related to this, it is important to fix two settings: the indentation offset and the tab width. Since eventually I will open a file, I want Emacs to be able to handle them properly. However, the defaults state that the tab width is 8 characters and the indentation offset is 2. That is a problem over there, and we will use the solomonic solution: set both to 4.

(setq tab-width 4)

(defvaralias 'c-basic-offset 'tab-width)
(defvaralias 'cperl-indent-level 'tab-width)

Fix scroll

Setting this values will force one-line scrolling everywhere (mouse and keyboard), resulting most of the times in a smoother scrolling than the actual smooth scrolling.

(setq scroll-step            1
      scroll-conservatively  10000
      mouse-wheel-scroll-amount '(1 ((shift) . 1))
      mouse-wheel-progressive-speed nil
      mouse-wheel-follow-mouse 't)

Find the configuration repository

This variable defines where Emacs is supposed to be looking for things. The main reason to use this is to differentiate regular Unix-like systems and the special configuration NixOS is supposed to deliver.

(if (file-directory-p "~/nixos-setup")
	(setq configuration-dir "~/nixos-setup/my-emacs/")
  (setq configuration-dir "~/my-emacs/"))

Add utils directory

Especially after the great EmacsWiki schism, it is useful to just drop some random packages in the configuration repository and load theme from there. For that reason, I like to add that folder to the load path.

(add-to-list 'load-path (concat configuration-dir "utils"))

Set the backups folder

This feature is super useful sometimes, but it kills me to swarm my projects with foo= files. That’s why I set them to a backup directory and stop them from polluting everywhere.

(setq backup-directory-alist '(("." . "~/.emacs.d/backup"))
      backup-by-copying t
      version-control t
      delete-old-versions t
      kept-new-versions 20
      kept-old-versions 5)

Real auto-save feature

Pretty straightforward. Instead of creating an auxiliary file, just use the file itself. This may look like error-prone, but most of the times it is indeed just basically the same.

(use-package real-auto-save
  :ensure t
  :demand t
  :config (setq real-auto-save-interval 10)
  :hook (prog-mode . real-auto-save-mode))

Fill the exec-path variable

I spent too much time until I figured this out. Since I mostly use GUI Emacs, the exec-path variable is empty at start (it has not been started through shell). That’s why we need a function to loading the contents of $PATH to Emacs variable and call it at start. This function was improved and uploaded as package to MELPA, so to install it:

(use-package exec-path-from-shell
  :ensure t
  :demand t
  :config
  (setq exec-path-from-shell-variables
        '("PATH" "MANPATH" "DESKTOP_SESSION"))
  (exec-path-from-shell-initialize))

Insert new line without breaking

One of the things I really miss from vim is the shortcut o, which was used to insert a new line below the line in which the cursor is. To have the same behavior in Emacs, I found this custom function that I bound to C-o.

(defun insert-new-line-below ()
  "Add a new line below the current line"
  (interactive)
  (let ((oldpos (point)))
    (end-of-line)
    (newline-and-indent)))

(global-set-key (kbd "C-o") 'insert-new-line-below)

Move buffers around

If we want to swap buffers location in frames, there’s no fast way to do it in Emacs by default. To do it, a good option that I found is to use buffer-move package, and use these key bindings.

(use-package buffer-move
  :ensure t
  :bind (("C-c w <up>"    . buf-move-up)
         ("C-c w <down>"  . buf-move-down)
         ("C-c w <left>"  . buf-move-left)
         ("C-c w <right>" . buf-move-right)))

Redefining sentences in Emacs

Emacs allows you to move in sentences using the commands M-a and M-e (to go to the beginning or the end of the sentence). This is super useful for editing text, but Emacs assumes that you always end sentences using a period and two whitespaces, which… I actually don’t. We can override this behavior with:

(setq-default sentence-end-double-space nil)

Auto-fill comments

For our comments (only comments, not code) to be automatically filled in programming modes, we can use this function:

(defun comment-auto-fill ()
  (setq-local comment-auto-fill-only-comments t)
  (auto-fill-mode 1))

(add-hook 'prog-mode-hook 'comment-auto-fill)

Increase or decrease font size across all buffers

Extracted from a file in Steve Purcell’s Emacs configuration, it is possible to use this functions to increase or decrease the text scale in all Emacs. Specially useful for presentations, demos and other shows alike.

(defun font-name-replace-size (font-name new-size)
  (let ((parts (split-string font-name "-")))
    (setcar (nthcdr 7 parts) (format "%d" new-size))
    (mapconcat 'identity parts "-")))

(defun increment-default-font-height (delta)
  "Adjust the default font height by DELTA on every frame.
The pixel size of the frame is kept (approximately) the same.
DELTA should be a multiple of 10, in the units used by the
:height face attribute."
  (let* ((new-height (+ (face-attribute 'default :height) delta))
         (new-point-height (/ new-height 10)))
    (dolist (f (frame-list))
      (with-selected-frame f
        ;; Latest 'set-frame-font supports a "frames" arg, but
        ;; we cater to Emacs 23 by looping instead.
        (set-frame-font (font-name-replace-size (face-font 'default)
                                                new-point-height)
                        t)))
    (set-face-attribute 'default nil :height new-height)
    (message "default font size is now %d" new-point-height)))

(defun increase-default-font-height ()
  (interactive)
  (increment-default-font-height 10))

(defun decrease-default-font-height ()
  (interactive)
  (increment-default-font-height -10))

(global-set-key (kbd "C-M-=") 'increase-default-font-height)
(global-set-key (kbd "C-M--") 'decrease-default-font-height)

More intuitive regions

This makes the visual region behave more like the contemporary concept of highlighted text, that can be erased or overwritten as a whole.

(delete-selection-mode t)

Add functions to determine system

To know in which system we are running, I use these functions:

(defun system-is-mac ()
  (interactive)
  (string-equal system-type "darwin"))

(defun system-is-linux ()
  (interactive)
  (string-equal system-type "gnu/linux"))

(defun system-is-chip ()
  (interactive)
  (string-equal system-name "chip"))

Define keybindings to eval-buffer on init and open README.org

Before this magical org configuration, it was easier to reload Emacs configuration on the fly: M-x eval-buffer RET. However, now the buffer to evaluate is not this one, but .emacs.d/init.el. That’s why it’s probably a better idea to define a new keybinding that automatically reloads that buffer.

(defun reload-emacs-configuration()
  "Reload the configuration"
  (interactive)
    (load "~/.emacs.d/init.el"))

(defun open-emacs-configuration ()
  "Open the configuration.org file in buffer"
  (interactive)
    (find-file (concat configuration-dir "README.org")))

(global-set-key (kbd "C-c c r") 'reload-emacs-configuration)
(global-set-key (kbd "C-c c o") 'open-emacs-configuration)

Scroll in the compilation buffer

It is really annoying to not have the last part of the output in the screen when compiling. This automatically scrolls the buffer for you as the output is printed.

(setq compilation-scroll-output t)

Add other keybindings

Miscellaneous keybindings that don’t really fit anywhere else. Also, not only adding but removing some.

;; (global-set-key (kbd "C-c b") 'bookmark-jump)
(global-set-key (kbd "M-j") 'mark-word)

(unbind-key "C-z")

My own Emacs-Lisp functions

Clean the buffer

This function cleans the buffer from trailing whitespaces, more than two consecutive new lines and tabs.

(defun my-clean-buffer ()
  "Cleans the buffer by re-indenting, removing tabs and trailing whitespace."
  (interactive)
  (delete-trailing-whitespace)
  (save-excursion
    (replace-regexp "^\n\\{3,\\}" "\n\n" nil (point-min) (point-max)))
  (untabify (point-min) (point-max)))

(global-set-key (kbd "C-c x") 'my-clean-buffer)

Move to indentation or beginning of the line

By default, C-c a moves the cursor to the beginning of the line. If there is indentation, usually you want to move to the beginning of the line after the indentation, which is indeed bound by default to M-m. However, my muscle memory seems to be unable to learn that those are two different actions, so it’s time to use some Emacs magic. beginning-of-line-dwim takes you to the beginning of indentation, as M-m would do. If you are already there, it takes you to the absolute beginning of the line.

(defun beginning-of-line-dwim ()
  (interactive)
  "Move to beginning of indentation, if there move to beginning of line."
  (if (= (point) (progn (back-to-indentation) (point)))
      (beginning-of-line)))

(global-set-key (kbd "C-a") 'beginning-of-line-dwim)

Set the fringe as the background

This function allows to set the fringe color the same as the background, which makes it look flatter and more minimalist.

(defun set-fringe-as-background ()
  "Force the fringe to have the same color as the background"
  (set-face-attribute 'fringe nil
                      :foreground (face-foreground 'default)
                      :background (face-background 'default)))

A twist on killing lines

I have the strange (and probably detrimental) muscle memory of using kill-line as a fast method for copying and pasting. However, this implies that I find myself far too often using C-k C-y. For that reason, I just wanted to merge these two options in a single keystroke. I bind it to M-k because I usually don’t move in sentences and I definitely don’t kill sentences; your mileage may vary.

(defun dont-kill-line()
  "Copy fromm the point to the end of the line without deleting it."
  (interactive)
  (kill-line)
  (yank))

(global-set-key (kbd "M-k") 'dont-kill-line)

Graphical Interface

Disabling GUI defaults

I always use Emacs in its GUI client, but because of the visual capabilities and not the tools and bars. That’s why I like to disable all the graphical clutter.

The first line disables the menu bar, but it is commented to allow the full screen behavior in macOS.

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)

Setting default font

I really like how condensed is Iosevka, a coding typeface. Although it may look weird in the beginning, then it’s a joy to have all your code properly fitting in the screen. Lately I have also been into Liberation Mono, Fira Mono, Office Code Pro and Roboto Mono. All great, readable fonts. In the case of Iosevka, it can usually benefit for a tiny line-height increase.

However, recently I have been using Overpass Mono, developed by Red Hat. This is a beautiful typeface, really easy in the eye and much less aggressive than Iosevka in terms of how condensed it is. Give it a try!

(if (system-is-chip)
    (set-face-attribute 'default nil :font "Liberation Mono 8")
  (setq-default line-spacing 0.08))

(defun set-custom-font (frame)
  (interactive)
  (set-face-attribute 'default frame
                      :font "Overpass Mono 11"
                      :weight 'semibold)
  (set-face-attribute 'variable-pitch frame
                      :font "Overpass Mono 11"
                      :weight 'semibold)
  (set-face-attribute 'fixed-pitch frame
                      :font "Overpass Mono 11"
                      :weight 'semibold)
  (set-face-attribute 'tooltip frame
                      :font "Overpass Mono 11"
                      :weight 'semibold))

(add-to-list 'after-make-frame-functions 'set-custom-font t)

Also, due to most of the fonts looking extremely light in Emacs GUI for Linux (we humans suck at font rendering), I like to make my fonts a bit thicker by just increasing the weight a bit (self-deception back to my macOS days).

(defun thicken-all-faces ()
  "Change all regular faces to semibold and bold to extrabold."
  (interactive)
  (mapc
   (lambda (face)
     (when (eq (face-attribute face :weight) 'bold)
       (set-face-attribute face nil :weight 'extrabold))
     (when (eq (face-attribute face :weight) 'normal)
       (set-face-attribute face nil :weight 'semibold)))
   (face-list)))

(add-hook 'change-major-mode-after-body-hook 'thicken-all-faces)

Highlight changed and uncommited lines

Use the git-gutter-fringe package for that. For me it’s more than enough to have it in programming modes and in org-mode.

(use-package git-gutter
  :ensure git-gutter-fringe
  :hook ((prog-mode . git-gutter-mode)
         (org-mode . git-gutter-mode)))

Setting my favorite theme

After a long journey through several of the base16 color themes, gruvbox, doom and other color schemes, I think I have finally found my endgame one of the all-time classics: solarized-light. It has a great contrast, that does not feel tiring on the eyes after a long time using it like some other light themes, and it is stupidly pretty overall. Furthermore, it is a great Emacs package that offers a lot of customization for different packages. The only thing that actually annoys me is the fact that it changes the font in org-mode headings by default (which is that variable I am setting to t).

(defadvice load-theme (before theme-dont-propagate activate)
  (mapc #'disable-theme custom-enabled-themes))

(use-package gruvbox-theme
  :ensure t
  :config

  (defun load-dark-theme ()
	(interactive)
    (disable-theme 'solarized-light)
	(load-theme 'gruvbox t)
    (set-face-attribute 'org-block nil :background "#2A2A2A"))

  :bind(("C-c c d" . load-dark-theme)))

(use-package solarized-theme
  :ensure t
  :demand t
  :config
  (setq solarized-use-variable-pitch nil
		solarized-scale-org-headlines nil)

  (defun load-light-theme ()
	(interactive)
    (disable-theme 'gruvbox)
	(load-theme 'solarized-light t)
    (set-face-attribute 'org-block nil :background "#f9f2d9"))

  :bind(("C-c c l" . load-light-theme)))

;; Start with solarized-light by default
(load-light-theme)

Disable splitting frames to creating frames

This functionality is to be used along i3wm in order to delegate window management to i3 completely. There is also a couple of functions that must be overriden in order to make everything work seamlessly.

;; Fix quit-window definitions to get rid of buffers
(defun quit-window-dwim (&optional args)
  "`delete-frame' if closing a single window, else `quit-window'."
  (interactive)
  (if (one-window-p)
      (delete-frame)
    (quit-window args)))

(defun running-i3 ()
  (string-match-p
   (regexp-quote "i3")
   (shell-command-to-string "echo $DESKTOP_SESSION")))

(defun set-up-i3 ()
  (interactive)
  (setq-default pop-up-frames 'graphic-only
                magit-bury-buffer-function 'quit-window-dwim
                magit-commit-show-diff nil)
  (substitute-key-definition 'quit-window 'quit-window-dwim
                             global-map)
  (substitute-key-definition 'quit-window 'quit-window-dwim
                             help-mode-map)
  (substitute-key-definition 'quit-window 'quit-window-dwim
                             compilation-mode-map)
  (substitute-key-definition 'quit-window 'quit-window-dwim
                             Buffer-menu-mode-map)
  (substitute-key-definition 'quit-window 'quit-window-dwim
                             org-brain-visualize-mode-map)
  (message "Configuration for i3 applied"))

(when (running-i3)
  (set-up-i3))

This last line checks up if i3 is running and sets everything up (duh), but when Emacs is started as a daemon in systemd or before actually running i3, this check will fail. My solution is to run emacsclient -e "(set-up-i3)" in my i3 configuration, so that i3 is the one actually telling the Emacs daemon to get the proper settings.

Better usage for C-x o

Following a similar train of thought as the one in the previous snippet, C-x o default behavior can be improved with a simple check.

;; TODO: fix different workspaces problem
(setq  focus-follow-mouse t)

(defun other-window-or-frame (&optional n)
  "Switch to a different window or frame depending on the context"
  (interactive)
  (let ((count (if (eq n nil) 1 n)))
        (if (one-window-p)
            (other-frame count)
          (other-window count))))

(global-set-key (kbd "C-x o") 'other-window-or-frame)

Disable no-break underlining

I appreciate the feature, but I don’t really need it and can be annoying sometimes. I just prefer to treat those white spaces like the rest.

(set-face-attribute 'nobreak-space nil :underline nil)

Mode-line configuration

After trying most of the alternatives available to customizing the mode-line (smart-mode-line, powerline, airline, spaceline…), I finally settled with this minimal configuration. These two packages, developed by the great Jonas Bernoulli, provide a beautiful and simple mode-line that shows all the information I need in a beautiful way, being less flashy and prone to errors than other packages. The general mode-line aesthetics and distribution is provided by moody, while minions provide an on-click menu to show minor modes. While you might be thinking “on-click menu? Wasn’t the point of all this editor thing not to use the mouse?”, just notice that the previous setup used delight to hide all minor modes. This does the same thing, saving me the effort of writing :delight in most package declarations and provides a nice menu if I need to check the minor modes at some points.

(use-package minions
  :ensure t
  :config
  (setq minions-mode-line-lighter "[+]")
  (minions-mode))

(use-package moody
  :ensure t
  :config
  (moody-replace-mode-line-buffer-identification)
  (moody-replace-vc-mode)

  (defun set-moody-face (frame)
    (let ((line (face-attribute 'mode-line :underline frame)))
      (set-face-attribute 'mode-line          frame :overline   line)
      (set-face-attribute 'mode-line-inactive frame :overline   line)
      (set-face-attribute 'mode-line-inactive frame :underline  line)
      (set-face-attribute 'mode-line          frame :box        nil)
      (set-face-attribute 'mode-line-inactive frame :box        nil)))

  ;; (defun set-current-moody-face (&optional args)
  ;;   (interactive)
  ;;   (set-moody-face (selected-frame)))

  (setq-default x-underline-at-descent-line t
                column-number-mode t)

  (add-to-list 'after-make-frame-functions 'set-moody-face t))
  ;; (add-to-list 'after-make-frame-functions 'set-current-moody-face t))

Set the cursor as a vertical bar

This is less agressive than the default brick, for sure. Thanks Alex for this snippet!

(setq-default cursor-type 'bar)

Adding icons with all-the-icons

This package comes with a set of icons gathered from different fonts, so they can be used basically everywhere. At least in macOS, remember to install the necessary fonts that come bundled in the package!

(use-package font-lock+
  :demand t)

(use-package all-the-icons
  :after font-lock+
  :ensure t)

Add custom functions for changing themes

(global-set-key (kbd "C-c c l") 'load-light-theme)
(global-set-key (kbd "C-c c d") 'load-dark-theme)

Packages & Tools

which-key

A beautiful package that helps a lot specially when you are new to Emacs. Every time a key chord is started, it displays all possible outcomes in the minibuffer.

(use-package which-key
  :ensure t
  :config (which-key-mode))

dired

Emacs’ default directory system. It may feel weird first, but it is super powerful. Its main feature is that it is a buffer. Yes, ok, everything is a buffer in Emacs, but if you press C-c C-q in a dired buffer it turns into a writeable buffer, so you can edit the directory files just as a regular Emacs piece of text! I also like to have the details hidden. Also, I feel like it can be useful to comment a bit on dired-dwim-target. This enables the dired’s Do What I Mean behavior, which means that if you try to rename a file with a second buffer open, it will assume that you want to move it there. Same with copy and other operations.

(use-package dired
  :config
  (setq dired-dwim-target t)
  :hook (dired-mode . dired-hide-details-mode))

(use-package all-the-icons-dired
  :ensure t
  :hook (dired-mode . all-the-icons-dired-mode))

(use-package dired-sidebar
  :ensure t
  :bind (("C-c s" . dired-sidebar-toggle-sidebar)))

ivy

After trying ido and helm, the only step left to try was ivy. The way it works is more similar to ido: it is a completion engine but more minimalist than helm, simpler and faster.

(use-package ivy
  :ensure t
  :config
  (ivy-mode 1)
  (setq ivy-count-format "%d/%d ")

  :bind (("C-s" . swiper)
         ("C-c h f" . counsel-describe-function)
         ("C-c h v" . counsel-describe-variable)
         ("M-i" . counsel-imenu)
         :map ivy-minibuffer-map
         ("RET" . ivy-alt-done)
         ("C-j" . ivy-done)))

Apart from ivy, I also like to use other alternative packages that complement it.

(use-package ivy-rich
  :ensure t
  :after ivy
  :config
  ;; Disable TRAMP buffers extended information to prevent slowdown
  (setq ivy-rich-parse-remote-buffer nil)
  (ivy-rich-mode 1))

iy-go-to-char

Mimic vim’s f with this function. I bind it to M-m to because the default function in there (beginning-of-indentation) is not necessary after adding beginning-of-line-dwim.

(use-package iy-go-to-char
  :ensure t
  :demand t
  :bind (("M-m" . iy-go-up-to-char)
         ("M-M" . iy-go-to-char)))

ws-butler

Remove the trailing whitespaces from the lines that have been edited. The point of removing only the ones from the lines edited is to preserve useful blames and diffs in VCS.

(use-package ws-butler
  :ensure t
  :config (ws-butler-global-mode 1))

magit

A porcelain client for git. magit alone is a reason to use Emacs over vi/vim. It is really wonderful to use and you should install right now. This also binds the status function to C-x g.

(use-package magit
  :ensure t
  :config
  (setq magit-section-initial-visibility-alist '((unpushed . show)))
  (git-commit-turn-on-auto-fill)
  (add-hook 'git-commit-mode-hook (lambda () (setq-local fill-column 72)))
  (when (running-i3)
    ;; When trying to quit a frame, delete such frame
    (setq-default magit-bury-buffer-function 'quit-window-dwim
                  magit-commit-show-diff nil))
  :bind (("C-x g" . magit-status)))

(use-package magit-lfs
  :ensure t)

(use-package magit-todos
  :ensure t
  :config (magit-todos-mode))

projectile

Enables different tools and functions to deal with files related to a project. To work, it searches for a VCS and sets it as the root of a project. I have it configured to ignore all files that has not been staged in the git project.

(use-package projectile
  :ensure t
  :config
  (projectile-global-mode 1)
  (define-key projectile-mode-map (kbd "C-C p") 'projectile-command-map)
  (setq projectile-use-git-grep t
        ;; Fix for compilation command and `generic` projects
        projectile-project-compilation-cmd ""
        projectile-project-run-cmd ""))

Also, the extension counsel-projectile adds integration with ivy.

(use-package counsel-projectile
  :ensure t
  :config (counsel-projectile-mode t))

flycheck

Checks syntax for different languages. Works wonders, even though sometimes has to be configured because it really makes things slow.

(use-package flycheck
  :ensure t
  :config 
  (add-hook 'prog-mode-hook #'flycheck-mode)
  (set-face-underline 'flycheck-error '(:color "#dc322f" :style line)))

flyspell

Just like flycheck, but it checks natural language in a text. Super useful for note taking and other text edition, specially if you use Emacs for everything like I do. flyspell is installed in new Emacs versions, but there are no completion tools by default in macOS, so we need to install the aspell engine by running brew install aspell --with-lang-en

To make flyspell not clash with different syntax in the same file (like for example, LaTeX or org-mode one) we need the last hook message.

(use-package flyspell
  :ensure t
  :config
  (setq ispell-program-name "aspell"
        ispell-dictionary "english")
  (set-face-underline 'flyspell-incorrect
                      '(:color "#dc322f" :style line))

  (defun change-dictionary-spanish ()
    (interactive)
    (ispell-change-dictionary "espanol"))

  (defun change-dictionary-english ()
    (interactive)
    (ispell-change-dictionary "english"))

  :hook (org-mode . (lambda () (setq ispell-parser 'tex)))
  :bind (:map flyspell-mode-map
              ("C-c d s" . change-dictionary-spanish)
              ("C-c d e" . change-dictionary-english)))

In case I am writing a text in a different language, I can just use M-x ispell-change-dictionary. Emacs seems to have a wide enough range of dictionaries preinstalled to suit my needs. Later in the configurations, hooks are added to each of the major-modes where I want flyspell to work.

And this function prevents the spell checker to get inside source blocks in org.

(defadvice org-mode-flyspell-verify (after org-mode-flyspell-verify-hack activate)
  (let* ((rlt ad-return-value)
         (begin-regexp "^[ \t]*#\\+begin_\\(src\\|html\\|latex\\|example\\|quote\\)")
         (end-regexp "^[ \t]*#\\+end_\\(src\\|html\\|latex\\|example\\|quote\\)")
         (case-fold-search t)
         b e)
    (when ad-return-value
      (save-excursion
        (setq b (re-search-backward begin-regexp nil t))
        (if b (setq e (re-search-forward end-regexp nil t))))
      (if (and b e (< (point) e)) (setq rlt nil)))
    (setq ad-return-value rlt)))

company

It is a light-weight completion system, supposed to be faster and simpler than good ‘ol auto-complete.

(use-package company
  :ensure t
  :demand t
  :config (setq company-tooltip-align-annotations t))

lsp-mode

For a super-powered auto-completion and documentation system, it is possible to use Microsoft’s very own Language Server Protocol in Emacs. Specific configurations will be added in this block for convenience, instead of in each language’s own section. The package lsp-ui is used to give some graphic goodies when using LSP.

NOTE: due to lag when booting up a file and other drawbacks, I would say that this is not yet ready to be used as a daily workflow. Your mileage may vary.

(use-package lsp-mode
  :ensure t
  :demand t
  :config
  (setq-default lsp-highlight-symbol-at-point nil)
  (lsp-define-stdio-client lsp-python "python"
                           #'projectile-project-root
                           '("/home/dvicente/Utilities/anaconda3/bin/pyls"))
  :hook (python-mode . lsp-python-enable))

(use-package lsp-imenu
  :after lsp-mode
  :hook (lsp-after-open . lsp-enable-imenu))


(use-package lsp-ui
  :ensure t
  :config
  (setq lsp-ui-sideline-show-hover nil
        lsp-ui-sideline-ignore-duplicate t
        ;; TODO: wtf is going on with the sideline?
        lsp-ui-sideline-enable nil)
  (set-face-attribute 'lsp-ui-doc-background  nil :background "#f9f2d9")
  (add-hook 'lsp-ui-doc-frame-hook
          (lambda (frame _w)
            (set-face-attribute 'default frame :font "Overpass Mono 11")))
  (set-face-attribute 'lsp-ui-sideline-global nil
                      :inherit 'shadow
                      :background "#f9f2d9")
  :hook (lsp-mode . lsp-ui-mode))

(use-package company-lsp
  :ensure t
  :config
  (setq company-lsp-enable-snippet t
		company-lsp-cache-candidates t)
  (push 'company-lsp company-backends)
  (push 'java-mode company-global-modes)
  (push 'kotlin-mode company-global-modes))

eglot

As an alternative to lsp-mode, eglot appears as a lightweight package that allows typical interaction via Language Server Protocol. Startup times for Python are still terrible (probably python-language-server is the one to blame here) but seems more resilient to errors.

(use-package eglot
  :ensure t
  ;; Automatically recognizes python-language-server
  :hook (python . eglot))

cheat-sh

The one true definitive cheat sheet, cht.sh also provides an Emacs package to interact with it.

(use-package cheat-sh
  :ensure t
  :bind (("C-c ?" . cheat-sh)))

smartparens

Auto-close parenthesis and other characters. Useful as it seems. Also, I add a new custom pair that makes it indent and pass the closing pair when a newline is inserted right after a curly bracket. This is specially useful in C and Go.

(use-package smartparens
  :ensure t
  :config
  (sp-use-paredit-bindings)
  (add-hook 'prog-mode-hook #'smartparens-mode)
  (sp-pair "{" nil :post-handlers '(("||\n[i]" "RET"))))

avy-jump

This awesome idea turns a key into a kind of i-search with shortcuts to each of the occurrences. It may seem like a weird way to move (at least that’s what I thought first) but it can be super powerful.

(use-package avy
  :ensure t
  :bind (("M-SPC" . 'avy-goto-char-timer)
         ("M-g a" . 'avy-goto-line)))

expand-region

Expand region allows to select hierarchically different text regions. It is, in a way, a replacement for vim text objects.

(use-package expand-region
  :ensure t
  :bind (("C-=" . er/expand-region)))

term

As I started to use Emacs, my use of the terminal was drastically reduced: most of my workflow was indeed moved to Emacs bindings, one way or another: git, ag, compiling and running… Everything could be done without using a terminal! I kept a binding to eshell, just because it was kind of cute and useful to have it laying around.

However, growing up in Linux means getting your hands dirtier and becoming geekier every time. I use i3, and although it revolves around Emacs, I need a terminal from time to time: systemd, ssh for maintenance (please do use tramp the rest of the time!)… It was time to finally start using term inside Emacs. However, the defaults were far from intuitive, and although it came close, some rough corners still felt off. Through a comment in Lobsters by its author, adamrt, I discovered sane-term: a group of convenience functions that restore basic sanity to the Emacs ansi-term and provides the closer to a proper shell in Emacs!

(use-package sane-term
  :ensure t
  :config

  (defun set-up-sane-term ()
    "Fix yanking and prepare for sane-term-mode."
    (setq-local global-hl-line-mode nil)
    (define-key
      term-raw-map
      (kbd "C-y")
      (lambda ()
        (interactive)
        (term-line-mode)
        (yank)
        (term-char-mode))))

  :hook (term-mode . set-up-sane-term)
  :bind (("C-c t" . sane-term)
         ("C-c T" . sane-term-create)
         ("C-c C-j" . sane-term-mode-toggle)))

iedit

This tool allows us to edit all variable names at once just by entering a single keystroke.

(use-package iedit
  :ensure t
  :bind (("C-c i" . iedit-mode)))

yasnippets

This package is a template and snippet system for Emacs, inspired by the syntax of TextMate.

(use-package yasnippet
  :ensure t
  :config
  (add-to-list 'yas-snippet-dirs (concat configuration-dir "snippets"))
  (yas-global-mode 1)
  (advice-add 'yas--auto-fill-wrapper :override #'ignore))

In the /snippets folder in this repository you can see my snippets collection. A good guide to understand the syntax used is in the manual for YASnippet. All the snippets are local to a certain mode (delimited by the name of the folder in the collection) and their keys can be expanded using TAB.

rainbow-delimiters

This package turns the parenthesis into color pairs, which makes everything easier (specially in Lisp)

(use-package rainbow-delimiters
  :ensure t
  :hook (prog-mode . rainbow-delimiters-mode))

rmsbolt

This package allows to explore the compiled code for several languages in a similar fashion to godbolt.

(use-package rmsbolt
  :ensure t)

Docker utils

There are several packages that can make your life easier if you work with Docker and Emacs. Also, it is important to note that I use C-x c as a the command to open the prompt for Docker, so… Helm must go somewhere else.

(use-package docker
  :ensure t
  :init (setq helm-command-prefix-key "C-x M-h")
  :bind ("C-x c" . docker))

(use-package dockerfile-mode
  :ensure t)

(use-package docker-tramp
  :ensure t)

(use-package counsel-tramp
  :ensure t)

SQL console

This snippet allows a smoother usage of the built-in sql-mode with my usual tools, which are basically PostgreSQL running on docker. The configuration is simple, yet enough to turn Emacs into a comfortable and powerful SQL console.

(setq sql-postgres-login-params
      '((user :default "postgres")
        (database :default "postgres")
        (server :default "0.0.0.0")
        (port :default 5432)))

(add-hook 'sql-interactive-mode-hook
          (lambda ()
            (toggle-truncate-lines t)))

zeal documentation

zeal is a tool that is able to browse documentation and reference sets offline (out of custom docset files). This package enables a smooth integration with Emacs, allowing for easy queries programming in it.

(use-package zeal-at-point
  :ensure t
  :bind ("C-c d" . zeal-at-point))

Programming Modes

Python

Regular Python configuration

(use-package elpy
  :ensure t
  :config
  (elpy-enable)
  (add-to-list 'python-shell-completion-native-disabled-interpreters "ipython")
  (setq python-shell-interpreter "ipython"
        python-shell-interpreter-args "-i --simple-prompt")
  (add-hook 'elpy-mode-hook (lambda () (highlight-indentation-mode -1))))

Package for Python docstrings

This package adds some nice features like automatic creation of docstrings and highlighting in them. There is also another package for better highlight and indentation of the comments.

(use-package sphinx-doc
  :ensure t
  :hook (python-mode . sphinx-doc-mode))

(use-package python-docstring
  :ensure t
  :config (setq python-docstring-sentence-end-double-space nil)
  :hook (python-mode . python-docstring-mode))

Notebook configuration

(use-package ein
  :ensure t
  :config
  :hook (ein:notebook-multilang-mode
         . (lambda () (ws-butler-mode -1) (visual-line-mode))))

Nim

Nim is a compiled language which has several super interesting features, like tail-recursion optimization, Uniform Function Call Syntax, and a Pythonic style. Even though it is still relatively young and underground, it already has a solid support in Emacs with everything that there is to be asked for:

(use-package nim-mode
  :ensure t
  :hook ((nim-mode . company-mode)))
         ;; (nim-mode . nimsuggest-mode)
         ;; (nim-mode . flycheck-mode)))

Clojure

After several people recommending it, I finally wrapped my head around the idea and started to learn my bit of Clojure.

(use-package clojure-mode
  :ensure t
  :demand t)

(use-package cider
  :ensure t
  :demand t
  :config (setq cider-font-lock-dynamically t)
  :hook ((clojure-mode . cider-mode)
         (cider-repl-mode . company-mode)
         (cider-mode . company-mode)))
(require 'clojure-mode)

(require 'cider)
(add-hook 'clojure-mode-hook #'cider-mode)
(add-hook 'cider-repl-mode-hook #'cider-company-enable-fuzzy-completion)
(add-hook 'cider-mode-hook #'cider-company-enable-fuzzy-completion)

R

The quintessential programming language for statistics and numerical methods. This basic setup allows to fiddle around with R, but I am still learning it so don’t expect a great and elaborate setup.

(use-package ess
  :ensure t)

C

Since C is as straightforward as a language can probably get, only minimal configuration is required.

(add-hook
 'c-mode-hook (lambda ()
				(setq-local flycheck-gcc-language-standard "c11")))

Kotlin

I recently started to learn Kotlin, which seems like a super promising language but it still seems like an impossible task to use outside an IDE. However, thanks to Language Server Protocol, is possible to inject all hardcore IntelliJ features in Emacs with a breeze. Simply install the corresponding LSP plugin in IntelliJ and enable all important modes.

(use-package kotlin-mode
  :ensure t
  :hook (kotlin-mode . company-mode))

(use-package lsp-intellij
  :ensure t
  :after lsp-mode
  :config
  (setq lsp-intellij--code-lens-kind-face nil)
  :bind (:map kotlin-mode-map
              ("C-c k u" . lsp-intellij-run-project)
              ("C-c k c" . lsp-intellij-build-project))
  :hook (kotlin-mode . lsp-intellij-enable))

Haskell

Enable intero

In my experience, trying to deal with haskell-mode head-on is a pain in the ass, and makes programming really slow. However, intero is a package with batteries-included that works wonders. The best idea is to install it and hook it to Haskell.

(use-package intero
  :ensure t
  :init (setenv "PATH" (concat "/usr/local/bin/ghci" (getenv "PATH")))
  :hook (haskell-mode . intero-mode))

Disable soft wrapping in profiling files

GHC generates .prof files, which sometimes tend to have really long lines. To make it easier to read them, I like to disable line truncation for that file extension. The problem is that, well, is not as straightforward as you may think…

(add-hook 'find-file-hook
          (lambda ()
            (when (and (stringp buffer-file-name)
                       (string-match "\\.prof\\'" buffer-file-name))
              (toggle-truncate-lines))))

Idris

Idris is a relatively new language: purely functional, general purpose, and oriented to type development and with a type and totality checker integrated. I think that it is a super interesting experiment but a good Emacs configuration makes it look like absolute magic.

(use-package idris-mode
  :ensure t
  :bind (:map idris-mode-map
              ("C-c C-a" . idris-add-clause)
              ("C-c C-s" . idris-case-split)
              ("C-c C-f" . idris-proof-search)))

With this config, we can use a type-define-refine cycle by using the keys C-c C-a to add a clause associated with a type definition, C-c C-s to split cases and C-c C-f to fill a hole if possible; which is easier for me to remember.

Go

I am a complete noob in Go, and I have been trying to hack a bit with it lately. This is just a little disclaimer taking into account that this is just a leisure configuration and if you are going to manage code on production maybe you need a different configuration to face it.

Installing and configuring go-mode

The first recommendation for a Go major mode seems to be this one:

(use-package go-mode
  :ensure t)

Set tab width

Since Go is not precisely characterized by its open-mindedness, we have to use tabs in our code (sigh). So, at least, let’s set its size to something that can be read (4 instead 8 characters long)

(setq-default tab-width 4)

Calling go-fmt on save

We can use hooks to automatically format our code according to the guidelines:

(add-to-list 'exec-path "/Users/diego/go")
(add-hook 'before-save-hook 'gofmt-before-save)

Adding the company backend

Making auto-completion work in Go requires:

(use-package company-go
  :ensure t
  :config
  (add-hook 'go-mode-hook 'company-mode)
  (add-to-list 'company-backends 'company-go))

Viewing documentation in the minibuffer

Using go-eldoc we can see the declaration, arguments, return types, etc of the functions we are using in our code.

(use-package go-eldoc
  :ensure t
  :config (add-hook 'go-mode-hook 'go-eldoc-setup))

Enabling playgrounds in Emacs

Go playgrounds enable a kind of REPL, which is super useful when trying to learn the language and fast iterate over some code snippets.

(use-package go-playground
  :ensure t)

Rust

More or less like Go’s one, this is just a minimal configuration for the language.

Basic major mode

(use-package rust-mode
  :ensure t
  :config (setq rust-format-on-save t))

Enable flycheck

(use-package flycheck-rust
  :ensure t
  :demand t
  :config (add-hook 'flycheck-mode-hook #'flycheck-rust-setup))

Enable the company backend

(use-package racer
  :ensure t
  :demand t
  :config
  (add-hook 'rust-mode-hook #'racer-mode)
  (add-hook 'racer-mode-hook #'eldoc-mode)
  (add-hook 'racer-mode-hook #'company-mode)
  :bind (:map rust-mode-map
              ("TAB" . company-indent-or-complete-common)))

LaTeX

With this configuration, we try to aim for a WYSIWYG editor in Emacs. It requires to have AUCTeX installed.

Basic AUCTeX setup

This snippet makes that the AUCTeX macros are loaded every time the editor requires them.

(setq TeX-auto-save t)
(setq TeX-parse-self t)
(setq TeX-save-query nil)
(setq-default TeX-master nil)
;(setq TeX-PDF-mode t)

Enable flyspell in Tex edition

Add the hook to enable it by default.

(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'LaTeX-mode-hook 'flyspell-buffer)

Enable auto-fill for Tex edition

We already enabled auto-fill for comments in programming modes, but in LaTeX is more useful to directly have everything auto-filled.

(add-hook 'LaTeX-mode-hook 'auto-fill-mode)

Adding company support for Tex

Add the backend enable auto-completion for LaTeX files.

(use-package company-auctex
  :ensure t
  :config 
  (add-hook 'LaTeX-mode-hook 'company-mode)
  (company-auctex-init))

org-mode

Basic setup and other habits

Pinning org-mode

Just to make sure that it is using org from ELPA (with contrib and not any other bundled version).

(add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t)

(use-package org
  :pin org)

(use-package org-plus-contrib
  :pin org
  :after org)

Enable auto-fill-mode in Emacs

I truly believe that code and other text files have to respect a 79 characters per line bound. No, 120 is not enough. Of course, for me org-mode should also be, so we enable this behaviour to be automatic. Also, keep in mind that Emacs auto fills to 70 characters, so we have to manually set the 79 limit.

(add-hook 'org-mode-hook 'auto-fill-mode)
(setq-default fill-column 79)

Save timestamps when completing tasks

(setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)" "DROP(x!)"))
      org-log-into-drawer t)

Ensure LaTeX export options

We need to ensure that the indentation is left unaltered when exporting to LaTeX, and also to add several options for org-ref exporting to work properly

(setq org-src-preserve-indentation t)

(setq org-latex-default-packages-alist
      (-remove-item
       '("" "hyperref" nil)
       org-latex-default-packages-alist))

(add-to-list 'org-latex-default-packages-alist '("" "natbib" "") t)
(add-to-list 'org-latex-default-packages-alist
             '("linktocpage,pdfstartview=FitH,colorlinks
-linkcolor=black,anchorcolor=black,
-citecolor=black,filecolor=blue,menucolor=black,urlcolor=blue"
               "hyperref" nil)
             t)

Load languages for source blocks

Some rough collection of languages that are loaded for use in my everyday org-mode workflow.

(org-babel-do-load-languages
 'org-babel-load-languages
 '(;; (shell . t)
   (python . t)))

In the same fashion, define the safe languages that require no explicit confirmation of being executed.

(defun my-org-confirm-babel-evaluate (lang body)
  (not (member lang '("emacs-lisp" "python" "sh" "dot"))))

(setq org-confirm-babel-evaluate 'my-org-confirm-babel-evaluate)

Native TAB in source blocks

This option makes TAB work as if the keystroke was issued in the code’s major mode.

(setq org-src-tab-acts-natively t)

Display inline images

A small piece of elisp extracted from The Joy of Programming to properly display inline images in org.

(defun my/fix-inline-images ()
  (when org-inline-image-overlays
    (org-redisplay-inline-images)))

(add-hook 'org-babel-after-execute-hook 'my/fix-inline-images)
(setq-default org-image-actual-width 620)

Set source blocks to export as listings

This little snippets ensures that org will export the source blocks in the lstlisting environment and highlight the syntax when necessary.

(require 'ox-latex)
(add-to-list 'org-latex-packages-alist '("" "color"))
(add-to-list 'org-latex-packages-alist '("" "listings"))
(setq org-latex-listings 'listings)

Set the directory

I set my org-directory in Dropbox. In there is the agenda files as well.

(setq org-directory "~/Dropbox/org")

(defun org-file-path (filename)
  "Return the absolute address of an org file, given its relative name."
  (concat (file-name-as-directory org-directory) filename))

(setq org-agenda-files (list (org-file-path "planner-2019.org")))

Add a planning file per project

I like the idea of having a file in the root of each project called planning.org, in which I can put all the tasks, ideas, and other research I perform about a project. In case I add TODO entries, meetings, or other artifacts, I want them to appear in the agenda. For that reason, this functions checks for possible planning files existing in my projects.

(defun get-my-planning-files ()
  "Get a list of existing planning files per project."
  (let ((candidates (map 'list
                        (lambda (x) (concat x "planning.org"))
                        (projectile-relevant-known-projects))))
    (remove-if-not 'file-exists-p candidates)))

(defun update-planning-files ()
  "Update the org-agenda-files variable with the planning files per project."
  (interactive)
  (dolist (new-org-file (get-my-planning-files))
    (add-to-list 'org-agenda-files new-org-file)))

;; For some reason, the list seem to be overwritten during init
(add-hook 'after-init-hook 'update-planning-files)

Better RET

While reading this post in the Kitchin Research Group website, I stumbled into this package that allows a better behavior of RET in org-mode.

(use-package org-autolist
  :ensure t
  :config (add-hook 'org-mode-hook (lambda () (org-autolist-mode))))

Formatting functions

I have defined several functions to help me format text using the org markup language. When I have selected text, I can use those keybindings to surround the text with the different signs.

(defun org-mode-format-bold (&optional arg)
  "Surround the selected text with asterisks (bold)"
  (interactive "P")
  (insert-pair arg ?\* ?\*))

(defun org-mode-format-italics (&optional arg)
  "Surround the selcted text with forward slashes (italics)"
  (interactive "P")
  (insert-pair arg ?\/ ?\/))

(defun org-mode-format-tt (&optional arg)
  "Surround the selcted text with virgules (monotype)"
  (interactive "P")
  (insert-pair arg ?\= ?\=))


(add-hook 'org-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c f b") 'org-mode-format-bold)
            (local-set-key (kbd "C-c f i") 'org-mode-format-italics)
            (local-set-key (kbd "C-c f m") 'org-mode-format-tt)
            (local-set-key (kbd "C-c f *") 'org-mode-format-bold)
            (local-set-key (kbd "C-c f /") 'org-mode-format-italics)
            (local-set-key (kbd "C-c f =") 'org-mode-format-tt)))

Keybinding for org-agenda

I like to have an easy access to the agenda, so I’ll just bind it to C-c a.

(setq org-agenda-span 14)
(global-set-key (kbd "C-c a") 'org-agenda)

Graphical aspects

Multi-font configuration for org-mode

I find it interesting to use a different mode for org-mode, which is basically text, a slightly different font than the regular one for frames. The perfect choice for that is Iosevka Slab, which is beautiful Iosevka with serifs.

;; (use-package org-variable-pitch
;;   :ensure t
;;   :init (set-face-attribute 'variable-pitch nil :font "Iosevka Slab 12")
;;   :config (setq org-variable-pitch-fixed-font "Iosevka 12")
;;   :hook (org-mode . org-variable-pitch-minor-mode))

Use syntax highlight in source blocks

When writing source code on a block, if this variable is enabled it will use the same syntax highlight as the mode supposed to deal with it.

(setq org-src-fontify-natively t)

Enable org-bullets

Enable org-bullets to make it clearer. Also, the defaults are maybe a bit too much for me, so edit them.

;; (use-package org-bullets
;;   :ensure t
;;   :config
;;   (add-hook 'org-mode-hook (lambda () (org-bullets-mode)))
;;   (setq org-bullets-bullet-list
;;         '("◉" "◎" "○" "○" "○" "○")))
(setq org-hide-leading-stars t)

Custom ellipsis

Also, I don’t really like ... to be the symbol for an org ellipsis. I prefer to set something much more visual:

(setq org-ellipsis "")

Pretty symbols

This setting will make subscripts (x_{subscript}) and superscripts (x^{superscript}) appear in org in a WYSIWYG fashion.

(setq-default org-pretty-entities t)

LaTeX blocks

To preview latex fragments, we need some quick set up to obtain proper quality to read it in a Retina display.

(setq org-latex-create-formula-image-program 'dvisvgm)

Spell checking

Add spell checking by enabling flyspell in its buffers. The configuration for flyspell is above.

(add-hook 'org-mode-hook 'flyspell-mode)
;(add-hook 'org-mode-hook 'flyspell-buffer)

org-ref

org-ref is a great package that enables a great deal of references and shortcuts in org-mode when exporting to different formats like HTML or LaTeX. The configuration can be a bit of a pain in the ass:

Basic setup and default dirs

We require the packages and set the default for the bibliography notes, the main .bib bibliography and the directory where the PDFs can be downloaded to.

;; (use-package org-ref-pdf
;;   :ensure t)

;; (use-package org-ref-url-utils
;;   :ensure t)

(use-package org-ref
  :ensure t
  :config
  (setq org-ref-bibliography-notes "~/Dropbox/org/bibliography/notes.org"
        org-ref-default-bibliography '("~/Dropbox/org/bibliography/main.bib")
        org-ref-pdf-directory "~/Dropbox/org/bibliography/pdfs"
        org-ref-insert-cite-function 'org-ref-helm-insert-cite-link
        org-latex-prefer-user-labels t
        org-latex-pdf-process
        '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
          "bibtex %b"
          "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
          "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")))

We also make sure to create the directory if it does not exist

(unless (file-exists-p org-ref-pdf-directory)
  (make-directory org-ref-pdf-directory t))

Set default key in Bibtex entries

When using tools like crossref-add-bibtex-entry, we want a meaningful key to be defined in the entries. I found this method in the org-ref config file.

(setq bibtex-autokey-year-length 4
      bibtex-autokey-name-year-separator "-"
      bibtex-autokey-year-title-separator "-"
      bibtex-autokey-titleword-separator "-"
      bibtex-autokey-titlewords 2
      bibtex-autokey-titlewords-stretch 1
      bibtex-autokey-titleword-length 5)

org-brain

This packages provides a way to create a kind of wiki / concept map using org-mode. This is a second attempt at centralizing all the notes I have laying around, not associated with a concrete project.

(use-package org-brain
  :ensure t
  :demand t
  :init
  (setq org-brain-path "~/Dropbox/org/brain/")
  :config
  (setq org-id-track-globally t)
  (setq org-id-locations-file "~/.emacs.d/.org-id-locations")
  (setq org-brain-visualize-default-choices 'all)
  (setq org-brain-title-max-length 30)
  (org-brain-update-id-locations)
  :bind (("C-c b" . org-brain-visualize)))

ditaa

ditaa is a command-line utility, packed with org, that allows conversion from ascii art to bitmap. This is basically sorcery for taking notes. To enable it, we have to explicitly load it to babel:

(org-babel-do-load-languages
 'org-babel-load-languages
 '((ditaa . t)))

Diagrams using graphviz

My personal setup for generating fast diagrams in org-mode. Super useful when one needs to take notes in class or other situations where a diagram is needed.

(use-package graphviz-dot-mode
  :ensure t)

(org-babel-do-load-languages
 'org-babel-load-languages
 '((dot . t)))

R notebooks

Not a lot to say here; there is no better notebook than an org file, especially when I work on these things by myself!

(use-package ess-site
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((R . t))))

Export settings

Open PDFs using Emacs

When opening a PDF, use the included pdf-tools viewer from Emacs.

(use-package org-pdfview
  :ensure t
  :after org
  :config
  (delete '("\\.pdf\\'" . default) org-file-apps)
  (add-to-list
   'org-file-apps
   '("\\.pdf\\'" . (lambda (file link) (org-pdfview-open link)))))

Clean intermediate files

Thanks to Dani for letting me know that it is possible to automatically delete the intermediate files generated when exporting org-mode files. Just set the file extensions of all the undesired files and Emacs will take care of it.

(setq org-latex-logfiles-extensions
      '("lof" "lot" "tex=" "aux" "idx" "log" "out" "toc" "nav" "snm" "vrb"
        "dvi" "fdb_latexmk" "blg" "brf" "fls" "entoc" "ps" "spl" "bbl"))

IEEE export

For class assignments and who-knows-what in the future, I was able to integrate a IEEE Conference template in org-mode export via Latex. To use it, just include the IEEEtran class in your org file. It has not been thoroughly tested, but its headers, index, abstract and general aesthetic works perfectly out of the box.

(add-to-list 'org-latex-classes
             '("IEEEtran" "\\documentclass[11pt]{IEEEtran}"
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\paragraph{%s}" . "\\paragraph*{%s}")
               ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
             t)

Beamer export

We need to manually enable the export to Beamer option.

(use-package ox-beamer)

Reveal.js export

This package allows for simple export to Reveal slides and includes the custom style I defined in my =org-css= repository.

(use-package ox-reveal
  :load-path "~/Utilities/org-reveal"
  :config
  (setq org-reveal-root "https://diego.codes/reveal.js"
        org-reveal-extra-css "https://diego.codes/org-css/reveal-theme.css"))

My blog publishing configuration

To generate my blog, I used hugo and org-mode files. After some issues with hugo and a few of its assumptions about how I used org, but above all after being absolutely marvelled by org’s power when exporting files, I decided to migrate the setup to just a pure org exporting web-site. I crafted a custom CSS style for the exporting and developed the following configuration, which relies on the properties that org-publish provides.

(use-package ox-publish
  :init
  (setq my-blog-header-file "~/Projects/blog/org/partials/header.html"
        my-blog-footer-file "~/Projects/blog/org/partials/footer.html"
        org-html-validation-link nil)

  ;; Load partials on memory
  (defun my-blog-header (arg)
    (with-temp-buffer
      (insert-file-contents my-blog-header-file)
      (buffer-string)))

  (defun my-blog-footer (arg)
    (with-temp-buffer
      (insert-file-contents my-blog-footer-file)
      (buffer-string)))

  (defun filter-local-links (link backend info)
    "Filter that converts all the /index.html links to /"
    (if (org-export-derived-backend-p backend 'html)
        (replace-regexp-in-string "/index.html" "/" link)))

  :config
  (setq org-publish-project-alist
        '(;; Publish the posts
          ("blog-notes"
           :base-directory "~/Projects/blog/org"
           :base-extension "org"
           :publishing-directory "~/Projects/blog/public"
           :recursive t
           :publishing-function org-html-publish-to-html
           :headline-levels 4
           :section-numbers nil
           :html-head nil
           :html-head-include-default-style nil
           :html-head-include-scripts nil
           :html-preamble my-blog-header
           :html-postamble my-blog-footer
           )

          ;; For static files that should remain untouched
          ("blog-static"
           :base-directory "~/Projects/blog/org/"
           :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|eot\\|svg\\|woff\\|woff2\\|ttf"
           :publishing-directory "~/Projects/blog/public"
           :recursive t
           :publishing-function org-publish-attachment
           )

          ;; Combine the two previous components in a single one
          ("blog" :components ("blog-notes" "blog-static"))))

  (add-to-list 'org-export-filter-link-functions 'filter-local-links))

Other Major Modes

markdown-mode

Even though I am not a great fan, I am often forced to write Markdown more often that I would like to. This is the minimal configuration I have for such mode.

(use-package markdown-mode
  :ensure t
  :hook (markdown-mode . visual-line-mode))

csv-mode

Data science is hard, ok? You have to deal with huge and not super friendly files. However, being stubborn and geek as a I am, I try to get into Excel/Calc as few times as I can. The best approach (specially when using a second screen, bigger than a laptop) was simply to use a mode that allows for markup and alignment of .csv files. However, since it was sometimes dealing with files too big to be completely aligned, I added a function to only align the visible part of the file.

(use-package csv-mode
  :ensure t
  :config
  (setq csv-align-padding 2)

  (defun csv-align-visible ()
    "Align only visible entries in csv-mode."
    (interactive)
    (csv-align-fields nil
     (window-start (selected-window))
     (window-end (selected-window)))
    (message "Aligned visible fields only. Press C-c C-w to align again."))

  ;; C-c C-a is already bound to align all fields, but can be too slow.
  :bind (:map csv-mode-map ("C-c C-w" . 'csv-align-visible))
  :hook (csv-mode . csv-align-visible))

pdf-tools

Although it supposedly works only on GNU/Linux, pdf-tools is a package that actually allows Emacs to be a PDF viewer. Actually, it is probably the best PDF viewer for Linux out there (maybe we could negotiate if Okular is). It allows to read, copy, select and annotate PDFs from Emacs.

(use-package pdf-tools
  :ensure t
  :demand t
  :config
  (pdf-tools-install t)
  (setq pdf-annot-activate-created-annotations t)
  :bind (:map pdf-view-mode-map
              ("C-s" . isearch-forward)
              ("h" . pdf-annot-add-highlight-markup-annotation)
              ("t" . pdf-annot-add-text-annotation)
              ("D" . pdf-annot-delete)))

gnus

Included with Emacs comes gnus, a fully fledged mail client and RSS reader. It is a bit harsh to get started on it but it is one of the most powerful pieces of software for everyday use I can think of. One special thing about gnus is that uses a separate configuration file, which is also contained in this repository. Make sure to run the following command to symlink it to an appropriate location:

ln -s gnus.el ~/.gnus.el

mu4e

After trying to love gnus, I am really having a hard time getting around the cumbersome interface and how slow it can be. For that reason, I also want to have mu / mu4e around. I guess I am not the kind of power user that takes full advantage of gnus.

(use-package mu4e
  :load-path "/usr/share/emacs/site-lisp/mu4e"
  :demand t
  :config
  (require 'shr)

  (setq send-mail-function 'smtpmail-send-it
        message-send-mail-function 'smtpmail-send-it
        smtpmail-auth-credentials (expand-file-name "~/.authinfo")
        ;; mu4e-sent-messages-behavior 'delete
        message-kill-buffer-on-exit t
        mu4e-confirm-quit nil
        mu4e-completing-read-function 'ivy-completing-read
        mu4e-compose-format-flowed t
        message-signature-file "~/my-emacs/utils/signature"
        ;; Don't get too clever showing html
        shr-use-colors nil
        shr-use-fonts nil
        shr-width 79
        mu4e-view-prefer-html t
        ;; Define the default folders
        mu4e-sent-folder   "/Fastmail/Sent"
        ;; mu4e-drafts-folder "/Fastmail/Drafts"
        mu4e-trash-folder  "/Fastmail/Trash"
        ;; Define custom shortcuts
        mu4e-maildir-shortcuts '(("/Fastmail/INBOX" . ?i)))

  ;; Override the default bookmark list
  (setq mu4e-bookmarks
    `( ,(make-mu4e-bookmark
         :name  "Unread messages"
         :query "flag:unread AND NOT flag:trashed AND NOT maildir:/Fastmail/Spam"
         :key ?u)
       ,(make-mu4e-bookmark
         :name "Today's messages"
         :query "date:today..now"
         :key ?t)
       ,(make-mu4e-bookmark
         :name "Last 7 days"
         :query "date:7d..now"
         :key ?w)
       ,(make-mu4e-bookmark
         :name "Inbox"
         :query "maildir:/Fastmail/INBOX"
         :key ?i)))

  :bind (("C-c m" . mu4e))
  :hook (mu4e-compose-mode . flyspell-mode))

This basic configuration also includes a notification daemon when I get new email in the mode-line.

(use-package mu4e-alert
  :ensure t
  :after mu4e
  :config
  (setq mu4e-alert-modeline-formatter
        'mu4e-alert-custom-mode-line-formatter)
  (mu4e-alert-set-default-style 'libnotify)
  (setq mu4e-alert-interesting-mail-query
        "flag:unread AND NOT flag:trashed AND NOT maildir:/Fastmail/Spam")
  :hook (after-init . mu4e-alert-enable-mode-line-display))

(defun mu4e-alert-custom-mode-line-formatter (mail-count)
  "Custom formatter used to get the string to be displayed in the mode-line.
Uses Font Awesome mail icon to have a more visual icon in the display.
MAIL-COUNT is the count of mails for which the string is to displayed"
  (when (not (zerop mail-count))
    (concat " "
            (propertize
             ""
             ;; 'display (when (display-graphic-p)
             ;;            display-time-mail-icon)
             'face display-time-mail-face
             'help-echo (concat (if (= mail-count 1)
                                    "You have an unread email"
                                  (format "You have %s unread emails" mail-count))
                                "\nClick here to view "
                                (if (= mail-count 1) "it" "them"))
             'mouse-face 'mode-line-highlight
             'keymap '(mode-line keymap
                                 (mouse-1 . mu4e-alert-view-unread-mails)
                                 (mouse-2 . mu4e-alert-view-unread-mails)
                                 (mouse-3 . mu4e-alert-view-unread-mails)))
            (if (zerop mail-count)
                " "
              (format " [%d] " mail-count)))))

Also, I define a custom function so that updating the mail is possible just by pinging the Emacs daemon. I have this function called as a post-hook every time the offlineimap is called.

(defun update-mail-in-server ()
  "Check for mail and update the mode line icon."
  (interactive)
  (mu4e-update-mail-and-index t)
  (mu4e-alert-enable-mode-line-display)
  ;; Clear echo area in 2 seconds after update
  (run-with-timer 2 nil (lambda () (message nil))))

org-mime is a package that enables HTML email writing using org-mode on Emacs’ side.

(use-package org-mime
  :ensure t
  :after org
  :config
  (setq-default composing-html-mail nil)

  (defun compose-org-mail ()
    "Create a new org scratch buffer to compose an HTML mail."
    (interactive)
    (let ((draft-buffer (generate-new-buffer "*org-draft*")))
      (with-current-buffer draft-buffer
        (org-mode)
        (insert "?header")
        (yas/expand))
      (display-buffer draft-buffer nil t)
      (setq composing-html-mail t)))

  (defun htmlize-org-mail ()
    "When in an org mail, htmlize it."
    (interactive)
    (when composing-html-mail
      (setq composing-html-mail nil)
      (org-mime-org-buffer-htmlize)))

  :hook (org-ctrl-c-ctrl-c . htmlize-org-mail))

bbdb: The Insidious Big Brother’s Database

One of the best known packages for contact management in Emacs.

(use-package bbdb
  :ensure t
  :after mu4e
  :config
  ;; (require 'bbdb-loaddefs)
  (autoload 'bbdb-insinuate-mu4e "bbdb-mu4e")
  (bbdb-initialize 'message 'mu4e)
  (setq bbdb-file (concat org-directory "/bbdb")
        bbdb-mail-user-agent 'message-user-agent
        mu4e-view-mode-hook '(bbdb-mua-auto-update visual-line-mode)
        mu4e-compose-complete-addresses nil
        bbdb-mua-pop-up t
        bbdb-mua-pop-up-window-size 5
        bbdb-phone-style nil
        org-bbdb-anniversary-field 'birthday))

Also, a secondary package takes advantage of counsel integration when completing addresses (much better idea than the default search).

(use-package counsel-bbdb
  :ensure t
  :config
  :bind (:map mu4e-compose-mode-map
              ("TAB" . counsel-bbdb-complete-mail)))

erc

erc is a IRC client for Emacs. It is a wonderful tool worth checking out, and requires really little configuration to make to be great.

Hide messages from inactive people

This snippet hides all the IRC messages that notify someone has joined, parted or quitted if that user has been inactive for more than half an hour.

(setq erc-lurker-hide-list '("JOIN" "PART" "QUIT"))
(setq erc-lurker-threshold-time 1800)

Use the proper default nickname

erc suggests a default nickname when logging in. agis is my username, if you want to set another one just change that argument.

(setq erc-nick "dvicente")

elfeed

Next step after reading mail in Emacs, is to read more things. Down the rabbit hole, we can see elfeed, a RSS reader. This configuration also uses an org file to store the subscriptions (which is darn fine for me).

(use-package elfeed
  :ensure t
  :config
  (setq elfeed-db-directory "~/Dropbox/org/rss/.elfeed"))

(use-package elfeed-org
  :ensure t
  :after elfeed
  :config
  (elfeed-org)
  (setq rmh-elfeed-org-files (list "~/Dropbox/org/rss/feeds.org")))

About

My Emacs configuration

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Emacs Lisp 82.9%
  • YASnippet 17.1%