emacs configuration #
This is my emacs configuration, available also on my Github account. This page is extracted from the same org source, included in the org roam note with
#+INCLUDE: "~/.emacs.d/init.org"
emacs configuration #
Miscellaneous #
Make things faster, particularly in lsp-mode.
(setq gc-cons-threshold 100000000)
(setq byte-compile-warnings '(cl-functions))
Certain commands only make sense in a GUI.
(if (display-graphic-p)
(progn
(tool-bar-mode -1)
(tooltip-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)))
Don’t let custom-set-variables dirty the init.el file.
(setq custom-file (concat user-emacs-directory "/custom.el"))
Set file backup in a single directory.
(setq backup-directory-alist '(("" . "~/.emacs.d/backup")))
Send usage information to ActivityWatch.
(use-package activity-watch-mode)
(global-activity-watch-mode)
Package system #
Manage packages with use-package.
(require 'package)
(require 'use-package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("elpa" . "https://elpa.gnu.org/packages/")
("org" . "http://orgmode.org/elpa/")))
(unless package-archive-contents
(package-refresh-contents))
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(setq use-package-always-ensure t)
Fix MacOS path issues with emacs.
(use-package exec-path-from-shell)
(when (memq window-system '(mac ns))
(setenv "SHELL" "/bin/zsh")
(exec-path-from-shell-initialize)
(exec-path-from-shell-copy-envs
'("PYTHONPATH")))
Initial UI #
(defvar clinton/default-font-size 140)
(defvar clinton/default-variable-font-size 140)
(setq inhibit-startup-message t)
(tool-bar-mode -1)
(tooltip-mode -1)
(menu-bar-mode -1)
(column-number-mode 1)
(display-time-mode 1)
(display-battery-mode 1)
(global-display-line-numbers-mode t)
;; Disable line numbers for some modes
(dolist (mode '(org-mode-hook
org-agenda-mode-hook
term-mode-hook
shell-mode-hook
treemacs-mode-hook
eshell-mode-hook))
(add-hook mode (lambda () (display-line-numbers-mode 0))))
;; Set the default face
(set-face-attribute 'default nil :font "Fira Code" :height clinton/default-font-size :weight 'light)
;; Set the fixed pitch face
(set-face-attribute 'fixed-pitch nil :font "Fira Code" :height clinton/default-font-size :weight 'light)
;; Set the variable pitch face
(set-face-attribute 'variable-pitch nil :font "Fira Code" :height clinton/default-variable-font-size)
Keybindings #
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
(global-set-key (kbd "C-x /") 'comment-region)
(global-unset-key (kbd "C-l"))
(global-unset-key (kbd "C-c h"))
(global-unset-key (kbd "C-x f"))
(global-unset-key (kbd "C-x d"))
(global-set-key (kbd "C-x d") 'dirvish)
(use-package which-key
:init
(setq which-key-separator " ")
(setq which-key-prefix-prefix "+")
(which-key-mode)
:diminish which-key-mode
:config
(setq which-key-idle-delay 1))
(use-package general
:config
(general-create-definer clinton/leader-c
:prefix "C-c")
(general-create-definer clinton/leader-l
:prefix "C-l"))
(clinton/leader-c
"a" 'org-agenda
"b" 'counsel-buffer-or-recentf
"c" 'org-capture
"d" 'my/org-agenda-daily
"e" 'deft
"h" 'call-my-script-with-word
"i" 'my/org-goto-inbox
"p" 'projectile-command-map
"r" 'revert-buffer
"s" 'org-store-link
"t" '(lambda () (interactive) (ansi-term "zsh"))
"w" 'my/org-agenda-weekly-review
"W" 'count-words)
(clinton/leader-l
"a" 'avy-goto-char-2
"c" 'helm-make-projectile
"d" 'xref-find-definitions
"e" 'flymake-goto-next-error
"f" 'follow-mode
"j" 'json-pretty-print
"k" 'eglot-code-actions
"o" 'olivetti-mode
"r" 'xref-find-references
"s" 'counsel-rg
"t" '(counsel-load-theme :which-key "choose theme")
"v" 'valign-table
"y" 'prettier-js
"x" 'org-babel-execute-src-block)
Appearance #
(use-package doom-themes
:init (load-theme 'doom-vibrant t))
(use-package all-the-icons)
(use-package dtrt-indent)
(setq dtrt-indent-original-indent 0)
(use-package doom-modeline
:init (doom-modeline-mode 1)
:custom ((doom-modeline-height 15)))
(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist '(ns-appearance . dark))
(setq ns-use-proxy-icon nil)
(setq frame-title-format "emacs")
(setq-default line-spacing 0.25)
UX #
-
Ivy, amx, counsel, swiper
(use-package ivy :diminish :init (use-package amx :defer t) (use-package counsel :diminish :config (counsel-mode 1)) (use-package swiper :defer t) (ivy-mode 1) :bind (("C-s" . swiper-isearch) (:map ivy-minibuffer-map ("M-RET" . ivy-immediate-done)) (:map counsel-find-file-map ("C-~" . counsel-goto-local-home))) :custom (ivy-use-virtual-buffers t) (ivy-height 10) (ivy-on-del-error-function nil) (ivy-magic-slash-non-match-action 'ivy-magic-slash-non-match-create) (ivy-count-format "【%d/%d】") (ivy-wrap t) :config (defun counsel-goto-local-home () "Go to the $HOME of the local machine." (interactive) (ivy--cd "~/")))
-
Dirvish
(use-package dirvish :ensure t :init (dirvish-override-dired-mode))
Markdown #
(defun clinton/markdown-mode-setup ()
(variable-pitch-mode 1)
(visual-line-mode 1))
(use-package markdown-mode
:ensure t
:hook (markdown-mode . clinton/markdown-mode-setup)
:commands (markdown-mode gfm-mode)
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "multimarkdown"))
Use Olivetti to have a nice clean editing environment.
(require 'olivetti)
org-mode #
-
Basic setup
(custom-set-faces '(default ((t (:foreground "#BBC2CF")))) '(org-level-1 ((t (:foreground "#BF9D7A")))) '(org-level-2 ((t (:foreground "#E4E9CD")))) '(org-level-3 ((t (:foreground "#EBF2EA")))) '(org-level-4 ((t (:foreground "#0ABDA0")))) '(org-level-5 ((t (:foreground "#80ADD7"))))) (defun clinton/org-mode-setup () (org-indent-mode) (variable-pitch-mode 1) (visual-line-mode 1)) (use-package org :hook (org-mode . clinton/org-mode-setup) :config (setq org-ellipsis " ▾") (setq org-agenda-start-with-log-mode t) (setq org-log-done 'time) (setq org-log-into-drawer t) (setq org-agenda-start-on-weekday 1) ;; Todo keywords (setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAITING(w)" "|" "DONE(d)" "CANCELLED(c)"))) (setq org-todo-keyword-faces '(("TODO" . (:foreground "orange" :weight bold)) ("NEXT" . (:foreground "red" :weight bold)) ("WAITING" . (:foreground "purple" :weight bold)) ("DONE" . (:foreground "green" :weight bold)) ("CANCELLED" . (:foreground "gray" :weight bold)))) ;; Tags (setq org-tag-alist '(("@active" . ?a) ("@someday" . ?s) ("@waiting" . ?w) ("@errand" . ?e) ("work" . ?W) ("home" . ?H))))
-
Agenda files
(setq org-directory "~/org") (defun my/org-agenda-files () "Build agenda file list from JD directories and org-monica contacts." (let ((dirs '("0-system" "1-admin" "2-personal" "3-career" "4-knowledge" "5-technical" "6-creative" "7-experiences" "8-media"))) (append (list (expand-file-name "inbox.org" org-directory)) (cl-loop for dir in dirs append (directory-files-recursively (expand-file-name dir org-directory) "\\.org$")) (when (boundp 'org-monica-directory) (cl-remove-if-not (lambda (f) (with-temp-buffer (insert-file-contents f) (re-search-forward "^\\*\\{2,\\} \\(TODO\\|DEADLINE\\)" nil t))) (directory-files-recursively (expand-file-name "contacts" org-monica-directory) "\\.org$")))))) (setq org-agenda-files (my/org-agenda-files)) (defun my/refresh-org-agenda-files () "Refresh the list of org agenda files." (interactive) (setq org-agenda-files (my/org-agenda-files)) (message "Refreshed org-agenda-files: %d files" (length org-agenda-files)))
-
Agenda display
(setq org-agenda-prefix-format '((agenda . " %i %-16:c%?-12t% s") (tags . " %i %-16:c") (todo . " %i %-16:c") (search . " %i %-16:c"))) (setq org-agenda-todo-ignore-scheduled 'future) (setq org-agenda-todo-ignore-deadlines 'far) (setq org-agenda-tags-todo-honor-ignore-options t) (setq org-agenda-skip-deadline-if-done t) (setq org-agenda-skip-scheduled-if-done t)
-
Agenda commands
(setq org-agenda-custom-commands '(("d" "Daily" ((agenda "" ((org-agenda-span 1) (org-agenda-start-day "today") (org-agenda-overriding-header "Today"))) (todo "NEXT" ((org-agenda-overriding-header "Next actions"))) (todo "WAITING" ((org-agenda-overriding-header "Waiting on"))) (alltodo "" ((org-agenda-files (list (expand-file-name "inbox.org" org-directory))) (org-agenda-overriding-header "Inbox (to process)"))) (tags "-TODO=\"DONE\"-TODO=\"TODO\"-TODO=\"NEXT\"-TODO=\"WAITING\"-TODO=\"CANCELLED\"" ((org-agenda-files (list (expand-file-name "inbox.org" org-directory))) (org-agenda-overriding-header "Inbox (no keyword)"))))) ("w" "Weekly Review" ((agenda "" ((org-agenda-span 14) (org-agenda-overriding-header "Next 2 weeks"))) (todo "NEXT" ((org-agenda-overriding-header "All next actions"))) (todo "WAITING" ((org-agenda-overriding-header "Waiting on"))) (alltodo "" ((org-agenda-files (list (expand-file-name "inbox.org" org-directory))) (org-agenda-overriding-header "Inbox (to process)"))) (tags "-TODO=\"DONE\"-TODO=\"TODO\"-TODO=\"NEXT\"-TODO=\"WAITING\"-TODO=\"CANCELLED\"" ((org-agenda-files (list (expand-file-name "inbox.org" org-directory))) (org-agenda-overriding-header "Inbox (no keyword)"))) (tags-todo "@active" ((org-agenda-overriding-header "Active projects — ensure each has a NEXT"))) (tags-todo "@someday" ((org-agenda-overriding-header "Someday/Maybe — review"))))) ("W" "Work" ((agenda "" ((org-agenda-span 1) (org-agenda-files (directory-files-recursively (expand-file-name "3-career" org-directory) "\\.org$")) (org-agenda-overriding-header "Work today"))) (todo "NEXT" ((org-agenda-files (directory-files-recursively (expand-file-name "3-career" org-directory) "\\.org$")) (org-agenda-overriding-header "Work next actions"))))) ("P" "Personal" ((agenda "" ((org-agenda-span 1) (org-agenda-files (append (directory-files-recursively (expand-file-name "1-admin" org-directory) "\\.org$") (directory-files-recursively (expand-file-name "2-personal" org-directory) "\\.org$"))) (org-agenda-overriding-header "Personal today"))) (todo "NEXT" ((org-agenda-files (append (directory-files-recursively (expand-file-name "1-admin" org-directory) "\\.org$") (directory-files-recursively (expand-file-name "2-personal" org-directory) "\\.org$"))) (org-agenda-overriding-header "Personal next actions")))))))
-
Capture templates
(setq org-default-notes-file (expand-file-name "inbox.org" org-directory)) (setq org-capture-templates '(("i" "Inbox" entry (file "inbox.org") "* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n" :empty-lines 1) ("t" "Todo" entry (file "inbox.org") "* TODO %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n" :empty-lines 1) ("n" "Next action" entry (file "inbox.org") "* NEXT %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n" :empty-lines 1) ("w" "Waiting" entry (file "inbox.org") "* WAITING %? :@waiting:\n:PROPERTIES:\n:CREATED: %U\n:END:\n" :empty-lines 1) ("m" "Meeting notes" entry (file "inbox.org") "* Meeting: %? :meeting:\n:PROPERTIES:\n:CREATED: %U\n:END:\n** Attendees\n** Notes\n** Actions\n" :empty-lines 1)))
-
Refile
(setq org-refile-targets '((org-agenda-files :maxlevel . 3))) (setq org-refile-use-outline-path 'file) (setq org-outline-path-complete-in-steps nil) (setq org-refile-allow-creating-parent-nodes 'confirm)
-
Helper functions
(defun my/org-agenda-daily () "Open daily agenda." (interactive) (org-agenda nil "d")) (defun my/org-agenda-weekly-review () "Open weekly review agenda." (interactive) (org-agenda nil "w")) (defun my/org-goto-inbox () "Open inbox file." (interactive) (find-file (expand-file-name "inbox.org" org-directory))) (defun my/org-goto-rituals () "Open rituals file." (interactive) (find-file (expand-file-name "0-system/rituals.org" org-directory))) (defun my/org-inbox-count () "Count items in inbox." (interactive) (let ((count (length (org-map-entries t nil (list (expand-file-name "inbox.org" org-directory)))))) (message "Inbox: %d items" count)))
-
org-roam
(use-package org-roam :init (setq org-roam-v2-ack t) (setq org-roam-db-location (expand-file-name "~/roam/org-roam.db")) :custom (org-roam-directory "~/roam") (org-roam-dailies-directory "journals/") (org-roam-file-exclude-regexp "\\.st[^/]*\\|logseq/.*$") (org-roam-capture-templates '(("d" "default" plain "%?" :target (file+head "pages/${slug}.org" "#+title: ${title}\n") :unnarrowed t))) :bind (("C-c n l" . org-roam-buffer-toggle) ("C-c n f" . org-roam-node-find) ("C-c i" . org-roam-node-insert) ("C-c n t" . org-roam-tag-add) ("C-c n r" . org-roam-db-sync) ("C-c n j" . org-roam-dailies-goto-today) ("C-c n y" . org-roam-dailies-goto-yesterday)) :hook (org-roam-mode . (lambda () (make-variable-buffer-local 'visual-line-mode) (visual-line-mode))) :config (setq org-roam-db-location "~/roam/org-roam.db") (org-roam-setup)) (setq org-roam-dailies-capture-templates '(("d" "default" entry "* %?" :target (file+head "%<%Y_%m_%d>.org" "#+title: %<%Y-%m-%d>\n")))) (add-hook 'org-roam-dailies-find-file-hook #'olivetti-mode) (defun clinton/deft-setup () (visual-line-mode 0)) (use-package deft :hook (deft-mode . clinton/deft-setup) :init (setq deft-directory "~/roam") (setq-default truncate-lines t)) (require 'deft)
-
org-monica
(add-to-list 'load-path "~/Documents_st/5. technical/55. emacs/org-monica") (setq org-monica-directory (expand-file-name "monica" "~/roam")) (setq org-monica-contacts-directory (expand-file-name "contacts" org-monica-directory)) (require 'org-monica) (my/refresh-org-agenda-files)(add-to-list 'load-path "~/Documents_st/5. technical/55. emacs/orgbd/") (require 'orgbd) (setq orgbd-directory "~/org/braindumps/") (setq orgbd-index-file "~/org/0-system/index.org")
-
org-roam-ui
(add-to-list 'load-path "~/.emacs.d/private/org-roam-ui") (load-library "org-roam-ui")
-
org babel
(org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (org . t) (shell . t) (haskell .t) (julia .t) (python . t) (jupyter . t) (latex . t) (R . t) (rust . t))) (setq org-babel-python-command "python") (setq ob-async-no-async-languages-alist '("python" "jupyter-python"))
-
Automatically tangle this configuration file
On save, automatically tangle to init.el.
(defvar clinton/init-org-file (concat user-emacs-directory "init.org")) (defvar clinton/init-el-file (concat user-emacs-directory "init.el")) (defun clinton/tangle-on-save () (when (equal (buffer-file-name) (expand-file-name clinton/init-org-file)) (let ((org-confirm-babel-evaluate nil)) (org-babel-tangle) (message "init.el tangled from init.org")))) (add-hook 'after-save-hook 'clinton/tangle-on-save)
magit #
(use-package magit)
Hugo support #
(use-package ox-hugo
:ensure t
:after ox)
(setq org-hugo-default-section-directory "/Users/clinton/Documents_st/5. technical/54. websites/digital-garden")
(defun title-capitalization (str)
"Convert str to title case"
(interactive)
(with-temp-buffer
(insert str)
(let* ((beg (point-min))
(end (point-max))
(do-not-capitalize-basic-words '("a" "ago" "an" "and" "as" "at" "but" "by" "for"
"from" "in" "into" "it" "next" "nor" "of" "off"
"on" "onto" "or" "over" "past" "so" "the" "till"
"to" "up" "yet"
"n" "t" "es" "s"))
(do-not-capitalize-words (if (boundp 'my-do-not-capitalize-words)
(append do-not-capitalize-basic-words my-do-not-capitalize-words)
do-not-capitalize-basic-words)))
(goto-char beg)
(setq continue t)
(while continue
(let ((last-point (point)))
(let ((word (thing-at-point 'word)))
(if (stringp word)
(if (and (member (downcase word) do-not-capitalize-words)
(not (= (point) 1)))
(downcase-word 1)
(if (string= word (upcase word))
(progn
(goto-char (+ (point) (length word) 1)))
(capitalize-word 1)))))
(skip-syntax-forward "^w" end)
(when (= (point) last-point)
(setq continue nil))))
(backward-word 1)
(let ((word (thing-at-point 'word)))
(if (and (>= (point) 0)
(not (member (or word "s")
'("n" "t" "es" "s")))
(not (string= word (upcase word))))
(capitalize-word 1))))
(buffer-string)))
Digital Garden Export #
(defvar script-name "~/.emacs.d/roam_build.sh")
(defun call-my-script-with-word ()
(interactive)
(shell-command
(concat script-name
" "
(thing-at-point 'word))))
Programming #
-
Rust
(use-package rust-mode :mode "\\.rs\\'" :custom (rust-format-on-save t) :bind (:map rust-mode-map ("C-c C-c" . rust-run)) :config (use-package flycheck-rust :after flycheck :config (with-eval-after-load 'rust-mode (add-hook 'flycheck-mode-hook #'flycheck-rust-setup))))
-
Python
(use-package python-mode :mode "\\.py\\'" :custom (rust-format-on-save t) :bind (:map python-mode-map ("C-c C-c" . python-run)) :config (use-package flycheck-python :after flycheck :config (with-eval-after-load python-mode)))
-
Typescript
(use-package tree-sitter :ensure t :config (global-tree-sitter-mode) (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode)) (use-package tree-sitter-langs :ensure t :after tree-sitter) (use-package typescript-mode :after tree-sitter :config (define-derived-mode typescriptreact-mode typescript-mode "TypeScript TSX") (add-to-list 'auto-mode-alist '("\\.tsx?\\'" . typescriptreact-mode)) (add-to-list 'tree-sitter-major-mode-language-alist '(typescriptreact-mode . tsx))) (cl-defmethod project-root ((project (head eglot-project))) (cdr project)) (defun my-project-try-tsconfig-json (dir) (when-let* ((found (locate-dominating-file dir "tsconfig.json"))) (cons 'eglot-project found))) (add-hook 'project-find-functions 'my-project-try-tsconfig-json nil nil) (require 'eglot) (add-to-list 'eglot-server-programs '((typescript-mode) "typescript-language-server" "--stdio")) (require 'prettier-js)
LSP mode #
(use-package eglot)
(setq lsp-rust-analyzer-server-command '("~/.cargo/bin/rust-analyzer"))
(add-to-list 'eglot-server-programs '(python-mode "/opt/homebrew/bin/pyls"))
(setq rustic-lsp-client 'eglot)
(setq python-shell-interpreter "~/.pyenv/versions/emacs/bin/python3")
(use-package company
:diminish company-mode
:hook ((prog-mode LaTeX-mode latex-mode ess-r-mode) . company-mode)
:bind
(:map company-active-map
([tab] . smarter-tab-to-complete)
("TAB" . smarter-tab-to-complete))
:custom
(company-minimum-prefix-length 1)
(company-tooltip-align-annotations t)
(company-require-match 'never)
(company-global-modes '(not shell-mode eaf-mode))
(company-idle-delay 0.1)
(company-show-numbers t))
(electric-pair-mode 1)
(setq electric-pair-preserve-balance nil)
Consult #
(use-package consult)
Howm #
(use-package howm
:ensure t)
Links and resources #
- rational-emacs, a nice package to start building one’s own configuration from, with sensible defaults for a lot of common usages.
- Another nice repo with a well-organised configuration to steal from.