Emacs configuration
Table of Contents
Emacs configuration
Source for my Emacs configuration. The init.el file can be generated from this org file using org-babel-tangle
(reference).
Boostrap straight.el
Use straight.el with the use-package
form to install and manage emacs packages.
;; Disable emacs default package.el (in favor of straight.el) (setq package-enable-at-startup nil) ;; Bootstrap straight.el (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 5)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) (straight-use-package 'use-package) ;; Use the use-package form to install define packages (use-package straight :custom (straight-use-package-by-default t)) ;; Make :straight t the default to ensure packages are installed
Custom file location
Set the custom file location.
;; Set custom file location (setq custom-file "~/.emacs.d/custom.el") (load custom-file)
Update defaults
Update a few defaults like the menu-bar, scroll-bar etc.
;; Remove menu, scroll bars and other visual things (menu-bar-mode -1) (scroll-bar-mode -1) (tool-bar-mode -1) (setq column-number-mode nil) (setq line-number-mode nil) (setq size-indication-mode nil) (setq inhibit-startup-screen t) (setq mouse-yank-at-point t) (setq-default indent-tabs-mode nil) (global-display-line-numbers-mode t) ;; Disable line numbers for some modes (dolist (mode '(org-mode-hook term-mode-hook shell-mode-hook treemacs-mode-hook eshell-mode-hook)) (add-hook mode (lambda () (display-line-numbers-mode 0)))) (add-hook 'before-save-hook 'delete-trailing-whitespace)
Org-mode
I use org-mode to organize my tasks, edit this website and write meeting notes and keep some long-term documentation.
I have a few different files for tasks and I like to clean them up by removing old DONE tasks. My exact workflow is that I have a recurrent tasks every two weeks that reminds me to refile DONE tasks older than two weeks to an archive.org
file.
To automate this, a few custom functions are needed to find DONE tasks older than two weeks. (source: https://stackoverflow.com/a/8186450)
(defun zin/since-state (since todo-state &optional done all) "List Agenda items that are older than SINCE. TODO-STATE is a regexp for matching to TODO states. It is provided to `zin/find-state' to match inactive timestamps. SINCE is compared to the result of `zin/org-date-diff'. If `zin/org-date-diff' is greater than SINCE, the entry is shown in the Agenda. Optional argument DONE allows for done and not-done headlines to be evaluated. If DONE is non-nil, match completed tasks. Optional argument ALL is passed to `zin/find-state' to specify whether to search for any possible match of STATE, or only in the most recent log entry." (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) ;; If DONE is non-nil, look for done keywords, if nil look for not-done (if (member (org-get-todo-state) (if done org-done-keywords org-not-done-keywords)) (let* ((subtree-end (save-excursion (org-end-of-subtree t))) (subtree-valid (save-excursion (forward-line 1) (if (and (< (point) subtree-end) ;; Find the timestamp to test (zin/find-state todo-state subtree-end all)) (let ((startpoint (point))) (forward-word 3) ;; Convert timestamp into days difference from today (zin/org-date-diff startpoint (point))))))) (if (or (not subtree-valid) (<= subtree-valid since)) next-headline nil)) (or next-headline (point-max))))) (defun zin/find-state (state &optional end all) "Used to search through the logbook of subtrees. Tests to see if the first line of the logbook is a change of todo status to status STATE - Status \"STATE\" from ... The search brings the point to the start of YYYY-MM-DD in inactive timestamps. Optional argument END defines the point at which to stop searching. Optional argument ALL when non-nil specifies to look for any occurence of STATE in the subtree, not just in the most recent entry." (let ((drawer (if all "" ":.*:\\W" "CLOSED:"))) (or (re-search-forward (concat drawer ".*State \\\"" state "\\\"\\W+from.*\\[") end t) (re-search-forward (concat drawer ".*\\[") end t)))) (defun zin/org-date-diff (start end &optional compare) "Calculate difference between selected timestamp to current date. The difference between the dates is calculated in days. START and END define the region within which the timestamp is found. Optional argument COMPARE allows for comparison to a specific date rather than to current date." (let* ((start-date (if compare compare (calendar-current-date)))) (- (calendar-absolute-from-gregorian start-date) (org-time-string-to-absolute (buffer-substring-no-properties start end))) ))
In the org-mode configuration you can see references to files that are not part of this repository. The org-agenda-files
and the org-capture-templates
templates.
I use capture templates to create new notes / recipes on this website. Each page is a different org file and for recipes there's a common template.
There's also the org-agenda-custom-commands
that uses the previously defined functions to refiles old DONE tasks.
(use-package org :bind (("C-c a" . org-agenda-list) ("C-c l" . org-store-link) ("C-c c" . org-capture)) :hook (org-mode . org-indent-mode) (org-mode . visual-line-mode) :config (setq org-html-doctype "html5" ;; HTML export ;; Visual tweaks org-hide-emphasis-markers t org-ellipsis " ▼ " ;; Startup org-startup-with-inline-images t org-startup-folded "showeverything" org-startup-indented t ;; Babel org-babel-min-lines-for-block-output 1 org-confirm-babel-evaluate nil org-src-preserve-indentation t ;; Fontify org-fontify-done-headline t org-src-fontify-natively t ;; Agenda org-agenda-start-with-log-mode t org-log-done 'time org-agenda-skip-scheduled-if-done t org-log-into-drawer t org-agenda-files '("~/org/work.org" "~/org/keep.org" "~/org/house.org" "~/org/perso.org") org-agenda-custom-commands '(("R" "Tasks that were completed more than 14 days ago." tags "-REFILE/" ((org-agenda-files '("~/org/work.org" "~/org/perso.org" "~/org/house.org")) (org-agenda-overriding-header "Archivable tasks") (org-agenda-skip-function '(zin/since-state 14 "\\\(DONE\\\|CANCELED\\\)" t))))) ;; Refiling org-refile-targets '(("~/org/archives.org" :maxlevel . 1))) (advice-add 'org-refile :after 'org-save-all-org-buffers) (setq org-todo-keywords '((sequence "TODO(t!)" "|" "DONE(d!)"))) (setq org-capture-templates '(("s" "Site Entries") ("sr" "Recipe" plain (file (lambda () (expand-file-name (read-string "Filename: ") "~/dev/volnt.github.io/recipes/"))) (file "~/org/templates/recipe.org")) ("sn" "Note" plain (file (lambda () (expand-file-name (read-string "Filename: ") "~/dev/volnt.github.io/notes/")))) ("t" "Todo Entries") ("tt" "Todo" entry (file "~/org/perso.org") "* TODO %?\n%U\n%i" :empty-lines 1) ("th" "House entry." entry (file "~/org/house.org") "* TODO %? :house:\n%U\n%i" :empty-lines 1) ("tw" "Work entry." entry (file "~/org/work.org") "* TODO %? :work:\n%U\n%i" :empty-lines 1) ("m" "Meeting Entries") ("mm" "Meeting" entry (file "~/org/perso.org") "* %? :meeting:\n%U\n%i" :clock-in :clock-resume :empty-lines 1) ("mw" "Work Meeting" entry (file "~/org/work.org") "* %? :work:meeting:\n%U\n%i" :clock-in :clock-resume :empty-lines 1) ("mo" "One-to-One" entry (file+headline "~/org/work.org" "One-to-one") "* TODO %? :work:meeting:\n%U\n%i" :clock-in :clock-resume :empty-lines 1) ("mt" "Tech Screening" entry (file "~/org/work.org") (file "~/org/templates/tech-screening.org") :clock-in :clock-resume :empty-lines 1) ("ml" "ML Interview" entry (file "~/org/work.org") (file "~/org/templates/ml-engineer-screening.org") :clock-in :clock-resume :empty-lines 1))) (org-babel-do-load-languages 'org-babel-load-languages '((python . t) (shell . t) (C . t) (gnuplot . t))) (use-package org-superstar :hook (org-mode . org-superstar-mode) :custom (org-superstar-remove-leading-stars t) (org-superstar-headline-bullets-list '("◉" "○" "●" "○" "●" "○" "●"))) (use-package epresent) (use-package ox-jira) (straight-use-package '(org-contrib :includes org-checklist)) (load "org-checklist"))
Install major modes
A few major modes I use.
;; Major modes (use-package ledger-mode) (use-package gnuplot :config (use-package gnuplot-mode)) (use-package lua-mode) (use-package typescript-mode) (use-package yaml-mode) (use-package terraform-mode) (use-package markdown-mode)
Theme
Setup the theme (color-theme + modeline).
;; Theme (use-package solarized-theme :config (load-theme 'solarized-selenized-dark)) (use-package doom-modeline :init (doom-modeline-mode 1) :config (use-package all-the-icons)) ;; eval-expression (all-the-icons-install-fonts) on first run
UI
All UI related packages.
Use vertico
for minibuffer completion with marginalia
for added details.
For code-completion use company
.
;; UI (use-package unicode-fonts :config (unicode-fonts-setup) (use-package font-utils) (use-package ucs-utils)) (use-package flycheck :config (global-flycheck-mode)) (use-package vertico :init (vertico-mode) ;; Different scroll margin ;; (setq vertico-scroll-margin 0) ;; Show more candidates ;; (setq vertico-count 20) ;; Grow and shrink the Vertico minibuffer ;; (setq vertico-resize t) ;; Optionally enable cycling for `vertico-next' and `vertico-previous'. ;; (setq vertico-cycle t) ) ;; Optionally use the `orderless' completion style. See ;; `+orderless-dispatch' in the Consult wiki for an advanced Orderless style ;; dispatcher. Additionally enable `partial-completion' for file path ;; expansion. `partial-completion' is important for wildcard support. ;; Multiple files can be opened at once with `find-file' if you enter a ;; wildcard. You may also give the `initials' completion style a try. (use-package orderless :init ;; Configure a custom style dispatcher (see the Consult wiki) ;; (setq orderless-style-dispatchers '(+orderless-dispatch) ;; orderless-component-separator #'orderless-escapable-split-on-space) (setq completion-styles '(orderless) completion-category-defaults nil completion-category-overrides '((file (styles partial-completion))))) ;; Persist history over Emacs restarts. Vertico sorts by history position. (use-package savehist :init (savehist-mode)) ;; A few more useful configurations... (use-package emacs :init ;; Add prompt indicator to `completing-read-multiple'. ;; Alternatively try `consult-completing-read-multiple'. (defun crm-indicator (args) (cons (concat "[CRM] " (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator) ;; Do not allow the cursor in the minibuffer prompt (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; Emacs 28: Hide commands in M-x which do not work in the current mode. ;; Vertico commands are hidden in normal buffers. ;; (setq read-extended-command-predicate ;; #'command-completion-default-include-p) ;; Enable recursive minibuffers (setq enable-recursive-minibuffers t)) (use-package marginalia :config (marginalia-mode)) (use-package company :config (setq company-global-modes '(not shell-mode)) (global-company-mode t) :bind (:map company-active-map ("<tab>" . company-complete-selection)) :custom (company-minimum-prefix-length 1) (company-idle-delay 0.0)) (use-package highlight-indentation :straight (highlight-identation :type git :host github :repo "antonj/Highlight-Indentation-for-Emacs") :hook (prog-mode . highlight-indentation-mode) :config (setq highlight-indentation-blank-lines t)) (use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode)) (use-package hl-line :config (global-hl-line-mode)) (use-package git-gutter :config (global-git-gutter-mode t)) (use-package helpful)
Utilities
Here are all utilities with no effect on UI.
;; Utilities (use-package magit :bind (("C-c s" . magit-status) ("C-c b" . magit-blame) ("C-c g" . vc-git-grep))) (use-package undo-tree :config (global-undo-tree-mode)) (use-package multiple-cursors :bind (("C-c m" . mc/mark-all-in-region) ("C-c n" . mc/mark-next-like-this))) (use-package projectile :config (projectile-mode) :bind-keymap ("C-c p" . projectile-command-map)) (use-package autorevert) (use-package which-key :config (which-key-mode)) (use-package eldoc) (use-package realgud :config (load-library "realgud")) (use-package yasnippet :config (yas-global-mode t) (use-package yasnippet-snippets)) (use-package gazr :straight (gazr :type git :host github :repo "volnt/gazr.el") :bind (("C-c C-g" . gazr)))
Vterm
Emacs-libvterm (vterm) is fully-fledged terminal emulator inside GNU Emacs based on libvterm, a C library. As a result of using compiled code (instead of elisp), emacs-libvterm is fully capable, fast, and it can seamlessly handle large outputs.
(use-package vterm :config (setq vterm-max-scrollback 100000))
Python setup
Because Python is the language I use the most, I use more packages than just the major-mode.
blacken
is used for code formatting, and py-isort
for imports ordering.
lsp-pyright
is used for code completion, flycheck warnings and find-definitions
/ find-references
.
;; Python (use-package lsp-mode :commands (lsp lsp-deferred) :config (lsp-enable-which-key-integration t) (use-package lsp-ui) (use-package lsp-treemacs)) (use-package py-isort :custom (py-isort-options '("-w 120")) :hook (before-save . py-isort-before-save)) (use-package blacken :hook (python-mode . blacken-mode) :custom (blacken-line-length 120)) (use-package lsp-pyright :hook (python-mode . (lambda () (require 'lsp-pyright) (lsp))) ; or lsp-deferred :init (setq lsp-pyright-python-executable-cmd "python3.8") :config (setq lsp-pyright-disable-organize-imports t) :bind-keymap ("C-c C-o" . lsp-command-map) :bind (("C-c ;" . xref-find-definitions) ("C-c ," . xref-pop-marker-stack) ("C-c :" . lsp-find-references)))