From a0a5b2e05d3beae7ed3d5ed14b6b65bbc811c7a9 Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Fri, 29 Sep 2023 22:30:58 +0100 Subject: (Emacs)~config->app,core The two largest sections of my config are separated into their own files now. Does increase init time, but I just can't handle how big this thing is. It'll be a bit nicer to look at and manage with separate files. --- Emacs/.config/emacs/core.org | 738 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 738 insertions(+) create mode 100644 Emacs/.config/emacs/core.org (limited to 'Emacs/.config/emacs/core.org') diff --git a/Emacs/.config/emacs/core.org b/Emacs/.config/emacs/core.org new file mode 100644 index 0000000..687b226 --- /dev/null +++ b/Emacs/.config/emacs/core.org @@ -0,0 +1,738 @@ +#+title: Core packages +#+author: Aryadev Chavali +#+description: The core components of my configuration +#+date: 2023-09-29 +#+property: header-args:emacs-lisp :tangle core.el :comments link :results none +#+options: toc:nil +#+startup: noindent + +Packages that are absolutely necessary for the rest of the +configuration. These yield core functionality such as keybinding, +modal editing, completion, auto typing to name a few. +* General +General provides a great solution for binding keys. It has evil and +use-package support so it fits nicely into configuration. In this +case, I define a "definer" for the "LEADER" keys. Leader is bound to +~SPC~ and it's functionally equivalent to the doom/spacemacs leader. +Local leader is bound to ~SPC ,~ and it's similar to doom/spacemacs +leader but doesn't try to fully assimilate the local-leader map, +instead just picking stuff I think is useful. This forces me to learn +only as many bindings as I find necessary; no more, no less. + +I also define prefix leaders for differing applications. These are +quite self explanatory by their name and provide a nice way to +visualise all bindings under a specific heading just by searching the +code. +#+begin_src emacs-lisp +(use-package general + :straight t + :demand t + :config + ;; General which key definitions for leaders + (general-def + :states '(normal motion) + "SPC" 'nil + "\\" '(nil :which-key "Local leader") + "SPC c" '(nil :which-key "Code") + "SPC f" '(nil :which-key "File") + "SPC t" '(nil :which-key "Shell") + "SPC m" '(nil :which-key "Toggle modes") + "SPC a" '(nil :which-key "Applications") + "SPC s" '(nil :which-key "Search") + "SPC b" '(nil :which-key "Buffers") + "SPC q" '(nil :which-key "Quit/Literate") + "SPC i" '(nil :which-key "Insert") + "SPC d" '(nil :which-key "Directories")) + + (general-create-definer leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC") + + (general-create-definer local-leader + :states '(normal motion) + :prefix "\\") + + (general-create-definer code-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC c") + + (general-create-definer file-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC f") + + (general-create-definer shell-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC t") + + (general-create-definer mode-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC m") + + (general-create-definer app-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC a") + + (general-create-definer search-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC s") + + (general-create-definer buffer-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC b") + + (general-create-definer quit-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC q") + + (general-create-definer insert-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC i") + + (general-create-definer dir-leader + :states '(normal motion) + :keymaps 'override + :prefix "SPC d") + + (general-create-definer general-nmmap + :states '(normal motion)) + + (defalias 'nmmap #'general-nmmap) + + (general-evil-setup t)) +#+end_src +** Some binds in Emacs +Some bindings that I couldn't fit elsewhere easily. +#+begin_src emacs-lisp +(use-package emacs + :straight nil + :general + (general-def + "C-x d" #'delete-frame) + + (nmmap + "C--" #'text-scale-decrease + "C-=" #'text-scale-increase) + + (leader + "SPC" '(execute-extended-command :which-key "M-x") + "'" '(browse-url-emacs :which-key "Open url in Emacs") + ";" 'eval-expression + ":" `(,(proc (interactive) (switch-to-buffer "*scratch*")) + :which-key "Switch to *scratch*") + "!" '(async-shell-command :which-key "Async shell command") + "h" '(help-command :which-key "Help")) + + (mode-leader + "t" #'+oreo/switch-theme) + + (code-leader + "F" `(,(proc (interactive) (find-file "~/Code/")) + :which-key "Open ~/Code/")) + + (file-leader + "f" #'find-file + "F" #'find-file-other-frame + "s" #'save-buffer + "p" `(,(proc (interactive) + (find-file (concat user-emacs-directory "config.org"))) + :which-key "Open config.org")) + + (quit-leader + "q" #'save-buffers-kill-terminal + "c" #'+literate/compile-config + "l" #'+literate/load-config + "d" #'delete-frame) + + (search-leader "i" #'imenu)) +#+end_src +* Evil +My editor journey started off with Vim rather than Emacs, so my brain +has imprinted on its style. Thankfully Emacs is super extensible so +there exists a package (more of a supreme system) for porting Vim's +modal editing style to Emacs, called Evil (Emacs Vi Layer). + +However there are a lot of packages in Vim that provide greater +functionality, for example 'vim-surround'. Emacs, by default, has +these capabilities but there are further packages which integrate them +into Evil. +** Evil core +Setup the evil package, with some opinionated keybindings: +- Switch ~evil-upcase~ and ~evil-downcase~ because I use ~evil-upcase~ + more +- Switch ~evil-goto-mark~ and ~evil-goto-mark-line~ as I'd rather have + the global one closer to the home row +- Use 'T' character as an action for transposing objects +#+begin_src emacs-lisp +(use-package evil + :demand t + :hook (after-init-hook . evil-mode) + :general + (leader + "w" '(evil-window-map :which-key "Window") + "wd" #'delete-frame) + + (nmmap + "TAB" #'evil-jump-item + "r" #'evil-replace-state + "zC" #'hs-hide-level + "zO" #'hs-show-all + "'" #'evil-goto-mark + "`" #'evil-goto-mark-line + "C-w" #'evil-window-map + "gu" #'evil-upcase + "gU" #'evil-downcase + "T" nil) + + (nmmap + :infix "T" + "w" #'transpose-words + "c" #'transpose-chars + "s" #'transpose-sentences + "p" #'transpose-paragraphs + "e" #'transpose-sexps + "l" #'transpose-lines) + :init + (setq evil-want-keybinding nil + evil-split-window-below t + evil-vsplit-window-right t + evil-want-abbrev-expand-on-insert-exit t + evil-undo-system #'undo-tree) + :config + (fset #'evil-window-vsplit #'make-frame)) +#+end_src +** Evil surround +Evil surround is a port for vim-surround. +#+begin_src emacs-lisp +(use-package evil-surround + :after evil + :config + (global-evil-surround-mode)) +#+end_src +** Evil commentary +Allows generalised commenting of objects easily. +#+begin_src emacs-lisp +(use-package evil-commentary + :after evil + :config + (evil-commentary-mode)) +#+end_src +** Evil multi cursor +Setup for multi cursors in Evil mode. Don't let evil-mc setup it's own +keymap because it uses 'gr' as its prefix, which I don't like. +#+begin_src emacs-lisp +(use-package evil-mc + :after evil + :init + (defvar evil-mc-key-map (make-sparse-keymap)) + :general + (nmap + :infix "gz" + "q" #'evil-mc-undo-all-cursors + "d" #'evil-mc-make-and-goto-next-match + "j" #'evil-mc-make-cursor-move-next-line + "k" #'evil-mc-make-cursor-move-prev-line + "j" #'evil-mc-make-cursor-move-next-line + "m" #'evil-mc-make-all-cursors + "z" #'evil-mc-make-cursor-here + "r" #'evil-mc-resume-cursors + "s" #'evil-mc-pause-cursors + "u" #'evil-mc-undo-last-added-cursor) + :config + ;; (evil-mc-define-vars) + ;; (evil-mc-initialize-vars) + ;; (add-hook 'evil-mc-before-cursors-created #'evil-mc-pause-incompatible-modes) + ;; (add-hook 'evil-mc-before-cursors-created #'evil-mc-initialize-active-state) + ;; (add-hook 'evil-mc-after-cursors-deleted #'evil-mc-teardown-active-state) + ;; (add-hook 'evil-mc-after-cursors-deleted #'evil-mc-resume-incompatible-modes) + ;; (advice-add #'evil-mc-initialize-hooks :override #'ignore) + ;; (advice-add #'evil-mc-teardown-hooks :override #'evil-mc-initialize-vars) + ;; (advice-add #'evil-mc-initialize-active-state :before #'turn-on-evil-mc-mode) + ;; (advice-add #'evil-mc-teardown-active-state :after #'turn-off-evil-mc-mode) + ;; (add-hook 'evil-insert-state-entry-hook #'evil-mc-resume-cursors) + (global-evil-mc-mode)) +#+end_src + +** Evil collection +Provides a community based set of keybindings for most modes in +Emacs. I don't necessarily like all my modes having these bindings +though, as I may disagree with some. So I use it in a mode to mode basis. +#+begin_src emacs-lisp +(use-package evil-collection + :after evil) +#+end_src +* Completion +Emacs is a text based interface. Completion is its bread and butter +in providing good user experience. By default Emacs provides +'completions-list' which produces a buffer of options which can be +searched and selected. We can take this further though! + +Ido and Icomplete are packages distributed with Emacs to provide +greater completion interfaces. They utilise the minibuffer to create +a more interactive experience, allowing incremental searches and +option selection. + +Ivy and Helm provide more modern interfaces, though Helm is quite +heavy. Ivy, on the other hand, provides an interface similar to Ido +with less clutter and better customisation options. +** Ivy +Ivy is a completion framework for Emacs, and my preferred one. It has +a great set of features with little to no pain with setting up. +*** Ivy Core +Setup for ivy, in preparation for counsel. Turn on ivy-mode just +after init. + +Setup vim-like bindings for the minibuffer ("M-(j|k)" for down|up the +selection list). +#+begin_src emacs-lisp +(use-package ivy + :defer t + :hook (after-init-hook . ivy-mode) + :general + (general-def + :keymaps 'ivy-minibuffer-map + "C-j" #'ivy-yank-symbol + "M-j" #'ivy-next-line-or-history + "M-k" #'ivy-previous-line-or-history + "C-SPC" #'ivy-occur) + (general-def + :keymaps 'ivy-switch-buffer-map + "M-j" #'ivy-next-line-or-history + "M-k" #'ivy-previous-line-or-history) + (nmap + :keymaps '(ivy-occur-mode-map ivy-occur-grep-mode-map) + "RET" #'ivy-occur-press-and-switch + "J" #'ivy-occur-press + "gr" #'ivy-occur-revert-buffer + "q" #'quit-window + "D" #'ivy-occur-delete-candidate + "W" #'ivy-wgrep-change-to-wgrep-mode + "{" #'compilation-previous-file + "}" #'compilation-next-file) + :init + (with-eval-after-load "evil" + (evil-set-initial-state 'ivy-occur-mode 'normal) + (evil-set-initial-state 'ivy-occur-grep-mode 'normal)) + (with-eval-after-load "amx" + (setq amx-backend 'ivy)) + + (setq ivy-height 10 + ivy-wrap t + ivy-fixed-height-minibuffer t + ivy-use-virtual-buffers nil + ivy-virtual-abbreviate 'full + ivy-on-del-error-function #'ignore + ivy-use-selectable-prompt t) + :config + (require 'counsel nil t)) +#+end_src +*** Counsel +Setup for counsel. Load after ivy and helpful. +#+begin_src emacs-lisp +(use-package counsel + :defer t + :general + (search-leader + "s" #'counsel-grep-or-swiper + "r" #'counsel-rg) + (file-leader + "r" #'counsel-recentf) + (insert-leader + "c" #'counsel-unicode-char) + (general-def + [remap describe-bindings] #'counsel-descbinds + [remap load-theme] #'counsel-load-theme) + :config + (setq ivy-initial-inputs-alist '((org-insert-link . "^")) + counsel-describe-function-function #'helpful-callable + counsel-describe-variable-function #'helpful-variable + counsel-grep-swiper-limit 1500000 + ivy-re-builders-alist '((swiper . ivy--regex-plus) + (counsel-grep-or-swiper . ivy--regex-plus) + (counsel-rg . ivy--regex-plus) + (t . ivy--regex-ignore-order))) + (counsel-mode)) +#+end_src +*** WIP Ivy posframe +:PROPERTIES: +:header-args:emacs-lisp: :tangle no +:END: +This makes ivy minibuffer windows use child frames. +Very nice eyecandy, but can get kinda annoying. +#+begin_src emacs-lisp +(use-package ivy-posframe + :hook (ivy-mode-hook . ivy-posframe-mode) + :straight t + :init + (setq ivy-posframe-parameters + '((left-fringe . 0) + (right-fringe . 0) + (background-color . "grey7"))) + + (setq ivy-posframe-display-functions-alist + '((t . ivy-posframe-display-at-window-center)))) +#+end_src +*** WIP Counsel etags +:PROPERTIES: +:header-args:emacs-lisp: :tangle no +:END: +Counsel etags allows me to search generated tag files for tags. I +already have a function defined to generate the tags, so it's just +searching them which I find to be a bit of a hassle, and where this +package comes in. + +This has been replaced by [[*xref][xref]] which is inbuilt. +#+begin_src emacs-lisp +(use-package counsel-etags + :after counsel + :general + (search-leader + "t" #'counsel-etags-find-tag)) +#+end_src +** WIP Ido +:PROPERTIES: +:header-args:emacs-lisp: :tangle no +:END: +Ido is a very old completion package that still works great to this +day. Though it is limited in its scope (and may thus be called a +completion add-on rather than a full on framework), it is still a very +powerful package. With the use of ido-completing-read+, it may be used +similarly to a fully fledged completion framework. + +#+begin_src emacs-lisp +(use-package ido + :demand t + :general + (general-def + :keymaps '(ido-buffer-completion-map + ido-file-completion-map + ido-file-dir-completion-map + ido-common-completion-map) + (kbd "M-j") #'ido-next-match + (kbd "M-k") #'ido-prev-match + (kbd "C-x o") #'evil-window-up) + :init + (setq ido-decorations + (list "{" "}" " \n" " ..." "[" "]" " [No match]" " [Matched]" + " [Not readable]" " [Too big]" " [Confirm]") + completion-styles '(flex partial-completion intials emacs22)) + (setq-default ido-enable-flex-matching t + ido-enable-dot-prefix t + ido-enable-regexp nil) + (with-eval-after-load "magit" + (setq magit-completing-read-function 'magit-ido-completing-read)) + :config + (ido-mode) + (ido-everywhere)) +#+end_src +*** Ido ubiquitous +Ido completing-read+ is a package that extends the ido package to work +with more text based functions. +#+begin_src emacs-lisp +(use-package ido-completing-read+ + :after ido + :config + (ido-ubiquitous-mode +1)) +#+end_src +** Amx +Amx is a fork of Smex that works to enhance the +execute-extended-command interface. It also provides support for ido +or ivy (though I'm likely to use ido here) and allows you to switch +between them. + +It provides a lot of niceties such as presenting the key bind when +looking for a command. + +#+begin_src emacs-lisp +(use-package amx + :config + (amx-mode)) +#+end_src +** Orderless +Orderless sorting method for completion, probably one of the best +things ever. +#+begin_src emacs-lisp +(use-package orderless + :after (ivy ido) + :config + (setf (alist-get t ivy-re-builders-alist) 'orderless-ivy-re-builder)) +#+end_src +** Completions-list +In case I ever use the completions list, some basic commands to look +around. +#+begin_src emacs-lisp +(use-package simple + :straight nil + :general + (nmmap + :keymaps 'completion-list-mode-map + "l" #'next-completion + "h" #'previous-completion + "ESC" #'delete-completion-window + "q" #'quit-window + "RET" #'choose-completion) + :config + (with-eval-after-load "evil" + (setq evil-emacs-state-modes (cl-remove-if + #'(lambda (x) (eq 'completions-list-mode x)) + evil-emacs-state-modes)) + (add-to-list 'evil-normal-state-modes 'completions-list-mode))) +#+end_src +** Company +Company is the auto complete system I use. I don't like having heavy +setups for company as it only makes it slower to use. In this case, +just setup some evil binds for company. +#+begin_src emacs-lisp +(use-package company + :straight t + :hook + (prog-mode-hook . company-mode) + (eshell-mode-hook . company-mode) + :general + (imap + "C-SPC" #'company-complete) + (general-def + :states '(normal insert) + "M-j" #'company-select-next + "M-k" #'company-select-previous)) +#+end_src +* Pretty symbols +Prettify symbols mode allows for users to declare 'symbols' that +replace text within certain modes. Though this may seem like useless +eye candy, it has aided my comprehension and speed of recognition +(recognising symbols is easier than words). + +Essentially a use-package keyword which makes declaring pretty symbols +for language modes incredibly easy. Checkout my [[C/C++][C/C++]] configuration +for an example. +#+begin_src emacs-lisp +(use-package prog-mode + :straight nil + :init + (setq prettify-symbols-unprettify-at-point t) + :config + (with-eval-after-load "use-package-core" + (add-to-list 'use-package-keywords ':pretty) + (defun use-package-normalize/:pretty (_name-symbol _keyword args) + args) + + (defun use-package-handler/:pretty (name _keyword args rest state) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (arg) + (let ((mode (car arg)) + (rest (cdr arg))) + `(add-hook + ',mode + #'(lambda nil + (setq prettify-symbols-alist ',rest) + (prettify-symbols-mode))))) + args))))) +#+end_src + +Here's a collection of keywords and possible associated symbols for +any prog language of choice. Mostly for reference and copying. +#+begin_example +("null" . "Ø") +("list" . "ℓ") +("string" . "𝕊") +("true" . "⊤") +("false" . "⊥") +("char" . "ℂ") +("int" . "ℤ") +("float" . "ℝ") +("!" . "¬") +("&&" . "∧") +("||" . "∨") +("for" . "∀") +("return" . "⟼") +("print" . "ℙ") +("lambda" . "λ") +#+end_example +* Window management +Emacs' default window management is quite bad, eating other windows on +a whim and not particularly caring for the current window setup. +Thankfully you can change this via the ~display-buffer-alist~ which +matches buffer names with how the window for the buffer should be +displayed. I add a use-package keyword to make ~display-buffer-alist~ +records within use-package. + +I have no idea whether it's optimal AT ALL, but it works for me. +#+begin_src emacs-lisp +(use-package window + :straight nil + :general + (buffer-leader + "b" #'switch-to-buffer + "d" #'kill-current-buffer + "K" #'kill-buffer + "j" #'next-buffer + "k" #'previous-buffer + "D" '(+oreo/clean-buffer-list :which-key "Kill most buffers")) + :init + (with-eval-after-load "use-package-core" + (add-to-list 'use-package-keywords ':display) + (defun use-package-normalize/:display (_name-symbol _keyword args) + args) + + (defun use-package-handler/:display (name _keyword args rest state) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (arg) + `(add-to-list 'display-buffer-alist + ',arg)) + args))))) +#+end_src +** Some display records +Using the ~:display~ keyword, setup up some ~display-buffer-alist~ +records. This is mostly for packages that aren't really configured +(like [[info:woman][woman]]) or packages that were configured before +(like [[Ivy][Ivy]]). +#+begin_src emacs-lisp +(use-package window + :straight nil + :defer t + :display + ("\\*Process List\\*" + (display-buffer-at-bottom) + (window-height . 0.25)) + + ("\\*\\(Ido \\)?Completions\\*" + (display-buffer-in-side-window) + (window-height . 0.25) + (side . bottom)) + + ("\\*ivy-occur.*" + (display-buffer-at-bottom) + (window-height . 0.25)) + + ("\\*Async Shell Command\\*" + (display-buffer-at-bottom) + (window-height . 0.25))) +#+end_src +* Auto typing +Snippets are a pretty nice way of automatically inserting code. Emacs +provides a ton of packages by default to do this, but there are great +packages to install as well. + +Abbrevs and skeletons make up a popular solution within Emacs default. +Abbrevs are for simple expressions wherein the only input is the key, +and the output is some Elisp function. They provide a lot of inbuilt +functionality and are quite useful. Skeletons, on the other hand, are +for higher level insertions + +The popular external solution is Yasnippet. Yasnippet is a great +package for snippets, which I use heavily in programming and org-mode. +I setup here the global mode for yasnippet and a collection of +snippets for ease of use. +** Abbrevs +Just define a few abbrevs for various date-time operations. Also +define a macro that will assume a function for the expansion, helping +with abstracting a few things away. +#+begin_src emacs-lisp +(use-package abbrev + :straight nil + :hook + (prog-mode-hook . abbrev-mode) + (text-mode-hook . abbrev-mode) + :init + (defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs) + `(progn + ,@(mapcar #'(lambda (abbrev) + `(define-abbrev + ,abbrev-table + ,(car abbrev) + "" + (proc (insert ,(cadr abbrev))))) + abbrevs))) + (setq save-abbrevs nil) + :config + (+abbrev/define-abbrevs + global-abbrev-table + ("sdate" + (format-time-string "%Y-%m-%d" (current-time))) + ("stime" + (format-time-string "%H:%M:%S" (current-time))) + ("sday" + (format-time-string "%A" (current-time))) + ("smon" + (format-time-string "%B" (current-time))))) +#+end_src +** WIP Skeletons +:PROPERTIES: +:header-args:emacs-lisp: :tangle no +:END: +Defines a macro for generating a skeleton + abbrev for a given mode. +Doesn't sanitise inputs because I assume callers are /rational/ actors +who would *only* use this for their top level Emacs config. + +Honestly didn't find much use for this currently, so disabled. +#+begin_src emacs-lisp +(use-package skeleton + :straight nil + :after abbrev + :config + (defmacro +autotyping/gen-skeleton-abbrev (mode abbrev &rest skeleton) + (let* ((table (intern (concat (symbol-name mode) "-abbrev-table"))) + (skeleton-name (intern (concat "+skeleton/" (symbol-name mode) "/" abbrev)))) + `(progn + (define-skeleton + ,skeleton-name + "" + ,@skeleton) + (define-abbrev ,table + ,abbrev + "" + ',skeleton-name))))) +#+end_src +** Auto insert +Allows inserting text immediately upon creating a new buffer with a +given name. Supports skeletons for inserting text. To make it easier +for later systems to define their own auto inserts, I define a +~use-package~ keyword ~auto-insert~ which allows one to define an +entry for ~auto-insert-alist~. +#+begin_src emacs-lisp +(use-package autoinsert + :straight nil + :demand t + :hook (after-init-hook . auto-insert-mode) + :config + (with-eval-after-load "use-package-core" + (add-to-list 'use-package-keywords ':auto-insert) + (defun use-package-normalize/:auto-insert (_name-symbol _keyword args) + args) + (defun use-package-handler/:auto-insert (name _keyword args rest state) + (use-package-concat + (use-package-process-keywords name rest state) + (mapcar + #'(lambda (arg) + `(add-to-list + 'auto-insert-alist + ',arg)) + args))))) +#+end_src +** Yasnippet default +Look at the snippets [[file:../.config/yasnippet/snippets/][folder]] +for all snippets I've got. +#+begin_src emacs-lisp +(use-package yasnippet + :defer t + :hook + (prog-mode-hook . yas-minor-mode) + :general + (insert-leader + "i" #'yas-insert-snippet) + :config + (yas-load-directory (no-littering-expand-etc-file-name "yasnippet/snippets"))) +#+end_src -- cgit v1.2.3-13-gbd6f