Ultimate emacs setup with documentation in org mode

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

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

Funny… if you get the pun.

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

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)
(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)
(define-key input-decode-map "\e[1;2A" [S-up])
(global-set-key (kbd "M-/") 'company-complete-common)
(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)
(setq org-src-window-setup 'current-window)
(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))
(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)
)
(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
)
(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))
(use-package evil-collection
:after evil)
(use-package evil-surround
:config
(global-evil-surround-mode 1))
(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))
(use-package scss-mode
:config
(setq scss-compile-at-save nil))
(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)
(setq multi-term-program-switches "--login")
(evil-set-initial-state 'term-mode 'emacs)
  1. 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.
  2. I bind M-o to quickly change windows. I’d like that in terminals, too.
  3. 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)))
(setq org-confirm-babel-evaluate nil)

htmlize

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

(use-package htmlize)
(use-package graphviz-dot-mode)
(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))
(setq org-export-with-smart-quotes t)
(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))
(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)

is a father of a 2 year old. A coder, likes solving problems and learn how things work. Writes about tech, productivity, unconventional way of doing things.