#+STARTUP: overview #+TITLE: My Emacs #+CREATOR: Laurens Miers #+LANGUAGE: en [[./img/dash_logo.png]] * Elpaca #+begin_src emacs-lisp (defvar elpaca-installer-version 0.7) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (< emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (load "./elpaca-autoloads"))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) #+end_src #+begin_src emacs-lisp ;; Install use-package support (elpaca elpaca-use-package ;; Enable use-package :ensure support for Elpaca. (elpaca-use-package-mode) ) #+end_src * Vertico-stack ** Vertico #+BEGIN_SRC emacs-lisp ;; Enable vertico (use-package vertico :ensure t ;; :custom ;; (vertico-scroll-margin 0) ;; Different scroll margin ;; (vertico-count 20) ;; Show more candidates ;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer ;; (vertico-cycle t) ;; Enable cycling for `vertico-next/previous' :init (vertico-mode)) #+END_SRC ** Consult #+BEGIN_SRC emacs-lisp (use-package consult :ensure t ;; Replace bindings. Lazily loaded by `use-package'. :bind (;; C-c bindings in `mode-specific-map' ;; ("C-c M-x" . consult-mode-command) ;; ("C-c h" . consult-history) ;; ("C-c k" . consult-kmacro) ;; ("C-c m" . consult-man) ;; ("C-c i" . consult-info) ([remap Info-search] . consult-info) ;; C-x bindings in `ctl-x-map' ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command ("C-x b" . consult-buffer) ;; orig. switch-to-buffer ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame ("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer ;; Custom M-# bindings for fast register access ;; ("M-#" . consult-register-load) ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated) ;; ("C-M-#" . consult-register) ;; Other custom bindings ("M-y" . consult-yank-pop) ;; orig. yank-pop ;; M-g bindings in `goto-map' ;; ("M-g e" . consult-compile-error) ;; ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck ("M-g g" . consult-goto-line) ;; orig. goto-line ("M-g M-g" . consult-goto-line) ;; orig. goto-line ;; ("M-g o" . consult-outline) ;; Alternative: consult-org-heading ;; ("M-g m" . consult-mark) ;; ("M-g k" . consult-global-mark) ("M-i" . consult-imenu) ("M-I" . consult-imenu-multi) ;; M-s bindings in `search-map' ;; ("M-s d" . consult-find) ;; Alternative: consult-fd ;; ("M-s c" . consult-locate) ;; ("M-s g" . consult-grep) ;; ("M-s G" . consult-git-grep) ;; ("M-s r" . consult-ripgrep) ("M-s l" . consult-line) ;; ("M-s L" . consult-line-multi) ;; ("M-s k" . consult-keep-lines) ;; ("M-s u" . consult-focus-lines) ;; Isearch integration ("M-s e" . consult-isearch-history) :map isearch-mode-map ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string ("M-s l" . consult-line) ;; needed by consult-line to detect isearch ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch ;; Minibuffer history :map minibuffer-local-map ("M-s" . consult-history) ;; orig. next-matching-history-element ("M-r" . consult-history) ;; orig. previous-matching-history-element ) ;; Enable automatic preview at point in the *Completions* buffer. This is ;; relevant when you use the default completion UI. :hook (completion-list-mode . consult-preview-at-point-mode) ;; The :init configuration is always executed (Not lazy) :init ;; Optionally configure the register formatting. This improves the register ;; preview for `consult-register', `consult-register-load', ;; `consult-register-store' and the Emacs built-ins. ;; (setq register-preview-delay 0.5 ;; register-preview-function #'consult-register-format) ;; Optionally tweak the register preview window. ;; This adds thin lines, sorting and hides the mode line of the window. ;; (advice-add #'register-preview :override #'consult-register-window) ;; Use Consult to select xref locations with preview (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) ;; Configure other variables and modes in the :config section, ;; after lazily loading the package. ;; :config ;; Optionally configure preview. The default value ;; is 'any, such that any key triggers the preview. ;; (setq consult-preview-key 'any) ;; (setq consult-preview-key "M-.") ;; (setq consult-preview-key '("S-" "S-")) ;; For some commands and buffer sources it is useful to configure the ;; :preview-key on a per-command basis using the `consult-customize' macro. ;; (consult-customize ;; consult-theme :preview-key '(:debounce 0.2 any) ;; consult-ripgrep consult-git-grep consult-grep ;; consult-bookmark consult-recent-file consult-xref ;; consult--source-bookmark consult--source-file-register ;; consult--source-recent-file consult--source-project-recent-file ;; :preview-key "M-." ;; :preview-key '(:debounce 0.4 any)) ;; Optionally configure the narrowing key. ;; Both < and C-+ work reasonably well. ;; (setq consult-narrow-key "<") ;; "C-+" ;; Optionally make narrowing help available in the minibuffer. ;; You may want to use `embark-prefix-help-command' or which-key instead. ;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help) ) #+END_SRC ** Corfu #+BEGIN_SRC emacs-lisp (use-package corfu :ensure t ;; Optional customizations ;; :custom ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' ;; (corfu-auto t) ;; Enable auto completion ;; (corfu-separator ?\s) ;; Orderless field separator ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match ;; (corfu-preview-current nil) ;; Disable current candidate preview ;; (corfu-preselect 'prompt) ;; Preselect the prompt ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches ;; (corfu-scroll-margin 5) ;; Use scroll margin ;; Enable Corfu only for certain modes. See also `global-corfu-modes'. ;; :hook ((prog-mode . corfu-mode) ;; (shell-mode . corfu-mode) ;; (eshell-mode . corfu-mode)) ;; Recommended: Enable Corfu globally. This is recommended since Dabbrev can ;; be used globally (M-/). See also the customization variable ;; `global-corfu-modes' to exclude certain modes. :init (global-corfu-mode)) #+end_src ** Orderless #+begin_src emacs-lisp (use-package orderless :ensure t :demand t :custom (completion-styles '(orderless basic)) ;; (gnus-completion-styles '(orderless substring basic)) ;; (completion-category-overrides '((file (styles basic partial-completion)))) ;; Below not necessary if using vertico ;; (completion-category-overrides '( ;; (command (styles orderless basic partial-completion)) ;; (file (styles orderless basic partial-completion)) ;;;; (buffer (styles orderless basic)) ;; (variable (styles orderless basic)) ;; (symbol (styles orderless basic)) ;; (consult-location (styles orderless)) ;; (consult-multi (styles orderless)) ;; ) ;; ) ) #+end_src ** Marginalia #+begin_src emacs-lisp ;; Enable rich annotations using the Marginalia package (use-package marginalia :ensure t ;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding ;; available in the *Completions* buffer, add it to the ;; `completion-list-mode-map'. :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle)) ;; The :init section is always executed. :init ;; Marginalia must be activated in the :init section of use-package such that ;; the mode gets enabled right away. Note that this forces loading the ;; package. (marginalia-mode)) #+end_src * General config ** Delete trailing whitespaces #+BEGIN_SRC emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) #+END_SRC ** Save history and recent files #+begin_src emacs-lisp ;; The built-in `savehist-mode' saves minibuffer histories. Vertico ;; can then use that information to put recently selected options at ;; the top. ;; ;; Further reading: https://protesilaos.com/emacs/dotemacs#h:25765797-27a5-431e-8aa4-cc890a6a913a (savehist-mode 1) ;; The built-in `recentf-mode' keeps track of recently visited files. ;; You can then access those through the `consult-buffer' interface or ;; with `recentf-open'/`recentf-open-files'. ;; ;; I do not use this facility, because the files I care about are ;; either in projects or are bookmarked. (recentf-mode 1) #+end_src ** Backups #+BEGIN_SRC emacs-lisp (defvar myrmi-backup-directory (concat user-emacs-directory "backups")) (if (not (file-exists-p myrmi-backup-directory)) (make-directory myrmi-backup-directory t) ) (setq backup-directory-alist `(("." . ,myrmi-backup-directory))) (setq make-backup-files t backup-by-copying t version-control t delete-old-versions t delete-by-moving-to-trash t kept-old-versions 6 kept-new-versions 9 auto-save-default t auto-save-timeout 20 auto-save-interval 200 ) #+END_SRC ** Yes-or-no Because I'm lazy, important yes-or-no questions can be answered with y-or-n: #+begin_src emacs-lisp (defalias 'yes-or-no-p 'y-or-n-p) #+end_src ** Switch windows #+begin_src emacs-lisp (global-set-key (kbd "M-o") 'other-window) #+end_src ** Maximize at startup More info : https://www.emacswiki.org/emacs/FullScreen #+begin_src emacs-lisp (push '(fullscreen . maximized) default-frame-alist) #+end_src ** ibuffer Use list-buffers bigger brother. #+begin_src emacs-lisp (global-set-key [remap list-buffers] 'ibuffer) #+end_src ** Mark #+begin_src emacs-lisp (global-set-key (kbd "M-SPC") 'mark-word) #+end_src ** Isearch Display number of matches: #+begin_src emacs-lisp (setq-default isearch-lazy-count t) #+end_src Reference that might be interesting for later: https://endlessparentheses.com/leave-the-cursor-at-start-of-match-after-isearch.html ** Sudo file #+begin_src emacs-lisp (defun sudo () "Use TRAMP to `sudo' the current buffer." (interactive) (when buffer-file-name (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name) ) ) ) #+end_src ** Abbrev #+begin_src emacs-lisp (global-set-key [remap dabbrev-expand] 'hippie-expand) #+end_src ** Zap #+begin_src emacs-lisp (global-set-key (kbd "M-S-z") 'zap-up-to-char) #+end_src ** Spell checking Look into customizing the 'ispell' group. #+begin_src emacs-lisp (add-hook 'prog-mode-hook 'flyspell-prog-mode) #+end_src * Dired #+begin_src emacs-lisp (require 'dired-x) #+end_src * Whole-line-or-region Source: https://github.com/purcell/whole-line-or-region Operate on the current line if no region is active. #+begin_src emacs-lisp (use-package whole-line-or-region :ensure t :config (whole-line-or-region-global-mode 1) ) #+end_src * Terminal ** Toggle between char- and line-mode Courtesy goes to https://joelmccracken.github.io/entries/switching-between-term-mode-and-line-mode-in-emacs-term/ #+BEGIN_SRC emacs-lisp (require 'term) (defun jnm/term-toggle-mode () "Toggles term between line mode and char mode" (interactive) (if (term-in-line-mode) (term-char-mode) (term-line-mode))) (define-key term-mode-map (kbd "C-c C-j") 'jnm/term-toggle-mode) (define-key term-mode-map (kbd "C-c C-k") 'jnm/term-toggle-mode) (define-key term-raw-map (kbd "C-c C-j") 'jnm/term-toggle-mode) (define-key term-raw-map (kbd "C-c C-k") 'jnm/term-toggle-mode) #+END_SRC For the keybindings, we have to defien them in both raw and line mode. From the help page of term mode: If you define custom keybindings, make sure to assign them to the correct keymap (or to both): use ‘term-raw-map’ in raw mode and ‘term-mode-map’ in line mode. * Theme #+BEGIN_SRC emacs-lisp (use-package monokai-theme :ensure t :init (load-theme 'monokai t) ) #+END_SRC * Dashboard #+begin_src emacs-lisp (use-package dashboard :ensure t :config (add-hook 'elpaca-after-init-hook #'dashboard-insert-startupify-lists) (add-hook 'elpaca-after-init-hook #'dashboard-initialize) (dashboard-setup-startup-hook)) #+end_src * Hydra Install and wait for hydra to be available since we are using it in this init.el : #+begin_src emacs-lisp (use-package hydra :ensure (:wait t) ) #+end_src ** Text zoom #+begin_src emacs-lisp (defhydra hydra-zoom (global-map "") "zoom" ("g" text-scale-increase "in") ("l" text-scale-decrease "out") ) #+end_src * Programming ** Electric pair #+BEGIN_SRC emacs-lisp (add-hook 'prog-mode-hook 'electric-pair-mode) #+END_SRC ** Eglot #+BEGIN_SRC emacs-lisp (use-package eglot :ensure t :defer t ;; This doesn't work for some reason, workaround below ;;:hook (prog-mode . eglot-ensure) ;; :config ;; (add-hook 'prog-mode-hook 'eglot-ensure) ) #+END_SRC Workaround to enable eglot in all programming modes: #+BEGIN_SRC emacs-lisp (add-hook 'prog-mode-hook 'eglot-ensure) #+END_SRC ** Yasnippet #+BEGIN_SRC emacs-lisp (use-package yasnippet :ensure t :config (yas-reload-all) (add-hook 'prog-mode-hook 'yas-minor-mode) ) #+END_SRC ** Magit *** Transient Magit depends on this and it seems it's not installed as a dependency, so install it explicitly. #+BEGIN_SRC emacs-lisp (use-package transient :ensure t ) #+END_SRC *** Core #+BEGIN_SRC emacs-lisp (use-package magit :ensure t ) #+END_SRC ** Smartparens Smart minor-mode to deal with pairs. https://github.com/Fuco1/smartparens #+BEGIN_SRC emacs-lisp (use-package smartparens :ensure t :bind ("C-M-w" . sp-copy-sexp) :hook (prog-mode text-mode markdown-mode) :config (require 'smartparens-config) ) #+END_SRC * Multiple cursors #+BEGIN_SRC emacs-lisp (use-package multiple-cursors :ensure t :bind ("C-x r a" . mc/edit-beginnings-of-lines) ("C-x r e" . mc/edit-ends-of-lines) ("C->" . mc/mark-next-like-this) ("C-<" . mc/mark-previous-like-this) ("C-c C->" . mc/mark-all-like-this) ) #+END_SRC * Comment-dwim-2 Replacement for built-in =comment-dwim=, more comment features. https://github.com/remyferre/comment-dwim-2 #+BEGIN_SRC emacs-lisp (use-package comment-dwim-2 :ensure t :config (global-set-key (kbd "M-;") 'comment-dwim-2) ) #+END_SRC * Projectile #+BEGIN_SRC emacs-lisp (use-package projectile :ensure t :config (setq projectile-indexing-method 'hybrid) (setq projectile-enable-caching t) (define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map) (projectile-mode +1) (require 'project) (add-hook 'project-find-functions #'project-projectile) ) #+END_SRC * Elisp ** Add demos to describe-function #+BEGIN_SRC emacs-lisp (use-package elisp-demos :ensure t :config (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1) ) #+END_SRC * Custom ** Save symbol at point #+BEGIN_SRC emacs-lisp (defun myrmi/save-symbol-at-point () "Make symbol at point the latest kill in the kill ring." (interactive) (let ((symbol (thing-at-point 'symbol))) (when symbol (kill-new symbol)))) (global-set-key (kbd "C-M-w") 'myrmi/save-symbol-at-point) #+END_SRC ** Ceedling #+BEGIN_SRC emacs-lisp (defvar ceedling-project-file-name "project.yml") (defvar ceedling-cmd "ceedling") (defvar ceedling-project-root ".") (defun myrmi/run-ceedling-tests (&optional file-name) (interactive) (let* ( (file-path (or file-name buffer-file-name)) (root-path (or (locate-dominating-file file-path ceedling-project-file-name) ceedling-project-root)) ) (compile (concat "cd " root-path " && " ceedling-cmd) ) ) ) #+END_SRC ** Set path to shell path #+BEGIN_SRC emacs-lisp (defun set-exec-path-from-shell-PATH () (let ((path-from-shell (replace-regexp-in-string "[[:space:]\n]*$" "" (shell-command-to-string "$SHELL -l -c 'echo $PATH'")))) (setenv "PATH" path-from-shell) (setq exec-path (split-string path-from-shell path-separator)))) (set-exec-path-from-shell-PATH) #+END_SRC ** Reload dir-locals.el #+BEGIN_SRC emacs-lisp (defun myrmi/reload-dir-locals-for-current-buffer () "Reload dir locals for the current buffer" (interactive) (let ((enable-local-variables :all)) (hack-dir-local-variables-non-file-buffer))) (defun myrmi/reload-dir-locals-for-all-buffers-in-this-directory () "For every buffer with the same `default-directory` as the current buffer, reload dir-locals." (interactive) (let ((dir default-directory)) (dolist (buffer (buffer-list)) (with-current-buffer buffer (when (equal default-directory dir) (myrmi/reload-dir-locals-for-current-buffer)))))) #+END_SRC ** Visit/reload config These snippets assume my-config-file variable is set outside this configuration. This should normally be done by the init.el to load this configuration. #+BEGIN_SRC emacs-lisp (defun myrmi/visit-config () "Reloads ~/.emacs.d/config.org at runtime" (interactive) (find-file my-config-file)) (defun myrmi/reload-config () "Reloads ~/.emacs.d/config.org at runtime" (interactive) (org-babel-load-file my-config-file)) #+END_SRC