25 KiB
Aner's Emacs Base Configuration
Base setup
Bootstrapping
Partially based on this post on StackOverflow. Setting backups, external custom file.
(setq
vc-follow-symlinks t ;Follow symlinks
;Backup location TODO FIND IF RELEVANT FOR NIX
backup-directory-alist `(("." . "~/.cache/emacs-backups/"))
auto-save-file-name-transforms '((".*" "~/.cache/emacs-backups/" t))
backup-by-copying t ;Always copy never link
delete-old-versions t ;Auto delete old backups
kept-new-versions 6 ;Number of versions to keep
kept-old-versions 2
version-control t ;Backups for files
gc-cons-threshold 100000000 ;Setting garbage collection to 100M.
;Set external cusotm file
custom-file (expand-file-name "custom.el" user-emacs-directory)
)
Load custom file
(if (file-exists-p custom-file) (load custom-file))
Utility functions
CL
CL is a base library that has a bunch of useful stuff, primarily for lists. We are using the built-in version, no need to pull from anywhere. This just makes sure the code is loaded early on for later use.
(require 'cl-lib)
Copy file name
Defining a function to copy filename.
(defun azos/copy-file-name () (interactive)
(let ((fpath buffer-file-name))
(if fpath (kill-new fpath) (message "No current file!"))))
Font candidate
(defun azos/font-candidate (&rest fonts)
"Return existing font which first match."
(cl-find-if (lambda (f) (find-font (font-spec :name f))) fonts))
Regex match function
(defun azos/re-seq (regexp string)
"Get a list of all regexp matches in a string"
(save-match-data
(let ((pos 0)
matches)
(while (string-match regexp string pos)
(push (match-string 0 string) matches)
(setq pos (match-end 0)))
matches)))
Keymap setup
In this section global keybindings are defined using a global minor mode.
First, utility functions that will be bound are defined.
The first, azos/set-window-width is a helper function that resizes a window. Used because I wanted a function
that resizes a window to 85 cols easily.
The second, azos/open-conf-file, opens the configuration file.
(defun azos/set-window-width (n)
(adjust-window-trailing-edge (selected-window) ( - n (window-width)) t))
(defun azos/open-conf-file ()
(interactive)
(find-file (concat user-emacs-directory "config.org")))
Now let's define keybindings. To start, we'd like M-o to be available to us, so let's unbind it.
;Unbind face menu map
(define-key global-map (kbd "M-o") nil)
This creates an "open keymap", a bunch of keybindings we'll use to open basic applications and files.
This will be mapped to M-o, and things will be opened form this sub-menu.
We'll start it with a binding to open the conf file with M-o o (MOO!)
Setting of keybindings based on this
;We'll define a basic keymap and already load window-manip funcs
(defvar azos/global-minor-mode/open-keymap
(let ((map (make-sparse-keymap)))
(define-key map (kbd "o") 'azos/open-conf-file)
map)
"global keymap for opening stuff on azos")
At this stage a minor-mode-map is defined with keybindings, and an accompanying minor-mode is added.
(defvar azos/global-minor-mode/keymap
(let ((map (make-sparse-keymap)))
;Window movement and manipulation
(define-key map (kbd "M-h") 'windmove-left)
(define-key map (kbd "M-l") 'windmove-right)
(define-key map (kbd "M-k") 'windmove-up)
(define-key map (kbd "M-j") 'windmove-down)
(define-key map (kbd "M-<left>") 'windmove-left)
(define-key map (kbd "M-<right>") 'windmove-right)
(define-key map (kbd "M-<up>") 'windmove-up)
(define-key map (kbd "M-<down>") 'windmove-down)
(define-key map (kbd "M-d M-d") 'delete-window)
(define-key map (kbd "M-d D") 'kill-buffer-and-window)
(define-key map (kbd "M-\\") 'split-window-horizontally)
(define-key map (kbd "M-\-") 'split-window-vertically)
(define-key map (kbd "M-d R") (lambda () (interactive)
(set-window-width 85)))
(define-key map (kbd "M-o") azos/global-minor-mode/open-keymap)
map)
"azos/global-minor-mode keymap.")
(define-minor-mode azos/global-minor-mode
"A minor mode for azos global keymaps."
:init-value t
:lighter "azos"
:keymap azos/global-minor-mode/keymap)
(azos/global-minor-mode 1)
This keymap will be referenced many times during this document at relevant points.
Keymaps are included with relevant sections.
EVIL mode
This section binds keys for changing window size. Done here because can only do after evil loads.
(setq evil-want-keybinding nil)
(use-package evil
:init
(setq evil-want-C-i-jump nil)
:config
(require 'evil )
(evil-mode 1)
:bind
(:map azos/global-minor-mode/keymap
("M-w h" . evil-window-decreace-width)
("M-w l" . evil-window-increase-width)
("M-w k" . evil-window-decrease-height)
("M-w j" . evil-window-increase-height))
)
Loading evil collection. Functions from this package will be referenced many times later in the configuration.
(use-package evil-collection
:config
(setq evil-collection-setup-minibuffer t)
)
(defvar azos/evil-color-normal "LightGoldenrod1")
(defvar azos/evil-color-emacs "LightBlue1")
(defvar azos/evil-color-insert "PaleGreen1")
(defvar azos/evil-color-replace "LightPink")
(defvar azos/evil-color-motion "LightCyan")
(defvar azos/evil-color-visual "LightGray")
(defvar azos/evil-color-operate "sandy brown")
IVY
Enabling IVY. Taken from their website.
Using ivy, hydra, counsel.
(use-package ivy
:custom
(ivy-use-virtual-buffers t)
(enable-recursive-minibuffers t)
(ivy-count-format "(%d/%d) ")
:config
(ivy-mode 1)
)
(use-package ivy-hydra
:after ivy)
(use-package ivy-avy
:after ivy)
(use-package counsel
:after ivy
:bind
(:map azos/global-minor-mode/keymap
("M-i" . counsel-imenu)
("M-b" . counsel-switch-buffer)
("C-x C-f" . counsel-find-file))
(:map azos/global-minor-mode/open-keymap
("l" . counsel-linux-app))
)
Using swiper. Replacing evil search with swiper search.
(use-package swiper
:after ivy evil
:config
(setq evil-search-module 'swiper-isearch)
:bind
(:map azos/global-minor-mode/keymap
("C-s" . swiper-isearch))
)
Setting up keymaps
(evil-collection-ivy-setup)
Assorted utility functions
UI
General
Clean UI
Disabling the toolbar, the splash-screen, the menu-bar and the scroll-bar
(menu-bar-mode -1) ; no menu bar
(when (display-graphic-p)
(tool-bar-mode -1) ; no tool bar with icons
(scroll-bar-mode -1) ; no scroll bars
(set-fringe-mode 0))
Background color
(add-to-list 'default-frame-alist '(background-color . "LightYellow"))
Fringe color
While we don't actually want fringes (almost at all), some frames use them.
(set-face-attribute 'fringe nil :background "LemonChiffon1")
Window dividers
(setq window-divider-default-bottom-width 1
window-divider-default-places 'bottom-only)
(window-divider-mode 1)
Easy Prompt
(defalias 'yes-or-no-p 'y-or-n-p)
Minibuff
(add-hook 'minibuffer-setup-hook
(lambda ()
(make-local-variable 'face-remapping-alist)
(add-to-list 'face-remapping-alist
'(default (:background "WhiteSmoke")))))
Bell
Disable bell, who needs the bell?
(setq ring-bell-function (lambda () ()))
Olivetti
Useful to have even if I rarely use it.
(use-package olivetti
:init
(setq olivetti-body-width 96))
Modeline
Setting colors
(set-face-attribute 'mode-line nil :box nil :background "AliceBlue")
(set-face-attribute 'mode-line-inactive nil :box nil :background "LightYellow3")
We use this stackoverflow page to make left\right aligned stuff.
We use this article to try and limit the mode name length.
(setq evil-normal-state-tag
(propertize " NORMAL " 'face
(list :background azos/evil-color-normal))
evil-emacs-state-tag
(propertize " EMACS " 'face
(list :background azos/evil-color-emacs))
evil-insert-state-tag
(propertize " INSERT " 'face
(list :background azos/evil-color-insert))
evil-replace-state-tag
(propertize " REPLACE " 'face
(list :background azos/evil-color-replace))
evil-motion-state-tag
(propertize " MOTION " 'face
(list :background azos/evil-color-motion))
evil-visual-state-tag
(propertize " VISUAL " 'face
(list :background azos/evil-color-visual))
evil-operator-state-tag
(propertize " OPERATE " 'face
(list :background azos/evil-color-operate)))
(defun azos/modeline/modeline-render (left right)
"Return a string of `window-width' length containing LEFT, and RIGHT
aligned respectively."
(let* ((available-width (- (window-width) (length left) 2)))
(format (format " %%s %%%ds " available-width) left right)))
(setq-default mode-line-buffer-identification
(list -80 (propertized-buffer-identification "%12b")))
(setq-default mode-line-format
'((:eval (azos/modeline/modeline-render
;;Left
(concat
(propertize (format-mode-line "%b") 'face '((:foreground "maroon")))
(format-mode-line " (%m) "))
;;Right
(concat
(format-mode-line "%5lL%4cC ")
evil-mode-line-tag)))))
Notifications
(require 'notifications)
Which-Key
(use-package which-key
:config
(which-key-mode))
Text
YASnippet
Loading yasnippet. Useful for snippeting. Mode-specific snippets defined in relevant sections.
(use-package yasnippet
:config
(yas-global-mode 1)
)
Text font
This section configures the base fonts. We select fonts if available (have configurations for good defaults in Linux and Windows).
Also setting default fixed-pitch and variable-pitch fonts.
Setting font size to 10. The value to place is font-size * 10
Font size 12 for variable pitch.
The function font-candidate is from https://www.gnu.org/software/emacs/manual/html_mono/cl.html.
(let ((variable-font (azos/font-candidate
"Liberation Serif" "Microsoft Sans Serif")))
(if variable-font
(set-face-attribute 'variable-pitch nil :font variable-font)))
(let ((fixed-font (azos/font-candidate
"Source Code Pro" "LiberationMono" "Consolas")))
(if fixed-font (progn
(set-face-attribute 'default nil :font fixed-font)
(set-face-attribute 'fixed-pitch nil :font fixed-font))))
(set-face-attribute 'default nil :height 100)
(set-face-attribute 'variable-pitch nil
:height 130
:weight 'normal
:width 'normal)
(set-face-attribute 'fixed-pitch nil
:height 100
:weight 'normal
:width 'normal)
(defun azos/default-variable-pitch ()
(face-remap-add-relative 'default '(:inherit 'variable-pitch)))
Line numbering
We want line numbering, but only in modes where it makes sense.
To do this, a custom minor-mode, azos/global-linum-mode, is created.
This mode selectively activates linum-mode if the mode is not one of a selected exempt modes.
These exempt modes are defined in display-line-numbers-exempt-modes.
Taken from this wiki entry.
(use-package display-line-numbers
:init
(defcustom azos/display-line-numbers-exempt-modes
'(vterm-mode
eshell-mode
shell-mode
term-mode
ansi-term-mode
magit-mode
magit-diff-mode
notmuch-hello
pdf-view-mode)
"Major modes on which to disable the linum mode, exempts them."
:group 'display-line-numbers
:type 'list
:version "green")
(define-global-minor-mode azos/global-linum-mode
display-line-numbers-mode
(lambda () (if (and
(not (apply 'derived-mode-p
azos/display-line-numbers-exempt-modes))
(not (minibufferp)))
(display-line-numbers-mode))))
(setq display-line-numbers-type 'visual
display-line-numbers-grow-only 1
display-line-numbers-width-start 1)
:config
(azos/global-linum-mode 1)
(set-face-attribute 'line-number nil
:family (face-attribute 'fixed-pitch :family))
)
Line highlight
Highlighting line with cursor.
Modification done to use EVIL colors on highlighted line.
(global-hl-line-mode)
(set-face-attribute 'hl-line nil :background azos/evil-color-emacs)
(defface hl-line-normal
(list (list t (list :inherit 'hl-line :background azos/evil-color-normal
:extend t)))
"Highlight face for evil normal mode."
:group 'hl-line)
(defface hl-line-insert
(list (list t (list :inherit 'hl-line :background azos/evil-color-insert
:extend t)))
"Highlight face for evil insert mode."
:group 'hl-line)
(defface hl-line-emacs
(list (list t (list :inherit 'hl-line :background azos/evil-color-emacs
:extend t)))
"Highlight face for evil insert mode."
:group 'hl-line)
(defface hl-line-replace
(list (list t (list :inherit 'hl-line :background azos/evil-color-replace
:extend t)))
"Highlight face for evil insert mode."
:group 'hl-line)
(defface hl-line-motion
(list (list t (list :inherit 'hl-line :background azos/evil-color-motion
:extend t)))
"Highlight face for evil insert mode."
:group 'hl-line)
(defface hl-line-visual
(list (list t (list :inherit 'hl-line :background azos/evil-color-visual
:extend t)))
"Highlight face for evil insert mode."
:group 'hl-line)
(defface hl-line-operate
(list (list t (list :inherit 'hl-line :background azos/evil-color-operate
:extend t)))
"Highlight face for evil insert mode."
:group 'hl-line)
(defun azos/hl-line-evil/set-hl-state (state-face)
"Refresh hl-line to be state-face"
(progn
(global-hl-line-unhighlight)
(setq-local hl-line-face state-face)
(global-hl-line-highlight)))
(add-hook 'evil-insert-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-insert)))
(add-hook 'evil-normal-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-normal)))
(add-hook 'evil-emacs-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-emacs)))
(add-hook 'evil-replace-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-replace)))
(add-hook 'evil-motion-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-motion)))
(add-hook 'evil-visual-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-visual)))
(add-hook 'evil-operate-state-entry-hook
(lambda () (azos/hl-line-evil/set-hl-state 'hl-line-operate)))
Line wrap
Don't want to have to scroll to see more chars.
(global-visual-line-mode t)
Parenthesis
Highlight matching parenthesis
(show-paren-mode 1)
Tabs
Using spaces instead of tabs, default offset is 4.
(setq-default indent-tabs-mode nil
tab-width 4
c-basic-offset 4
tab-always-indent 'complete)
BIDI and lang
Setting up Hebrew as alternative input, using bidi mode so that every line is aligned left\right accordingly.
(setq-default default-input-method "hebrew"
bidi-display-reordering t
bidi-paragraph-direction 'nil)
(defun azos/set-bidi-env ()
(setq bidi-paragraph-direction 'nil)
)
(define-key azos/global-minor-mode/keymap
(kbd "C-SPC") 'toggle-input-method)
Whitespace mode
We define a custom global-whitespace-mode in order to enable it only on relevant modes.
We check if the current mode doesn't derive from a set of blacklisted mode, the main culprit being terminal modes where whitespace occur naturally and are a pain to see all the time.
(setq-default whitespace-style
'(face tabs trailing tab-mark
lines-tail indentation))
(defun azos/whitespace-mode-func ()
(interactive)
(if (derived-mode-p 'text-mode 'prog-mode 'org-mode)
(whitespace-mode 1) (whitespace-mode -1)))
(add-hook 'after-change-major-mode-hook 'azos/whitespace-mode-func)
Commenter
Quick keybindings to comment out regions.
(use-package evil-nerd-commenter
:config
(define-key evil-normal-state-map (kbd "C-;")
'evilnc-comment-or-uncomment-lines))
Company mode
Auto completion framework.
(use-package company
:ensure t
:defer t
:init (add-hook 'after-init-hook 'global-company-mode)
:config
;; (use-package company-irony :ensure t :defer t)
(setq
company-minimum-prefix-length 2
company-show-numbers t
company-tooltip-limit 20
company-idle-delay 0.2
)
:bind ("C-;" . company-complete-common)
;; :hook (irony-mode . company-mode)
)
Folding
(add-hook 'prog-mode-hook 'hs-minor-mode)
Mode specific
Undo tree
Loading undo-tree for undo/redo functionality with evil.
Redo taken from https://github.com/syl20bnr/spacemacs/issues/14036
(use-package undo-tree
:after evil
:config
(evil-set-undo-system 'undo-tree)
(setq undo-tree-history-directory-alist
(list (cons "." (concat user-emacs-directory "undo-tree"))))
(global-undo-tree-mode 1)
)
Projectile
Startup up projectile.
A config line here disables modeline display because I don't want my modeline to be cluttered.
Mapping modeline commands to M-p prefix. Also adding a shortcut to add project.
(use-package projectile
:config
(projectile-mode +1)
(setq projectile-mode-line-function (lambda () ""))
:bind
(:map projectile-command-map
("a" . projectile-add-known-project)
)
(:map azos/global-minor-mode/keymap
("M-p" . projectile-command-map))
)
Ivy for projectile: Parts taken from this post and this post from StackOverflow.
Helps with many functions to use counsel's/ivy's autocomplete with projectile.
(use-package counsel-projectile
:after counsel projectile
:config
(counsel-projectile-mode +1)
(setq projectile-completion-system 'ivy)
;Making counsel start with empty regex
(when (commandp 'counsel-M-x)
(global-set-key [remap execute-extended-command] 'counsel-M-x))
(setcdr (assoc 'counsel-M-x ivy-initial-inputs-alist) "")
)
Tramp
Ensuring tramp is loaded, and loading counsel-tramp for easy tramping.
(use-package tramp
:straight (:type built-in))
(use-package counsel-tramp)
Dired
Need to autoload dired-x for dired-omit
;; (autoload 'dired-omit-mode "dired-x")
(setq dired-omit-files "^\\...+$")
(add-hook 'dired-mode-hook 'dired-omit-mode)
(add-hook 'dired-mode-hook 'dired-hide-details-mode)
(evil-collection-dired-setup)
(use-package dired-subtree
:config
(evil-collection-define-key 'normal 'dired-mode-map
(kbd "SPC") 'dired-subtree-toggle
(kbd "TAB") 'dired-subtree-cycle
)
(setq dired-subtree-use-backgrounds nil)
;Evil collection binds these keys, we need them for window movement
(evil-collection-define-key 'normal 'dired-mode-map
(kbd "M-j") nil
(kbd "M-k") nil)
)
Magit
(use-package magit
:config
(evil-collection-magit-setup)
:bind
(:map anerenv/global-minor-mode/open-keymap
("g" . 'magit-status))
)
Org
Base
(require 'org-faces)
(defun anerenv/set-org-mode-fixed-pitch-faces ()
(mapc (lambda (face) (set-face-attribute face nil
:font (face-attribute 'fixed-pitch :font)
:height (face-attribute 'fixed-pitch :height)))
`(line-number
org-block
org-special-keyword
org-drawer
org-todo
org-done
org-priority
org-checkbox
org-block-end-line
org-block-begin-line
org-table
org-verbatim)))
(use-package org
:hook
(org-mode . variable-pitch-mode)
(org-mode . anerenv/set-bidi-env)
(org-mode . (lambda ()
(setq-local whitespace-style '(face tabs trailing tab-mark
indentation))))
:config
(anerenv/set-org-mode-fixed-pitch-faces)
(setq org-src-tab-acts-natively t
org-adapt-indentation nil
org-startup-folded t
org-hide-emphasis-markers t)
(set-face-attribute 'org-code nil
:family (face-attribute 'fixed-pitch :family))
(set-face-attribute 'org-block nil
:family (face-attribute 'fixed-pitch :family))
:bind
("C-a" . nil)
("C-a l" . org-toggle-latex-fragment)
)
#+end_src
Code blocks
The following displays the contents of code blocks in Org-mode files using the major-mode of the code. It also changes the behavior of TAB to as if it were used in the appropriate major mode.
(setq org-src-fontify-natively t
org-src-tab-acts-natively t
org-src-preserve-indentation t)
(set-face-attribute 'org-block nil
:background "LemonChiffon1")
(set-face-attribute 'org-block-begin-line nil
:background "LightYellow2")
(set-face-attribute 'org-block-end-line nil
:background "LightYellow2")
Babel
Define languages to use
(require 'ob)
(require 'ob-tangle)
;; TODO Not sure I like this here
(org-babel-do-load-languages
'org-babel-load-languages
'((shell . t)
(emacs-lisp . t)
(python . t)
(org . t)
(lilypond . t)
(latex . t)
(js . t)
(java . t)
(dot . t)
(C . t)))
;; TODO move these around
;; (add-to-list 'org-src-lang-modes (quote ("dot". graphviz-dot)))
;; (add-to-list 'org-src-lang-modes (quote ("plantuml" . fundamental)))
;; (add-to-list 'org-babel-tangle-lang-exts '("clojure" . "clj"))
This section makes code-indentation correction work inside source blocks. Taken from: https://github.com/emacs-evil/evil/issues/1288
(defun anerenv/org/evil-org-insert-state-in-edit-buffer (fun &rest args)
"Bind `evil-default-state' to `insert' before calling FUN with ARGS."
(let ((evil-default-state 'insert)
;; Force insert state
evil-emacs-state-modes
evil-normal-state-modes
evil-motion-state-modes
evil-visual-state-modes
evil-operator-state-modes
evil-replace-state-modes)
(apply fun args)
(evil-refresh-cursor)))
(advice-add 'org-babel-do-key-sequence-in-edit-buffer
:around #'anerenv/org/evil-org-insert-state-in-edit-buffer)
Provide
(provide 'azos-emacs-base)