Ultimate emacs setup with documentation in org mode

Supratim Samanta
10 min readSep 10, 2021

--

Emacs is the tool to do anything. It has been here since 1976 and still going strong. They regularly release new builds for all the platforms and keep on adding features.

Photo by Karolina Grabowska from Pexels

The plugin system of Emacs is so vast and robust, that you can literally do anything you want inside Emacs. I have heard someone say this, which resonates within me too, and I understand from where the sentiment is coming from:

I don’t care about the system, to me it’s just jump box to my emacs.

Funny… if you get the pun.

I use emacs for various reasons, predominantly:

  • Programming is different languages
  • Documentation with org mode
  • Reading feeds with elfeed
  • Git activities with magic
  • Navigate documents with evil mode. etc.

Here in this post I will discuss my emacs setup and how to configure it, if you want.

The project is available here.

Prerequisites:

  • Familiarity with emacs is required as these are advanced configuration
  • Knowledge on emacs configuration

Install and configure:

  • Clone the repo
  • Copy emacs/init.el and emacs/configuration.org to ~/.emacs.d
  • Install emacs from https://www.gnu.org/software/emacs/download.html
  • Open emacs. The copied files will be read by emacs and will create several other files inside ~/emacs.d

Configuration settings:

Now let’s go through the configuration settings. These is coming form the configuration.org file. If you open it in GitHub, you will see the same documentation as below(One of the best features of org mode)

Startup

Personalization

(setq initial-scratch-message
(concat
";; This buffer is for text that is not saved, and for Lisp evaluation.\n"
";; To create a file, visit it with C-x C-f and enter text in its buffer.\n"
";;\n"
";; __ __ _ \n"
";; \\ \\ / / | | \n"
";; \\ \\ /\\ / /__| | ___ ___ _ __ ___ ___ \n"
";; \\ \\/ \\/ / _ \\ |/ __/ _ \\| '_ ` _ \\ / _ \\ \n"
";; \\ /\\ / __/ | (_| (_) | | | | | | __/_ \n"
";; \\/ \\/ \\___|_|\\___\\___/|_| |_| |_|\\___(_)\n"))
;; Leave this here, or package.el will just add it again.

File limits

(setq gc-cons-thresold 50000000)
(setq large-file-warning-thresold 100000000)

Encoding

(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)

Configure use-package

