271 lines
8.6 KiB
EmacsLisp
271 lines
8.6 KiB
EmacsLisp
;;; init.el --- Minimal Emacs: built-ins + evil + consult -*- lexical-binding: t; -*-
|
|
;; Learning: F1 k KEY, F1 f FUNCTION, F1 v VARIABLE,
|
|
;; F1 m (modes), F1 b (bindings).
|
|
;; C-h works too, except in normal/motion state where it moves
|
|
;; to the window on the left.
|
|
;;; Code:
|
|
|
|
;;;; 1. Emac setup, files, packages and paths
|
|
(let ((bin (expand-file-name "~/.local/bin")))
|
|
(add-to-list 'exec-path bin)
|
|
(setenv "PATH" (concat bin path-separator (getenv "PATH"))))
|
|
|
|
;; all the emacs runtime files live in var/.
|
|
;; early-init.el redirects eln-cache and elpa.
|
|
(defvar my/var (locate-user-emacs-file "var/"))
|
|
(make-directory (concat my/var "transient/") t)
|
|
(setq savehist-file (concat my/var "history")
|
|
save-place-file (concat my/var "places.eld")
|
|
recentf-save-file (concat my/var "recentf.eld")
|
|
project-list-file (concat my/var "projects.eld")
|
|
transient-history-file (concat my/var "transient/history.el")
|
|
transient-levels-file (concat my/var "transient/levels.el")
|
|
transient-values-file (concat my/var "transient/values.el"))
|
|
|
|
;; Custom output goes to its own file, not here.
|
|
(setq custom-file (expand-file-name "var/custom.el" user-emacs-directory))
|
|
(load custom-file 'noerror 'nomessage)
|
|
|
|
;; No session manager save: stops the empty session.* files.
|
|
(add-hook 'emacs-save-session-functions #'always)
|
|
|
|
;; Package GNU, NonGNU are built in melpa additional lowest priority.
|
|
(require 'package)
|
|
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
|
|
(setq package-archive-priorities '(("gnu" . 3) ("nongnu" . 2) ("melpa" . 1)))
|
|
(package-initialize)
|
|
|
|
(require 'use-package)
|
|
(setq use-package-always-ensure t)
|
|
|
|
;;;; 2. Ui settings, theme setup and fonts
|
|
(add-to-list 'default-frame-alist '(fullscreen . maximized))
|
|
(menu-bar-mode -1)
|
|
(tool-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
(blink-cursor-mode -1)
|
|
|
|
(setq inhibit-startup-screen t
|
|
initial-scratch-message nil
|
|
ring-bell-function #'ignore
|
|
use-short-answers t
|
|
scroll-conservatively 101
|
|
scroll-margin 15)
|
|
|
|
(setq-default indent-tabs-mode nil
|
|
tab-width 4)
|
|
|
|
(global-hl-line-mode 1)
|
|
(global-display-line-numbers-mode 1)
|
|
(pixel-scroll-precision-mode 1)
|
|
(column-number-mode 1)
|
|
(electric-pair-mode 1)
|
|
(which-key-mode 1)
|
|
|
|
(add-to-list 'warning-suppress-types '(files))
|
|
(use-package jbeans-theme
|
|
:config
|
|
(load-theme 'jbeans t))
|
|
|
|
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font" :height 120)
|
|
(setq-default line-spacing '(4 . 4))
|
|
|
|
;; set mode line at the top of each window
|
|
(setq-default header-line-format mode-line-format mode-line-format nil)
|
|
(set-face-attribute 'header-line nil :inherit 'mode-line :background "#131313")
|
|
|
|
;; Command launcher.
|
|
(keymap-global-set "C-S-p" #'execute-extended-command)
|
|
|
|
;; Escape behaves like C-g
|
|
(keymap-global-set "<escape>" #'keyboard-quit)
|
|
(keymap-set minibuffer-local-map "<escape>" #'abort-minibuffers)
|
|
|
|
;;;; 3. Files
|
|
(setq make-backup-files nil
|
|
auto-save-default nil
|
|
auto-save-list-file-prefix nil
|
|
create-lockfiles nil
|
|
confirm-kill-processes nil
|
|
vc-follow-symlinks t)
|
|
|
|
(save-place-mode 1)
|
|
(savehist-mode 1)
|
|
(recentf-mode 1)
|
|
(global-auto-revert-mode 1)
|
|
|
|
;;;; 4. Completion
|
|
;; C-n/C-p navigates up and down, section 5 stops evil from overrriding
|
|
(use-package vertico
|
|
:init
|
|
(vertico-mode 1)
|
|
:custom
|
|
(vertico-count 15))
|
|
|
|
(setq completion-styles '(basic partial-completion flex)
|
|
completion-ignore-case t
|
|
read-buffer-completion-ignore-case t
|
|
read-file-name-completion-ignore-case t
|
|
completions-format 'one-column
|
|
isearch-lazy-count t
|
|
xref-search-program 'ripgrep)
|
|
|
|
(use-package vertico-posframe
|
|
:after vertico
|
|
:custom
|
|
(vertico-posframe-poshandler #'posframe-poshandler-frame-center)
|
|
:config
|
|
(vertico-posframe-mode 1))
|
|
|
|
;;;; 5. Evil
|
|
(defvar evil-mode-buffers nil)
|
|
|
|
(defun my/insert-tab ()
|
|
"Insert a literal tab character."
|
|
(interactive)
|
|
(insert "\t"))
|
|
|
|
(use-package evil
|
|
:init
|
|
(setq evil-want-C-u-scroll t
|
|
evil-undo-system 'undo-redo
|
|
evil-want-keybinding nil)
|
|
|
|
:config
|
|
(evil-mode 1)
|
|
|
|
(evil-define-key 'insert 'global
|
|
(kbd "TAB") #'my/insert-tab
|
|
(kbd "<tab>") #'my/insert-tab
|
|
;; complete using the buffer's completion sources.
|
|
(kbd "C-SPC") #'completion-at-point
|
|
(kbd "C-@") #'completion-at-point)
|
|
|
|
;; C-p, C-f and C-b are assigned to Consult below.
|
|
(evil-define-key '(normal motion) 'global
|
|
(kbd "C-e") #'dired-jump))
|
|
|
|
(use-package evil-collection
|
|
:after evil
|
|
:custom
|
|
;; Don't add its global [b ]b [e ]e ... bindings.
|
|
(evil-collection-want-unimpaired-p nil)
|
|
:config
|
|
(evil-collection-init '(info)))
|
|
|
|
;; Minibuffer. Vertico
|
|
(with-eval-after-load 'vertico
|
|
(evil-define-key '(insert normal) vertico-map
|
|
(kbd "C-n") #'vertico-next
|
|
(kbd "C-p") #'vertico-previous
|
|
(kbd "TAB") #'vertico-insert
|
|
(kbd "<tab>") #'vertico-insert))
|
|
|
|
(evil-define-key '(insert normal) minibuffer-local-map
|
|
(kbd "<escape>") #'abort-minibuffers)
|
|
|
|
;; Window navigation (normal/motion state): Ctrl+hjkl move, Ctrl+` closes.
|
|
(evil-define-key '(normal motion) 'global
|
|
(kbd "C-h") #'windmove-left
|
|
(kbd "C-j") #'windmove-down
|
|
(kbd "C-k") #'windmove-up
|
|
(kbd "C-l") #'windmove-right
|
|
(kbd "C-q") #'delete-window)
|
|
|
|
;; Result buffers: hjkl motions and RET to jump to a result.
|
|
(dolist (m '(occur-mode grep-mode xref--xref-buffer-mode))
|
|
(evil-set-initial-state m 'motion))
|
|
|
|
(with-eval-after-load 'compile
|
|
(evil-define-key 'motion compilation-mode-map
|
|
(kbd "RET") #'compile-goto-error))
|
|
|
|
(with-eval-after-load 'grep
|
|
(evil-define-key 'motion grep-mode-map
|
|
(kbd "RET") #'compile-goto-error))
|
|
|
|
(with-eval-after-load 'replace
|
|
(evil-define-key 'motion occur-mode-map
|
|
(kbd "RET") #'occur-mode-goto-occurrence))
|
|
|
|
(with-eval-after-load 'xref
|
|
(evil-define-key 'motion xref--xref-buffer-mode-map
|
|
(kbd "RET") #'xref-goto-xref))
|
|
|
|
;;;; 6. Consult
|
|
;; Requires the external `fd` and `rg` executables. Both honour .ignore files,
|
|
;; so keep excludes (node_modules, elpa, ...) in ~/.ignore or per-project .ignore.
|
|
;; Searches start at the project root when there is one, else the current directory.
|
|
(use-package consult
|
|
:config
|
|
;; Hide noise from the buffer picker.
|
|
(dolist (re '("\\`\\*scratch\\*\\'"
|
|
"\\`\\*Messages\\*\\'"
|
|
"\\`\\*Warnings\\*\\'"
|
|
"\\`\\*xref\\*\\'"))
|
|
(add-to-list 'consult-buffer-filter re))
|
|
|
|
;; Don't re-run the preview on every keystroke while typing a search.
|
|
(consult-customize consult-ripgrep
|
|
:preview-key '(:debounce 0.4 any)))
|
|
|
|
;; Bind immediately, without waiting for consult to load.
|
|
(evil-define-key '(normal motion) 'global
|
|
(kbd "C-p") #'consult-fd
|
|
(kbd "C-f") #'consult-ripgrep
|
|
(kbd "C-b") #'consult-buffer)
|
|
|
|
;;;; 7. Dired
|
|
(setq dired-dwim-target t
|
|
dired-kill-when-opening-new-dired-buffer t
|
|
dired-listing-switches "-agho --group-directories-first"
|
|
dired-recursive-copies 'always
|
|
delete-by-moving-to-trash t)
|
|
|
|
(with-eval-after-load 'dired
|
|
(evil-set-initial-state 'dired-mode 'motion)
|
|
|
|
(evil-define-key 'motion dired-mode-map
|
|
"h" #'dired-up-directory
|
|
"l" #'dired-find-file
|
|
(kbd "RET") #'dired-find-file))
|
|
|
|
;;;; 8. Build
|
|
(setq compile-command "./build.sh"
|
|
compilation-read-command nil
|
|
compilation-ask-about-save nil
|
|
compilation-scroll-output t)
|
|
|
|
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)
|
|
|
|
;; Compilation buffer always at the bottom, full frame width.
|
|
(add-to-list 'display-buffer-alist
|
|
'((derived-mode . compilation-mode)
|
|
(display-buffer-reuse-window display-buffer-at-bottom)
|
|
(window-height . 0.45)))
|
|
|
|
;; C-c C-c builds from the project root.
|
|
;; C-c b asks first: the prompt is pre-filled with ./build.sh, so type the args after it.
|
|
(defun my/project-compile-prompt ()
|
|
"Run `project-compile', prompting to edit the command."
|
|
(interactive)
|
|
(let ((compilation-read-command t))
|
|
(call-interactively #'project-compile)))
|
|
|
|
(keymap-global-set "C-c C-c" #'project-compile)
|
|
(keymap-global-set "C-c b" #'my/project-compile-prompt)
|
|
|
|
;; Focus the compilation window when a build starts.
|
|
(add-hook 'compilation-start-hook
|
|
(lambda (proc)
|
|
(when-let* ((win (get-buffer-window (process-buffer proc))))
|
|
(select-window win))))
|
|
|
|
;;;; 9. Modules - additional functionality outside of the core init.el
|
|
;; Magit and the :git command live in modules/git.el.
|
|
(load (locate-user-emacs-file "modules/git.el") nil 'nomessage)
|
|
;; Setup for dev languages, modes, lsp and lsp keybinds.
|
|
(load (locate-user-emacs-file "modules/development.el") nil 'nomessage)
|
|
|
|
;;; init.el ends here
|