560 lines
18 KiB
Org Mode
560 lines
18 KiB
Org Mode
#+TITLE: Emacs configuration
|
||
#+PROPERTY: header-args:emacs-lisp :tangle init.el
|
||
|
||
* Preface
|
||
|
||
GNU Emacs isn't a text editor, it's the ultimate productivity environment for hackers.
|
||
This document contains the core parts of my Emacs configuration which drive the workflow
|
||
I use every day.
|
||
|
||
This Emacs configuration file does use =setup.el= for package management.
|
||
|
||
* Default themes and faces
|
||
|
||
Default theme. It is needed by =early-init.el= too. In this case we have nice themed
|
||
emacs even only early-init does exist.
|
||
|
||
** Face extensions
|
||
|
||
*** Common face definitions
|
||
|
||
#+begin_src emacs-lisp :tangle lisp/themes/r2849-faces.el :mkdirp true
|
||
;;; -*- lexical-binding: t -*-
|
||
;;; Do not edit by hand! It is generated from readme.org.
|
||
|
||
;; -------------------------------------------------------------------------------------------------
|
||
;; common face definitions
|
||
;; -------------------------------------------------------------------------------------------------
|
||
(defface mr:operator-face
|
||
'((t (:foreground "red")))
|
||
"Face used to highlight operator characters like +, -"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:relop-face
|
||
'((t (:foreground "dodgerblue1")))
|
||
"Face used to highlight relational operator characters like <, >="
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:paren-face
|
||
'((t (:foreground "indianred")))
|
||
"Face used to highlight parentheses characters like (, ]"
|
||
:group 'mr:faces)
|
||
|
||
(defvar mr:operator-face 'mr:operator-face )
|
||
(defvar mr:relop-face 'mr:relop-face )
|
||
(defvar mr:paren-face 'mr:paren-face )
|
||
#+end_src
|
||
|
||
*** Assembly mode face definitions
|
||
|
||
#+begin_src emacs-lisp :tangle lisp/themes/r2849-faces.el
|
||
;; -------------------------------------------------------------------------------------------------
|
||
;; assembly mode face definitions
|
||
;; -------------------------------------------------------------------------------------------------
|
||
(defface mr:asm-comment2-face
|
||
'((t (:foreground "gray50")))
|
||
"Face used to highlight comments beginnig with ;;"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-comment3-face
|
||
'((t (:foreground "gray50")))
|
||
"Face used to highlight comments beginnig with ;;;"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-glabel-face
|
||
'((t (:foreground "sienna3")))
|
||
"Face used to highlight global labels"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-label-face
|
||
'((t (:foreground "sienna1")))
|
||
"Face used to highligh labels"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-delim-face
|
||
'((t (:foreground "firebrick")))
|
||
"Face used to highlight delimiters"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-inst-face
|
||
'((t (:foreground "DodgerBlue3")))
|
||
"Face used to highlight mnemonics"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-reg-face
|
||
'((t (:foreground "DeepSkyBlue3")))
|
||
"Face used to highlight register names"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-dir-face
|
||
'((t (:foreground "brown")))
|
||
"Face used to highlight assembler directives"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-op-face
|
||
'((t (:foreground "firebrick")))
|
||
"Face used to highlight operators"
|
||
:group 'mr:faces)
|
||
|
||
(defface mr:asm-op2-face
|
||
'((t (:foreground "CadetBlue3")))
|
||
"Face used to highlight text operators"
|
||
:group 'mr:faces)
|
||
|
||
(defvar mr:asm-comment2-face 'mr:asm-comment2-face )
|
||
(defvar mr:asm-comment3-face 'mr:asm-comment3-face )
|
||
(defvar mr:asm-glabel-face 'mr:asm-glabel-face )
|
||
(defvar mr:asm-label-face 'mr:asm-label-face )
|
||
(defvar mr:asm-delim-face 'mr:asm-delim-face )
|
||
(defvar mr:asm-inst-face 'mr:asm-inst-face )
|
||
(defvar mr:asm-reg-face 'mr:asm-reg-face )
|
||
(defvar mr:asm-dir-face 'mr:asm-dir-face )
|
||
(defvar mr:asm-op-face 'mr:asm-op-face )
|
||
(defvar mr:asm-op2-face 'mr:asm-op2-face )
|
||
#+end_src
|
||
|
||
*** Provide face
|
||
#+begin_src emacs-lisp :tangle lisp/themes/r2849-faces.el
|
||
(provide 'r2849-faces)
|
||
#+end_src
|
||
|
||
** Default theme
|
||
|
||
#+begin_src emacs-lisp :tangle lisp/themes/r2849-theme.el :mkdirp yes
|
||
;;; -*- lexical-binding: t -*-
|
||
;;; Do not edit by hand! It is generated from readme.org.
|
||
|
||
(deftheme r2849
|
||
"Created 2023-04-13.")
|
||
|
||
(custom-theme-set-variables
|
||
'r2849
|
||
'(inhibit-startup-message t)
|
||
'(scroll-step 1)
|
||
'(truncate-lines t)
|
||
'(tool-bar-mode nil)
|
||
'(scroll-bar-mode nil)
|
||
'(column-number-mode t)
|
||
'(size-indication-mode t)
|
||
'(display-time-24hr-format t)
|
||
'(display-time-day-and-date t)
|
||
'(tab-always-indent nil)
|
||
'(scroll-bar-mode nil)
|
||
'(visible-bell nil))
|
||
|
||
(custom-set-faces
|
||
'(default
|
||
((t
|
||
(
|
||
:inherit nil
|
||
:family "RobotoMono Nerd Font Mono"
|
||
:foundry "monotype"
|
||
:stipple nil
|
||
:background "black"
|
||
:foreground "gray70"
|
||
:height 140
|
||
:weight normal
|
||
))))
|
||
|
||
;; org mode faces
|
||
'(org-document-title ((t (:height 1.50 :bold t :italic nil ))))
|
||
'(org-level-1 ((t (:height 1.20 :bold t :italic nil ))))
|
||
'(org-level-2 ((t (:height 1.10 :bold nil :italic t ))))
|
||
'(org-level-3 ((t (:height 1.05 ))))
|
||
'(org-level-4 ((t (:height 1.00 ))))
|
||
'(org-level-5 ((t (:height 1.00 ))))
|
||
'(org-level-6 ((t (:height 1.00 ))))
|
||
'(org-level-7 ((t (:height 1.00 ))))
|
||
'(org-level-8 ((t (:height 1.00 ))))
|
||
|
||
|
||
;; modeline
|
||
'(mode-line-emphasis ((t (:italic t :bold nil))))
|
||
|
||
;; doom modeline
|
||
'(doom-modeline-persp-name ((t (:inherit mode-line-emphasis :foreground "dark orange" :italic t))))
|
||
|
||
;; common progmode faces
|
||
'(font-lock-comment-face ((t (:foreground "gray50" ))))
|
||
'(font-lock-function-name-face ((t (:foreground "deep pink" ))))
|
||
'(font-lock-keyword-face ((t (:foreground "dark orange" ))))
|
||
'(font-lock-string-face ((t (:foreground "dark sea green" ))))
|
||
|
||
;; current line and line number faces
|
||
'(line-number-current-line ((t (:foreground "white" :italic nil :bold nil ))))
|
||
'(line-number ((t (:foreground "gray50" :italic nil :bold nil ))))
|
||
'(hl-line ((t (:background "gray10" ))))
|
||
)
|
||
|
||
;; Local Variables:
|
||
;; no-byte-compile: t
|
||
;; End:
|
||
|
||
(provide-theme 'r2849)
|
||
#+end_src
|
||
|
||
* Early init
|
||
|
||
Most customizations for Emacs should be put in the normal init file. However, it is sometimes
|
||
necessary to have customizations take effect during Emacs startup earlier than the normal init
|
||
file is processed. Such customizations can be put in the early init file. This file is loaded
|
||
before the package system and GUI is initialized, so in it you can customize variables that
|
||
affect the package initialization process, such as =package-enable-at-startup=, =package-load-list=,
|
||
and =package-user-dir=.
|
||
|
||
#+begin_src emacs-lisp :tangle early-init.el
|
||
;;; -*- lexical-binding: t -*-
|
||
;;; Do not edit by hand! It is generated from readme.org.
|
||
|
||
;;; Disable package.el in favor of straight.el
|
||
;;; Do not allow loading from the package cache.
|
||
(setq package-enable-at-startup nil)
|
||
(setq package-quickstart nil)
|
||
|
||
;;; Make startup faster by reducing the frequency of garbage collection and then use a hook to
|
||
;;; measure Emacs startup time. After init, restore the default garbage collection parameters.
|
||
(let ((default-gc-threshold gc-cons-threshold)
|
||
(default-gc-percentage gc-cons-percentage))
|
||
(setq gc-cons-threshold most-positive-fixnum
|
||
gc-cons-percentage 0.8)
|
||
(add-hook 'after-init-hook
|
||
(lambda ()
|
||
(setq gc-cons-percentage default-gc-percentage
|
||
gc-cons-threshold default-gc-threshold))))
|
||
|
||
;;; profile emacs startup
|
||
(add-hook 'emacs-startup-hook
|
||
(lambda ()
|
||
(message "*** Emacs loaded in %s seconds with %d garbage collections."
|
||
(emacs-init-time "%.2f")
|
||
gcs-done)))
|
||
|
||
;;; Prevent unwanted runtime builds in gccemacs (native-comp); packages are compiled
|
||
;;; ahead-of-time when they are installed and site files are compiled when gccemacs
|
||
;;; is installed.
|
||
(setq comp-deferred-compilation nil)
|
||
|
||
;;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
|
||
(push '(menu-bar-lines . 0) default-frame-alist)
|
||
(push '(tool-bar-lines . 0) default-frame-alist)
|
||
(push '(vertical-scroll-bars) default-frame-alist)
|
||
|
||
;;; Resizing the Emacs frame can be a terribly expensive part of changing the font. By inhibiting
|
||
;;; this, we easily halve startup times with fonts that are larger than the system default.
|
||
(setq frame-inhibit-implied-resize t)
|
||
|
||
;;; disable GUI elements
|
||
(menu-bar-mode -1)
|
||
(tool-bar-mode -1)
|
||
(scroll-bar-mode -1)
|
||
|
||
(setq inhibit-splash-screen t)
|
||
(setq use-file-dialog nil)
|
||
|
||
;;; Default coding system
|
||
(prefer-coding-system 'utf-8)
|
||
(set-default-coding-systems 'utf-8)
|
||
(set-terminal-coding-system 'utf-8)
|
||
(set-keyboard-coding-system 'utf-8)
|
||
(setq buffer-file-coding-system 'utf-8)
|
||
|
||
;;; Default frame size
|
||
(add-to-list 'default-frame-alist '(height . 45))
|
||
(add-to-list 'default-frame-alist '(width . 160))
|
||
|
||
;;; Include paths
|
||
(add-to-list 'load-path (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp" ))
|
||
(add-to-list 'load-path (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp/themes" ))
|
||
(add-to-list 'load-path (substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp/progmodes" ))
|
||
|
||
;;; Theme paths
|
||
(add-to-list 'custom-theme-load-path
|
||
(substitute-in-file-name "$XDG_CONFIG_HOME/emacs/lisp/themes"))
|
||
|
||
;;; tab-bar-mode is incompatible with perspective (as of 2023-05) so do not enable it.
|
||
;;; (setq tab-bar-format '(tab-bar-format-global))
|
||
;;; (tab-bar-mode)
|
||
|
||
(setq tab-width 4)
|
||
|
||
;;; Do not appear a bunch of transient files as untracked in the Git/Plastic repo so
|
||
;;; move them all to another location.
|
||
;;;
|
||
;;; Change the user-emacs-directory to keep unwanted things out of ~/.emacs.d
|
||
(setq user-emacs-directory (expand-file-name "~/.cache/emacs/")
|
||
url-history-file (expand-file-name "url/history" user-emacs-directory))
|
||
|
||
;;; Set the cache folder for native compiled files.
|
||
(when (boundp 'native-comp-eln-load-path)
|
||
(startup-redirect-eln-cache (expand-file-name "eln-cache" user-emacs-directory)))
|
||
|
||
;;; Bootstrap feature =straight= to install no-littering which is needed to keep clean .emacs.d
|
||
;;; It should install itself into the redefined user-emacs-directory !
|
||
|
||
(unless (featurep 'straight)
|
||
;; 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)))
|
||
|
||
;;; Use straight.el for use-package expressions
|
||
(straight-use-package 'use-package)
|
||
|
||
;;; Use no-littering to automatically set common paths to the new user-emacs-directory.
|
||
;;; Load the feature =no-littering= as early as possible. Make sure it is loaded at least
|
||
;;; before any path variables are changed using some other methods.
|
||
(straight-use-package 'no-littering)
|
||
(require 'no-littering)
|
||
|
||
;;; Customizations must be in a separate file not in the user initialization script (init.el).
|
||
(setq custom-file (locate-user-emacs-file "custom.el"))
|
||
(load custom-file 'noerror 'nomessage)
|
||
|
||
;;; Load theme as soon as possible
|
||
(setq custom-safe-themes t)
|
||
(load-theme 'r2849)
|
||
#+end_src
|
||
|
||
* Streamlined configuration with setup.el
|
||
|
||
Use the excellent [[https://www.emacswiki.org/emacs/SetupEl][setup.el]] by [[https://ruzkuku.com][pkal]] as an alternative to =use-package=.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(straight-use-package
|
||
'(setup
|
||
:type git
|
||
:host nil
|
||
:repo "https://git.sr.ht/~pkal/setup"))
|
||
|
||
(require 'setup)
|
||
|
||
;; Uncomment this for debugging purposes
|
||
;; (defun dw/log-require (&rest args)
|
||
;; (with-current-buffer (get-buffer-create "*require-log*")
|
||
;; (insert (format "%s\n"
|
||
;; (file-name-nondirectory (car args))))))
|
||
;; (add-to-list 'after-load-functions #'dw/log-require)
|
||
|
||
#+end_src
|
||
|
||
** :straight
|
||
|
||
StraightEl (on GitHub) is a “next-generation, purely functional package manager
|
||
for the Emacs hacker,” according to its repo. I like using it is all I know.
|
||
Here’s a :straight keyword for setup:
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup-define :straight
|
||
(lambda (recipe)
|
||
`(unless (straight-use-package ',recipe)
|
||
,(setup-quit)))
|
||
:documentation
|
||
"Install RECIPE with `straight-use-package'.
|
||
This macro can be used as HEAD, and will replace itself with the
|
||
first RECIPE's package."
|
||
:repeatable t
|
||
:shorthand (lambda (sexp)
|
||
(let ((recipe (cadr sexp)))
|
||
(if (consp recipe)
|
||
(car recipe)
|
||
recipe))))
|
||
#+end_src
|
||
|
||
** :pkg
|
||
#+begin_src emacs-lisp
|
||
|
||
(defun mr/filter-straight-recipe (recipe)
|
||
(let* ((plist (cdr recipe))
|
||
(name (plist-get plist :straight)))
|
||
(cons (if (and name (not (equal name t)))
|
||
name
|
||
(car recipe))
|
||
(plist-put plist :straight nil))))
|
||
|
||
(setup-define :pkg
|
||
(lambda (&rest recipe)
|
||
`(straight-use-package ',(mr/filter-straight-recipe recipe)))
|
||
:documentation "Install RECIPE via Guix or straight.el"
|
||
:shorthand #'cadr)
|
||
|
||
#+end_src
|
||
** :delay
|
||
|
||
|
||
Delay the loading of a package until a certain amount of idle time has passed.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup-define :delay
|
||
(lambda (&rest time)
|
||
`(run-with-idle-timer ,(or time 1)
|
||
nil ;; Don't repeat
|
||
(lambda () (require ',(setup-get 'feature)))))
|
||
:documentation "Delay loading the feature until a certain amount of idle time has passed.")
|
||
|
||
#+end_src
|
||
** :disabled
|
||
|
||
Used to disable a package configuration, similar to =:disabled= in =use-package=.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup-define :disabled
|
||
(lambda ()
|
||
`,(setup-quit))
|
||
:documentation "Always stop evaluating the body.")
|
||
|
||
#+end_src
|
||
|
||
** :load-after
|
||
|
||
This keyword causes a body to be executed after other packages/features are loaded:
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup-define :load-after
|
||
(lambda (features &rest body)
|
||
(let ((body `(progn
|
||
(require ',(setup-get 'feature))
|
||
,@body)))
|
||
(dolist (feature (if (listp features)
|
||
(nreverse features)
|
||
(list features)))
|
||
(setq body `(with-eval-after-load ',feature ,body)))
|
||
body))
|
||
:documentation "Load the current feature after FEATURES."
|
||
:indent 1)
|
||
|
||
#+end_src
|
||
|
||
** :face
|
||
Customizing faces.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup-define :face
|
||
(lambda (face spec)
|
||
`(custom-set-faces (quote (,face ,spec))))
|
||
:documentation "Customize FACE to SPEC."
|
||
:signature '(face spec ...)
|
||
:debug '(setup)
|
||
:repeatable t
|
||
:after-loaded t)
|
||
|
||
#+end_src
|
||
|
||
* Keyboard bindings
|
||
** ESC cancels all
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
|
||
|
||
#+end_src
|
||
|
||
** Let's Be Evil
|
||
|
||
Some tips can be found here:
|
||
|
||
- [[https://github.com/noctuid/evil-guide][Evil guide]]
|
||
- [[https://nathantypanski.com/blog/2014-08-03-a-vim-like-emacs-config.html][Vim like Emacs config]]
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup (:straight undo-tree)
|
||
(setq undo-tree-auto-save-history nil)
|
||
(global-undo-tree-mode 1))
|
||
|
||
(setup (:straight goto-chg))
|
||
(setup (:straight evil)
|
||
|
||
;; Pre-load configuration
|
||
(setq evil-want-integration t)
|
||
(setq evil-want-keybinding nil)
|
||
(setq evil-want-C-u-scroll t)
|
||
(setq evil-want-C-i-jump nil)
|
||
(setq evil-respect-visual-line-mode t)
|
||
(setq evil-undo-system 'undo-tree)
|
||
|
||
;; Activate the Evil
|
||
(evil-mode 1)
|
||
|
||
;; Set Emacs state modes
|
||
(dolist (mode '(custom-mode
|
||
eshell-mode
|
||
git-rebase-mode
|
||
erc-mode
|
||
circe-server-mode
|
||
circe-chat-mode
|
||
circe-query-mode
|
||
sauron-mode
|
||
term-mode))
|
||
(add-to-list 'evil-emacs-state-modes mode))
|
||
|
||
(define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
|
||
(define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join)
|
||
|
||
;; Use visual line motions even outside of visual-line-mode buffers
|
||
(evil-global-set-key 'motion "j" 'evil-next-visual-line)
|
||
(evil-global-set-key 'motion "k" 'evil-previous-visual-line)
|
||
|
||
(evil-set-initial-state 'messages-buffer-mode 'normal)
|
||
(evil-set-initial-state 'dashboard-mode 'normal))
|
||
|
||
(setup (:pkg evil-collection)
|
||
;; Is this a bug in evil-collection?
|
||
(setq evil-collection-company-use-tng nil)
|
||
(:load-after evil
|
||
(:option evil-collection-outline-bind-tab-p nil
|
||
(remove evil-collection-mode-list) 'lispy
|
||
(remove evil-collection-mode-list) 'org-present)
|
||
(evil-collection-init)))
|
||
#+end_src
|
||
|
||
** Keybinding Panel (which-key)
|
||
|
||
[[https://github.com/justbur/emacs-which-key][which-key]] is great for getting an overview of what keybindings are available
|
||
based on the prefix keys you entered. Learned about this one from Spacemacs.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup (:pkg which-key)
|
||
(:load-after diminish
|
||
(diminish 'which-key-mode)
|
||
(which-key-mode)
|
||
(setq which-key-idle-delay 0.3)))
|
||
|
||
#+end_src
|
||
|
||
** Simplify leader bindings (general.el)
|
||
|
||
[[https://github.com/noctuid/general.el][general.el]] is a fantastic library for defining prefixed keybindings, especially
|
||
in conjunction with Evil modes.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(setup (:pkg general)
|
||
(general-evil-setup t)
|
||
|
||
(general-create-definer mr/leader-key
|
||
:keymaps '(normal insert visual emacs)
|
||
:prefix "SPC"
|
||
:global-prefix "C-SPC")
|
||
|
||
(general-create-definer mr/ctrl-c-keys
|
||
:prefix "C-c"))
|
||
|
||
#+end_src
|
||
|