(require 'use-package-ensure)
(setq use-package-always-ensure t)

Always compile packages, and use the newest version available.

(use-package auto-compile
:config (auto-compile-on-load-mode))
(setq load-prefer-newer t)

Global

Key bindings

(global-set-key (kbd "C-x k") 'kill-this-buffer)
(global-set-key (kbd "C-w") 'backward-kill-word)
(global-set-key (kbd "M-o") 'other-window)

Remap when working in terminal Emacs.

(define-key input-decode-map "\e[1;2A" [S-up])

Use M-/ for company completion.

(global-set-key (kbd "M-/") 'company-complete-common)

Use C-c r for elfeed

(global-set-key (kbd "C-c r") 'elfeed)

Miscellaneous

(fset 'yes-or-no-p 'y-or-n-p)(global-auto-revert-mode t)
(add-hook 'before-save-hook 'whitespace-cleanup)

Personal Information

(setq user-full-name "Supratim Samanta"
user-mail-address "supratim.iee23.ju2005@gmail.com")

Visual configurations

Title and menu bar

(menu-bar-mode -1)
(toggle-scroll-bar -1)
(tool-bar-mode -1)
(blink-cursor-mode -1)
(global-hl-line-mode +1)
(line-number-mode +1)
(global-display-line-numbers-mode +1)
(column-number-mode t)
(size-indication-mode t)
(setq inhibit-startup-screen t)(setq frame-title-format
'((:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b"))))
(setq scroll-margin 0
scroll-conservatively 100000
scroll-preserve-screen-position 1)
(set-frame-font "Hack 12" nil t)

Theme

(use-package doom-themes
:ensure t
:config
(load-theme 'doom-one t)
(doom-themes-visual-bell-config))

Mode line

(use-package doom-modeline
:ensure t
:init (doom-modeline-mode 1))

Miscleneous preferences

Org ellipsis

(setq org-ellipsis "⤵")

Org bullets

(use-package org-bullets :ensure t)
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))
(setq org-hide-leading-stars t)

Org syntax highlighting

(setq org-src-fontify-natively t)

Fancy Lambdas

(global-prettify-symbols-mode t)

Highlight uncommited changes

(use-package diff-hl
:config
(add-hook 'prog-mode-hook 'turn-on-diff-hl-mode)
(add-hook 'vc-dir-mode-hook 'turn-on-diff-hl-mode))

Hide all minor modes

(use-package minions
:config
(setq minions-mode-line-lighter ""
minions-mode-line-delimiters '("" . ""))
(minions-mode 1))

Highlight the current line

(global-hl-line-mode)

Some more

Make TAB act as if it were issued in a buffer of the language’s major mode.

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

When editing a code snippet, use the current window rather than popping open a new one (which shows the same information).

(setq org-src-window-setup 'current-window)

Quickly insert a block of elisp:

(add-to-list 'org-structure-template-alist
'("el" . "src emacs-lisp"))

Editing

smartparens

(use-package smartparens
:ensure t
:diminish smartparens-mode
:config
(progn
(require 'smartparens-config)
(smartparens-global-mode 1)
(show-paren-mode t)))

expand-region

Selecting a region becomes smarter with expand region which keeps selecting an increasing region based on dwim syntax.

(use-package expand-region
:ensure t
:bind ("M-m" . er/expand-region))

crux

Some useful defaults are provided by the crux package of Prelude fame. “C-k” now kills a line if nothing is selected. “C-a” now toggles between first letter on the line, or beginning of the line.

(use-package crux
:ensure t
:bind
("C-k" . crux-smart-kill-line)
("C-c n" . crux-cleanup-buffer-or-region)
("C-c f" . crux-recentf-find-file)
("C-a" . crux-move-beginning-of-line))

undo-tree

Tree based undo management

(use-package undo-tree)

yasnippet

(use-package yasnippet
:ensure t
:config (use-package yasnippet-snippets :ensure t) (yas-reload-all))
(yas-global-mode 1)

Tabs

(setq-default tab-width 4
indent-tabs-mode nil)

Project management

flycheck

(use-package let-alist)
(use-package flycheck)

company

Use company everywhere.

(use-package company)
(add-hook 'after-init-hook 'global-company-mode)

magit

Use magit for versin control. Tweak a few stuff like bring up statu with C-x g, use evil key bindings with magit and a few more changes.

(use-package magit
:bind
("C-x g" . magit-status)
:config
(use-package evil-magit)
(use-package with-editor)
(setq magit-push-always-verify nil)
(setq git-commit-summary-max-length 50)
(with-eval-after-load 'magit-remote
(magit-define-popup-action 'magit-push-popup ?P
'magit-push-implicitly--desc
'magit-push-implicitly ?p t))
(add-hook 'with-editor-mode-hook 'evil-insert-state))

Quickly page through the history of a file with git-timemachine

(use-package git-timemachine)

projectile and helm

Projectile is a project manager that lets you easily switch between files in a project and seamlessly between projects as well. I use it with helm which I set up below.

(use-package projectile
:ensure t
:diminish projectile-mode
:bind
(("C-c p f" . helm-projectile-find-file)
("C-c p p" . helm-projectile-switch-project)
("C-c p s" . projectile-save-project-buffers))
:config
(projectile-mode +1)
)

It is a dwim fuzzy completion framework for Emacs and makes navigating Emacs a much nicer experience overall. I like to setup Helm to be a comfortable 20 pts in height and bind the most frequent Emacs commands like “M-x” with the helm equivalents.

(use-package helm
:ensure t
:defer 2
:bind
("M-x" . helm-M-x)
("C-x C-f" . helm-find-files)
("M-y" . helm-show-kill-ring)
("C-x b" . helm-mini)
("C-s" . helm-occur-from-isearch)
:config
(require 'helm-config)
(helm-mode 1)
(setq helm-locate-fuzzy-match t)
(setq helm-split-window-inside-p t
helm-move-to-line-cycle-in-source t)
(setq helm-autoresize-max-height 0)
(setq helm-autoresize-min-height 20)
(helm-autoresize-mode 1)
(define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) ; rebind tab to run persistent action
(define-key helm-map (kbd "C-i") 'helm-execute-persistent-action) ; make TAB work in terminal
(define-key helm-map (kbd "C-z") 'helm-select-action) ; list actions using C-z
)

Combile projectile with helm to show project files through a helm fuzzy find interface

(use-package helm-projectile
:ensure t
:config
(helm-projectile-on))

Navigation configurations

golden

When working with many windows at the same time, each window has a size that is not convenient for editing. golden-ratio helps on this issue by resizing automatically the windows you are working on to the size specified in the “Golden Ratio”.

(use-package golden-ratio :ensure t)
(golden-ratio-mode 1)

swiper

(use-package swiper
:ensure t)
(use-package swiper :ensure t
:config (progn (global-set-key "\C-s" 'swiper)))

evil

(use-package evil
:init
(setq evil-want-keybinding nil)
:config
(evil-mode 1))

Install evil-collection, which provides evil-friendly bindings for many modes.

(use-package evil-collection
:after evil)

Enable surround everywhere.

(use-package evil-surround
:config
(global-evil-surround-mode 1))

Use evil with Org agendas.

(use-package evil-org
:after org
:config
(add-hook 'org-mode-hook 'evil-org-mode)
(add-hook 'evil-org-mode-hook
(lambda () (evil-org-set-key-theme)))
(require 'evil-org-agenda)
(evil-org-agenda-set-keys))

Programming environments

CSS, SASS and LESS

Indent by 4 spaces

(use-package css-mode
:config
(setq css-indent-offset 4))

Don’t compile the current SCSS file every time I save.

(use-package scss-mode
:config
(setq scss-compile-at-save nil))

Install Less.

(use-package less-css-mode)

sh

Indent by 2 spaces

(add-hook 'sh-mode-hook
(lambda ()
(setq sh-basic-offset 2
sh-indentation 2)))

YAML

(use-package yaml-mode)

Golang

(setq lsp-gopls-staticcheck t)
(setq lsp-eldoc-render-all t)
(setq lsp-gopls-complete-unimported t)
(use-package lsp-mode
:ensure t
:commands (lsp lsp-deferred)
:hook (go-mode . lsp-deferred))
;;Set up before-save hooks to format buffer and add/delete imports.
;;Make sure you don't have other gofmt/goimports hooks enabled.
(defun lsp-go-install-save-hooks ()
(add-hook 'before-save-hook #'lsp-format-buffer t t)
(add-hook 'before-save-hook #'lsp-organize-imports t t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)
;;Optional - provides fancier overlays.(use-package lsp-ui
:ensure t
:commands lsp-ui-mode
:init
)
;;Company mode is a standard completion package that works well with lsp-mode.
;;company-lsp integrates company mode completion with lsp-mode.
;;completion-at-point also works out of the box but doesn't support snippets.
(use-package company
:ensure t
:config
(setq company-idle-delay 0)
(setq company-minimum-prefix-length 1))
;;Optional - provides snippet support.(use-package yasnippet
:ensure t
:commands yas-minor-mode
:hook (go-mode . yas-minor-mode))
;;lsp-ui-doc-enable is false because I don't like the popover that shows up on the right
;;I'll change it if I want it back
(setq lsp-ui-doc-enable nil
lsp-ui-peek-enable t
lsp-ui-sideline-enable t
lsp-ui-imenu-enable t
lsp-ui-flycheck-enable t)
(defun custom-go-mode ()
(display-line-numbers-mode 1))
(use-package go-mode
:defer t
:ensure t
:mode ("\\.go\\'" . go-mode)
:init
(setq compile-command "echo Building... && go build -v && echo Testing... && go test -v && echo Linter... && golint")
(setq compilation-read-command nil)
(add-hook 'go-mode-hook 'custom-go-mode)
:bind (("M-," . compile)
("M-." . godef-jump)))
(setq compilation-window-height 14)
(defun my-compilation-hook ()
(when (not (get-buffer-window "*compilation*"))
(save-selected-window
(save-excursion
(let* ((w (split-window-vertically))
(h (window-height w)))
(select-window w)
(switch-to-buffer "*compilation*")
(shrink-window (- h compilation-window-height)))))))
(add-hook 'compilation-mode-hook 'my-compilation-hook)
(global-set-key (kbd "C-c C-c") 'comment-or-uncomment-region)
(setq compilation-scroll-output t)

Terminal

Install multi-term and bind it to C-c t

(use-package multi-term)
(global-set-key (kbd "C-c t") 'multi-term)

Use a login bash shell:

(setq multi-term-program-switches "--login")

Rather not use Evil in the terminal. It’s not especially useful (I don’t use vi bindings in xterm) and it shadows useful keybindings (C-d for EOF, for example).

(evil-set-initial-state 'term-mode 'emacs)

A bunch of hooks in the term-mode

  1. URL’s are clickable
  2. Yanking in term-mode doesn’t quite work. The text from the paste appears in the buffer but isn’t sent to the shell process. This correctly binds C-y and middle-click to yank the way we’d expect.
  3. I bind M-o to quickly change windows. I’d like that in terminals, too.
  4. Don’t want to perform yasnippet expansion when tab-completing.
(defun hrs/term-paste (&optional string)
(interactive)
(process-send-string
(get-buffer-process (current-buffer))
(if string string (current-kill 0))))
(add-hook 'term-mode-hook
(lambda ()
(goto-address-mode)
(define-key term-raw-map (kbd "C-y") 'hrs/term-paste)
(define-key term-raw-map (kbd "<mouse-2>") 'hrs/term-paste)
(define-key term-raw-map (kbd "M-o") 'other-window)
(setq yas-dont-activate t)))

Miscleneous

Reveal.JS

(use-package ox-reveal :ensure t)
(setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")
(setq org-reveal-mathjax t)

Exporting

ox

Allow export to markdown and beamer (for presentations).

(require 'ox-md)
(require 'ox-beamer)

babel

Allow babel to evaluate Emacs lisp, dot, Gnuplot code.

(use-package gnuplot)(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(dot . t)
(gnuplot . t)))

Don’t ask before evaluating code blocks.

(setq org-confirm-babel-evaluate nil)

htmlize

Use htmlize to ensure that exported code blocks use syntax highlighting.

(use-package htmlize)

Associate the “dot” language with the graphviz-dot major mode.

(use-package graphviz-dot-mode)
(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))

Translate regular ol’ straight quotes to typographically-correct curly quotes when exporting.

(setq org-export-with-smart-quotes t)

Don’t include a footer with my contact and publishing information at the bottom of every exported HTML document.

(setq org-html-postamble nil)

Feed

elfeed to load all the feeds.

(use-package elfeed :ensure t
:config (elfeed-set-max-connections 32))
(use-package elfeed-org :ensure t
:config
(progn
(elfeed-org)
(setq rmh-elfeed-org-files (list "~/.emacs.d/feeds.org"))))
(use-package elfeed-goodies :ensure t
:config (elfeed-goodies/setup))

Use o to browse the entry in a Web browser.

(add-to-list 'evil-emacs-state-modes 'elfeed-show-mode)
(add-to-list 'evil-emacs-state-modes 'elfeed-search-mode)
(evil-add-hjkl-bindings elfeed-search-mode-map)
(evil-add-hjkl-bindings elfeed-show-mode-map)
(define-key elfeed-show-mode-map "o" 'elfeed-show-visit)
(define-key elfeed-search-mode-map "o" 'elfeed-search-browse-url)

Happy configuring and using emacs.🙏

--

--

Supratim Samanta

300K+views 🙏🏼#Unconventional #ProblemSolver #Tech #Dev #Productivity . Join my fascinating journey.🕺 Support me at https://susamn.medium.com/membership