diff options
Diffstat (limited to 'Emacs/.config/emacs/config.org')
-rw-r--r-- | Emacs/.config/emacs/config.org | 2143 |
1 files changed, 1105 insertions, 1038 deletions
diff --git a/Emacs/.config/emacs/config.org b/Emacs/.config/emacs/config.org index 34b593b..b1c2763 100644 --- a/Emacs/.config/emacs/config.org +++ b/Emacs/.config/emacs/config.org @@ -82,27 +82,29 @@ Let's setup a few absolute essentials: (use-package emacs :demand t :init - (setq user-full-name "Aryadev Chavali" - user-mail-address "aryadev@aryadevchavali.com" - buffer-file-coding-system 'utf-8-unix - save-buffer-coding-system 'utf-8-unix - backup-directory-alist `(("." . ,(no-littering-expand-var-file-name "saves/"))) - global-auto-revert-non-file-buffers t - auto-revert-verbose nil + (setq auth-sources '("~/.authinfo.gpg") auto-revert-use-notify nil - select-enable-clipboard t + auto-revert-verbose nil + backup-directory-alist `(("." . ,(no-littering-expand-var-file-name "saves/"))) + buffer-file-coding-system 'utf-8-unix delete-by-moving-to-trash t + global-auto-revert-non-file-buffers t remote-file-name-inhibit-delete-by-moving-to-trash t - use-file-dialog nil + save-buffer-coding-system 'utf-8-unix + select-enable-clipboard t use-dialog-box nil + use-file-dialog nil + user-full-name "Aryadev Chavali" + user-mail-address "aryadev@aryadevchavali.com" warning-minimum-level :error) :config (fset 'yes-or-no-p 'y-or-n-p) (global-auto-revert-mode) - (set-face-attribute 'default nil :height - (pcase (system-name) - ("rhmaiden" 120) - (_ 120)))) + (let ((font-size (pcase (system-name) + ("rhmaiden" 150) + (_ 120)))) + (set-face-attribute 'default nil :height font-size) + (set-face-attribute 'mode-line nil :height font-size))) #+end_src * Custom functionality and libraries This is custom Lisp that I or someone else has written which I really @@ -127,6 +129,11 @@ this macro. "For a given list of forms BODY, return a quoted 0 argument lambda." `(function (lambda nil ,@BODY))) + +(defmacro proc-int (&rest BODY) + "For a given list of forms BODY, return a quoted 0 argument +lambda with the first form of the lambda being (INTERACTIVE)." + `(function (lambda nil (interactive) ,@BODY))) #+end_src ** Clean buffer list If you've got a particularly long running Emacs instance, as I usually @@ -271,6 +278,20 @@ forcefully adjust the font size. (add-to-list 'enable-theme-functions #'+oreo/font-reset) #+end_src +** Proper paths in Emacs +Imagine you adjust your path in ZSH. This change won't necessarily +affect the results of ~(getenv "PATH")~ - you'd need to ensure Emacs +was loaded from a recent ZSH instance. This allows you to synchronise +the PATH variable with the shell to avoid any silly issues. + +#+begin_src emacs-lisp +(use-package exec-path-from-shell + :straight t + :demand t + :config + (when (member window-system '(mac ns x)) + (exec-path-from-shell-initialize))) +#+end_src * Essential packages External and internal packages absolutely necessary for the rest of this configuration. @@ -398,28 +419,31 @@ set of examples on how to use general. :init (setq duplicate-line-final-position -1 async-shell-command-buffer 'new-buffer) + :config + (defmacro +oreo/then-recenter-top (&rest actions) + `(proc-int ,@actions (recenter 0))) :general (leader "SPC" #'execute-extended-command "R" #'revert-buffer - ":" (proc (interactive) (switch-to-buffer "*scratch*")) + ":" (proc-int (switch-to-buffer "*scratch*")) "!" #'async-shell-command "h" #'help-command) (mode-leader - "t" (proc (interactive) (+oreo/load-theme)) - "T" (proc (interactive) (+oreo/switch-theme))) + "t" (proc-int (+oreo/load-theme)) + "T" (proc-int (+oreo/switch-theme))) (code-leader - "F" (proc (interactive) (find-file "~/Code/"))) + "F" (proc-int (find-file "~/Code/"))) (search-leader "i" #'imenu) (file-leader "f" #'find-file - "P" (proc (interactive) - (find-file (concat user-emacs-directory "config.org"))) + "P" (proc-int + (find-file (concat user-emacs-directory "config.org"))) "F" #'find-file-other-window "t" #'find-file-other-tab "v" #'add-file-local-variable @@ -448,25 +472,31 @@ set of examples on how to use general. "c" #'+literate/compile-config "C" #'+literate/clean-config "l" #'+literate/load-config - "s" (proc (interactive) (find-file (concat user-emacs-directory "straight/")))) + "s" (proc-int (find-file (concat user-emacs-directory "straight/")))) + + (leader + :prefix "SPC n" + "p" #'narrow-to-page + "f" #'narrow-to-defun + "r" #'narrow-to-region + "w" #'widen) ;; General normal/motion state maps (nmmap :keymaps 'override - "M-%" #'replace-regexp-as-diff + "M-'" #'replace-regexp-as-diff + "M-%" #'query-replace-regexp "M-o" #'duplicate-dwim "M-;" #'comment-dwim "gC" #'comment-dwim "g=" #'align-regexp "C--" #'text-scale-decrease "C-=" #'text-scale-increase - "C-+" #'text-scale-adjust) + "C-+" #'text-scale-adjust + "M-[" (+oreo/then-recenter-top (backward-paragraph)) + "M-]" (+oreo/then-recenter-top (forward-paragraph))) ;; Key chord jk to exit insert-state - (imap - "j" (general-key-dispatch #'self-insert-command - :timeout 0.25 - "k" #'evil-normal-state)) (:keymaps 'override "M-ESC" #'keyboard-quit) @@ -598,7 +628,7 @@ in it. :defer t :init (setq enable-recursive-minibuffers t - completion-styles '(basic substring flex) + completion-styles '(basic flex substring) completion-category-defaults nil completion-category-overrides '((file (styles flex partial-completion substring))) @@ -682,6 +712,8 @@ outperforming ~icomplete~ consistently when displaying results. "DEL" #'vertico-directory-delete-char) (:state '(normal insert) :keymaps 'vertico-grid-map + "M-K" #'vertico-grid-scroll-down + "M-J" #'vertico-grid-scroll-up "M-h" #'vertico-grid-left "M-l" #'vertico-grid-right)) #+end_src @@ -721,6 +753,10 @@ embark act more like how you wish, which I've barely touch on here. :general (:keymaps 'override "M-m" #'embark-act) + :display + ("\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))) :init (setq embark-verbose-indicator-display-action '((display-buffer-in-side-window) @@ -1539,6 +1575,7 @@ description I give won't do it justice. (use-package aggressive-indent :straight t :hook (emacs-lisp-mode-hook . aggressive-indent-mode) + :hook (scheme-mode-hook . aggressive-indent-mode) :hook (lisp-mode-hook . aggressive-indent-mode)) #+end_src ** Compilation @@ -1576,7 +1613,9 @@ so you can actually read the text. (nmap "M-r" #'recompile) (:keymaps 'compilation-mode-map - "g" nil) ;; by default this is recompile + "g" nil ;; by default this is recompile + "M-j" #'compilation-next-error + "M-k" #'compilation-previous-error) (nmmap :keymaps 'compilation-mode-map "c" #'recompile) @@ -1640,6 +1679,7 @@ Here I: (leader "p" project-prefix-map) :config + (setq project-vc-extra-root-markers '(".project")) (defun +project/command (folder) (format "ctags -Re -f %sTAGS %s*" folder folder)) @@ -1705,25 +1745,17 @@ clearly. "L" #'+license/insert-complete-license)) #+end_src ** diff mode -Oh diffs; the way of the ancient ones. Nowadays we use our newfangled -"pull requests" and "cool web interfaces" to manage changes in our -code repositories, but old school projects use patches to make code -changes. They're a pain to distribute and can be very annoying to use -when applying them to code. Even then I somewhat like patches, if -only for their simplicity. - -[[https://git.aryadevchavali.com/dwm][dwm]] uses patches for adding -new features and Emacs has great functionality to work with patches -effectively. Here I configure ~diff-mode~, which provides most of -this cool stuff, to be a bit more ergonomic with ~evil~. - +Good diff management is essentially mandatory in development. Emacs +comes with functionality out of the box to generate, manipulate, and +apply diffs - here I configure a small subset. #+begin_src emacs-lisp (use-package diff-mode :general (nmmap :keymaps 'diff-mode-map - "}" #'diff-hunk-next - "{" #'diff-hunk-prev + "J" #'diff-hunk-next + "K" #'diff-hunk-prev + "M-RET" #'diff-apply-hunk "RET" #'diff-goto-source)) #+end_src * Org mode @@ -1847,6 +1879,7 @@ Emacs was very helpful here. :defer t :init (setq org-edit-src-content-indentation 0 + org-bookmark-names-plist nil org-eldoc-breadcrumb-separator " → " org-enforce-todo-dependencies t org-export-backends '(ascii html latex odt icalendar) @@ -1901,6 +1934,9 @@ write the code. :general (nmmap "M-F" #'org-open-at-point) + (nmmap + :keymaps 'org-mode-map + "TAB" #'org-cycle) (file-leader "l" #'org-store-link) (insert-leader @@ -1911,9 +1947,6 @@ write the code. (local-leader :states '(normal motion) :keymaps 'org-mode-map - "l" nil - "'" nil - "c" nil "r" #'org-list-repair "d" #'org-date-from-calendar "t" #'org-todo @@ -1966,7 +1999,7 @@ a very tidy way to manage your time. (evil-set-initial-state 'org-agenda-mode 'normal) :general (file-leader - "a" (proc (interactive) + "a" (proc-int (--> (directory-files (car org-agenda-files)) (mapcar #'(lambda (x) (concat (car org-agenda-files) x)) it) (completing-read "Enter directory: " it nil t) @@ -2093,12 +2126,13 @@ learnt the basics of org). (setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t tex:dvipng" org-msg-greeting-name-limit 3) - (add-to-list 'org-msg-enforce-css - '(img latex-fragment-inline - ((transform . ,(format "translateY(-1px) scale(%.3f)" - (/ 1.0 (if (boundp 'preview-scale) - preview-scale 1.4)))) - (margin . "0 -0.35em"))))) + (add-to-list + 'org-msg-enforce-css + '(img latex-fragment-inline + ((transform . ,(format "translateY(-1px) scale(%.3f)" + (/ 1.0 (if (boundp 'preview-scale) + preview-scale 1.4)))) + (margin . "0 -0.35em"))))) #+end_src ** Org for evil Evil org for some nice bindings. @@ -2107,957 +2141,27 @@ Evil org for some nice bindings. (use-package evil-org :straight t :defer t - :hook (org-mode-hook . evil-org-mode) - :general - (nmmap - :keymaps 'org-mode-map - "TAB" #'org-cycle)) -#+end_src -** Org bookmark -I maintain a bookmarks file at =~/Text/bookmarks.org=. I would like -the ability to construct new bookmarks and open bookmarks. They may -be either articles I want to read, useful information documents or -just straight up youtube videos. So I wrote a -[[file:elisp/org-bookmark.el][library]] myself which does the -appropriate dispatching and work for me. Pretty sweet! - -Also I define a template for org-capture here for bookmarks and add it -to the list ~org-capture-templates~. - -#+begin_src emacs-lisp -(use-package org-bookmark - :defer t - :load-path "elisp/" - :general - (file-leader - "b" #'org-bookmark/open-bookmark) - :init - (with-eval-after-load "org-capture" - (add-to-list - 'org-capture-templates - '("b" "Bookmark" entry - (file "bookmarks.org") - "* %? :bookmark: -%T -%^{url|%x}p -" - )))) -#+end_src -* Languages -For a variety of (programming) languages Emacs comes with default -modes but this configures them as well as pulls any modes Emacs -doesn't come with. -** Makefile -Defines an auto-insert for Makefiles. Assumes C but it's very easy to -change it for C++. - -#+begin_src emacs-lisp -(use-package make-mode - :defer t - :auto-insert - (("[mM]akefile\\'" . "Makefile skeleton") - "" - "CC=gcc -OUT=main.out -LIBS= -ARGS= - -RELEASE=0 -GFLAGS=-Wall -Wextra -Werror -Wswitch-enum -std=c11 -DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined -RFLAGS=-O3 -ifeq ($(RELEASE), 1) -CFLAGS=$(GFLAGS) $(RFLAGS) -else -CFLAGS=$(GFLAGS) $(DFLAGS) -endif - -.PHONY: all -all: $(OUT) - -$(OUT): main.c - $(CC) $(CFLAGS) $^ -o $@ $(LIBS) - -.PHONY: run -run: $(OUT) - ./$^ $(ARGS) - -.PHONY: -clean: - rm -v $(OUT) -" - _)) -#+end_src -** WAIT SQL -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -The default SQL package provides support for connecting to common -database types (sqlite, mysql, etc) for auto completion and query -execution. I don't use SQL currently but whenever I need it it's -there. - -#+begin_src emacs-lisp -(use-package sql - :defer t - :init - (setq sql-display-sqli-buffer-function nil)) -#+end_src -** NHexl -Hexl-mode is the inbuilt package within Emacs to edit hex and binary -format buffers. There are a few problems with hexl-mode though, -including an annoying prompt on /revert-buffer/. - -Thus, nhexl-mode! It comes with a few other improvements. Check out -the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself. - -#+begin_src emacs-lisp -(use-package nhexl-mode - :straight t - :defer t - :mode ("\\.bin" "\\.out")) -#+end_src -** NASM -#+begin_src emacs-lisp -(use-package nasm-mode - :straight t - :defer t - :mode ("\\.asm" . nasm-mode)) -#+end_src -** C/C++ -Setup for C and C++ modes, using Emacs' default package: cc-mode. -*** cc-mode -Tons of stuff, namely: -+ ~auto-fill-mode~ for 80 char limit -+ Some keybindings to make evil statement movement easy -+ Lots of pretty symbols -+ Indenting options and a nice (for me) code style for C -+ Auto inserts to get a C file going - -#+begin_src emacs-lisp -(use-package cc-mode - :defer t - :hook - ((c-mode-hook c++-mode-hook) . auto-fill-mode) - :general - (:keymaps '(c-mode-map c++-mode-map) - :states '(normal motion visual) - "(" #'c-beginning-of-statement - ")" #'c-end-of-statement - "{" #'c-beginning-of-defun - "}" #'c-end-of-defun) - :init - (setq c-basic-offset 2 - c-auto-newline nil - c-default-style '((other . "user"))) - (add-hook 'c-mode-hook (proc (c-toggle-comment-style -1))) - (add-hook 'c++-mode-hook (proc (c-toggle-comment-style -1))) - (defun +cc/copyright-notice () - (let* ((lines (split-string (+license/copyright-notice) "\n")) - (copyright-line (car lines)) - (rest (cdr lines))) - (concat - "* " - copyright-line - "\n" - (mapconcat - #'(lambda (x) - (if (string= x "") - "" - (concat " * " x))) - rest - "\n")))) - :auto-insert - (("\\.c\\'" . "C skeleton") - "" - "/" (+cc/copyright-notice) "\n\n" - " * Created: " (format-time-string "%Y-%m-%d") "\n" - " * Description: " _ "\n" - " */\n" - "\n") - (("\\.cpp\\'" "C++ skeleton") - "" - "/" (+cc/copyright-notice) "\n\n" - " * Created: " (format-time-string "%Y-%m-%d") "\n" - " * Description: " _ "\n" - " */\n" - "\n") - (("\\.\\([Hh]\\|hh\\|hpp\\|hxx\\|h\\+\\+\\)\\'" . "C / C++ header") - (replace-regexp-in-string "[^A-Z0-9]" "_" - (string-replace "+" "P" - (upcase - (file-name-nondirectory buffer-file-name)))) - "/" (+cc/copyright-notice) "\n\n" - " * Created: " (format-time-string "%Y-%m-%d") "\n" - " * Description: " _ "\n" - " */\n\n" - "#ifndef " str n "#define " str "\n\n" "\n\n#endif") - :config - (c-add-style - "user" - '((c-basic-offset . 2) - (c-comment-only-line-offset . 0) - (c-hanging-braces-alist (brace-list-open) - (brace-entry-open) - (substatement-open after) - (block-close . c-snug-do-while) - (arglist-cont-nonempty)) - (c-cleanup-list brace-else-brace) - (c-offsets-alist - (statement-block-intro . +) - (substatement-open . 0) - (access-label . -) - (inline-open . 0) - (label . 0) - (statement-cont . +))))) -#+end_src -*** Clang format -clang-format is a program that formats C/C++ files. It's highly -configurable and quite fast. I have a root configuration in my -Dotfiles (check it out -[[file:~/Dotfiles/ClangFormat/).clang-format][here]]. - -Clang format comes inbuilt with clang/LLVM, so it's quite likely to be -on your machine. - -#+begin_src emacs-lisp -(use-package clang-format - :load-path "/usr/share/clang/" - :defer t - :after cc-mode - :commands (+code/clang-format-region-or-buffer - clang-format-mode) - :general - (code-leader - :keymaps '(c-mode-map c++-mode-map) - "f" #'clang-format-buffer) - :config - (define-minor-mode clang-format-mode - "On save formats the current buffer via clang-format." - :lighter nil - (let ((save-func (proc (interactive) - (clang-format-buffer)))) - (if clang-format-mode - (add-hook 'before-save-hook save-func nil t) - (remove-hook 'before-save-hook save-func t)))) - (defun +code/clang-format-region-or-buffer () - (interactive) - (if (mark) - (clang-format-region (region-beginning) (region-end)) - (clang-format-buffer)))) -#+end_src -*** cc org babel -To ensure org-babel executes language blocks of C/C++, I need to load -it as an option in ~org-babel-load-languages~. - -#+begin_src emacs-lisp -(use-package org - :after cc-mode - :init - (org-babel-do-load-languages - 'org-babel-load-languages - '((C . t)))) -#+end_src -*** cc compile fsan -Sanitisers are a blessing for C/C++. An additional runtime on top of -the executable which catches stuff like undefined behaviour or memory -leaks make it super easy to see where and how code is failing. -However, by default, Emacs' compilation-mode doesn't understand the -logs =fsanitize= makes so you usually have to manually deal with it -yourself. - -Compilation mode uses regular expressions to figure out whether -something is an error and how to navigate to the file where that error -is located. So adding support for =-fsanitize= is as simple as making -a regular expression which captures file names and digits - -#+begin_src emacs-lisp -(use-package compile - :after cc-mode - :config - (add-to-list 'compilation-error-regexp-alist-alist - `(fsan ,(rx (and - line-start " #" digit " 0x" (1+ hex) " in " - (1+ (or word "_")) " " - (group (seq (* any) (or ".c" ".cpp" ".h" ".hpp"))) ":" - (group (+ digit)))) - - 1 2)) - (add-to-list 'compilation-error-regexp-alist - 'fsan)) -#+end_src -** Markdown -Why use Markdown when you have org-mode? Because LSP servers -sometimes format their documentation as markdown, which -[[*Eglot][Eglot]] can use to provide nicer views on docs! -#+begin_src emacs-lisp -(use-package markdown-mode - :defer t - :straight t) -#+end_src -** WAIT Rust -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -2025-02-15: Haven't needed to use Rust at all recently - but leaving -this here in case. - -Rust is the systems programming language that also does web stuff and -CLI programs and basically tries to be a jack of all trades. It's got -some interesting stuff but most importantly it's very new, so everyone -must learn it, right? - -#+begin_src emacs-lisp -(use-package rust-mode - :straight t - :defer t - :general - (code-leader - :keymaps 'rust-mode-map - "f" #'rust-format-buffer) - (local-leader - :keymaps 'rust-mode-map - "c" #'rust-run-clippy) - :init - (setq rust-format-on-save t) - (with-eval-after-load "eglot" - (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer")))) -#+end_src -** WAIT Racket -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -A scheme with lots of stuff inside it. Using it for a language design -book so it's useful to have some Emacs binds for it. - -#+begin_src emacs-lisp -(use-package racket-mode - :straight t - :defer t - :hook (racket-mode-hook . racket-xp-mode) - :display - ("\\*Racket REPL*" - (display-buffer-at-bottom) - (window-height . 0.3)) - :init - (setq racket-documentation-search-location 'local) - :general - (nmap - :keymaps 'racket-describe-mode-map - "q" #'quit-window) - (nmap - :keymaps 'racket-mode-map - "gr" #'racket-eval-last-sexp) - (local-leader - :keymaps '(racket-mode-map racket-repl-mode-map) - "d" #'racket-repl-describe) - (local-leader - :keymaps 'racket-mode-map - "r" #'racket-run - "i" #'racket-repl - "e" #'racket-send-definition - "sr" #'racket-send-region - "sd" #'racket-send-definition)) -#+end_src -** WAIT Haskell -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -2025-02-15: Haskell is a fun language so I'll leave this configuration -for now. - -Haskell is a static lazy functional programming language (what a -mouthful). It's quite a beautiful language and really learning it -will change the way you think about programming. However, my -preferred functional language is still unfortunately Lisp so no extra -brownie points there. - -Here I configure the REPL for Haskell via the -~haskell-interactive-mode~. I also load my custom package -[[file:elisp/haskell-multiedit.el][haskell-multiedit]] which allows a -user to create temporary ~haskell-mode~ buffers that, upon completion, -will run in the REPL. Even easier than making your own buffer. - -#+begin_src emacs-lisp -(use-package haskell-mode - :straight t - :defer t - :hook - (haskell-mode-hook . haskell-indentation-mode) - (haskell-mode-hook . interactive-haskell-mode) - :display - ("\\*haskell.**\\*" - (display-buffer-at-bottom) - (window-height . 0.3)) - :general - (shell-leader - "h" #'haskell-interactive-bring) - (local-leader - :keymaps 'haskell-mode-map - "c" #'haskell-compile - "t" #'haskell-process-do-type) - (nmmap - :keymaps 'haskell-mode-map - "C-c C-c" #'haskell-process-load-file) - (local-leader - :keymaps 'haskell-interactive-mode-map - "c" #'haskell-interactive-mode-clear) - (imap - :keymaps 'haskell-interactive-mode-map - "M-k" #'haskell-interactive-mode-history-previous - "M-j" #'haskell-interactive-mode-history-next) - :init - (setq haskell-interactive-prompt "[λ] " - haskell-interactive-prompt-cont "{λ} " - haskell-interactive-popup-errors nil - haskell-stylish-on-save t - haskell-process-type 'auto) - :config - (load (concat user-emacs-directory "elisp/haskell-multiedit.el"))) -#+end_src -** Python -Works well for python. If you have ~pyls~ it should be on your path, so -just run eglot if you need. But an LSP server is not necessary for a -lot of my time in python. Here I also setup org-babel for python -source code blocks. - -#+begin_src emacs-lisp -(use-package python - :defer t - :general - (nmmap - :keymaps 'python-mode-map - "C-M-x" #'python-shell-send-defun) - (local-leader - :keymaps 'python-mode-map - "c" #'python-check) - (local-leader - :keymaps 'python-mode-map - :infix "e" - "e" #'python-shell-send-statement - "r" #'python-shell-send-region - "f" #'python-shell-send-buffer) - :pretty - (python-mode-hook - ("None" . "Ø") - ("list" . "ℓ") - ("List" . "ℓ") - ("str" . "𝕊") - ("!" . "¬") - ("for" . "∀") - ("print" . "φ") - ("lambda" . "λ") - ("reduce" . "↓") - ("map" . "→") - ("return" . "≡") - ("yield" . "≈")) - :init - (setq python-indent-offset 4) - :config - (with-eval-after-load "org-mode" - (setf (alist-get 'python org-babel-load-languages) t))) -#+end_src -*** Python shell -Setup for python shell, including a toggle option - -#+begin_src emacs-lisp -(use-package python - :defer t - :commands +python/toggle-repl - :general - (shell-leader - "p" #'run-python) - :hook - (inferior-python-mode-hook . company-mode) - :display - ("\\*Python\\*" - (display-buffer-at-bottom) - (window-height . 0.3))) -#+end_src -** YAML -YAML is a data language which is useful for config files. - -#+begin_src emacs-lisp -(use-package yaml-mode - :defer t - :straight t) -#+end_src -** HTML/CSS/JS -Firstly, web mode for consistent colouring of syntax. - -#+begin_src emacs-lisp -(use-package web-mode - :straight t - :defer t - :mode ("\\.html" . web-mode) - :mode ("\\.css" . web-mode) - :custom - ((web-mode-code-indent-offset 2) - (web-mode-markup-indent-offset 2) - (web-mode-css-indent-offset 2))) -#+end_src -*** Emmet -Emmet for super speed code writing. - -#+begin_src emacs-lisp -(use-package emmet-mode - :straight t - :defer t - :hook (web-mode-hook . emmet-mode) - :general - (imap - :keymaps 'emmet-mode-keymap - "TAB" #'emmet-expand-line - "M-j" #'emmet-next-edit-point - "M-k" #'emmet-prev-edit-point)) -#+end_src -*** HTML Auto insert -An auto-insert for HTML buffers, which just adds some nice stuff. - -#+begin_src emacs-lisp -(use-package web-mode - :defer t - :auto-insert - (("\\.html\\'" . "HTML Skeleton") - "" - "<!doctype html> -<html lang=''> - <head> - <meta charset='utf-8'> - <title>"(read-string "Enter title: ") | """</title> - <meta name='description' content='" (read-string "Enter description: ") | "" "'> - <meta name='author' content='"user-full-name"'/> - <meta name='viewport' content='width=device-width, initial-scale=1'> - - <link rel='apple-touch-icon' href='/apple-touch-icon.png'> - <link rel='shortcut icon' href='/favicon.ico'/> - </head> - <body> -" - _ - " </body> -</html>")) -#+end_src -*** Javascript Mode -A better mode for JavaScript that also has automatic integration with -eglot. - -#+begin_src emacs-lisp -(use-package js - :defer t - :mode ("\\.js" . js-mode) - :hook (js-mode-hook . auto-fill-mode) - :init - (setq js-indent-level 2)) -#+end_src -*** Typescript -A language that adds a build step to JavaScript projects for "static" -typing. It's nice because it adds good auto completion. - -#+begin_src emacs-lisp -(use-package typescript-mode - :straight t - :defer t - :init - (setq typescript-indent-level 2)) -#+end_src -** Scheme -Another Lisp but simpler than the rest. A beauty of engineering and -fun to write programs in. Here I setup ~geiser~, which is the -premiere way to interact with scheme REPLs. - -#+begin_src emacs-lisp -(use-package geiser - :defer t - :straight t - :display - ("\\*Geiser.*" - (display-buffer-reuse-mode-window display-buffer-at-bottom) - (window-height . 0.3)) - :general - (shell-leader - "S" #'geiser) - (local-leader - :keymaps 'scheme-mode-map - "t" #'geiser - "m" #'geiser-doc-look-up-manual - "d" #'geiser-doc-symbol-at-point) - (local-leader - :keymaps 'scheme-mode-map - :infix "e" - "e" #'geiser-eval-last-sexp - "b" #'geiser-eval-buffer - "d" #'geiser-eval-definition - "r" #'geiser-eval-region) - :init - (with-eval-after-load "evil" - (evil-set-initial-state 'geiser-debug-mode-map 'emacs))) - -(use-package geiser-guile - :defer t - :straight t) -#+end_src -** WAIT Ocaml -:PROPERTIES: -:header-args:emacs-lisp: :tangle no :results none -:END: -*** Ocaml Setup -Firstly, install ~opam~ and ~ocaml~. Then run the following script: -#+begin_src sh -opam install tuareg ocamlformat odoc utop merlin user-setup; -opam user-setup install; -mv ~/.emacs.d/opam-user-setup.el ~/.config/emacs/elisp; -rm -rf ~/.emacs.d ~/.emacs; -#+end_src - -This sets up the necessary packages (particularly Emacs Lisp) and some -configuration that ensures Emacs is consistent with the user -installation. Notice the moving of =opam-user-setup.el= into -=~/.config/emacs/elisp=, which we'll use to setup the ocaml -experience. -*** Ocaml Configuration -Here I load the =opam-user-setup= package setup earlier, with some -neat tips from the default =~/.emacs= generated by ~opam user-setup -install~. -#+begin_src emacs-lisp -(use-package opam-user-setup - :defer t - :load-path "elisp/" - :mode ("\\.ml" . tuareg-mode) - :hook (tuareg-mode-hook . whitespace-mode) - :display - ("\\*utop\\*" - (display-buffer-at-bottom) - (window-height . 0.3)) - :general - (code-leader - :keymaps 'tuareg-mode-map - "f" #'+ocaml/format-buffer) - :config - (defun +ocaml/format-buffer () - (interactive) - (when (eq major-mode 'tuareg-mode) - (let ((name (buffer-file-name (current-buffer))) - (format-str "ocamlformat -i --enable-outside-detected-project %s")) - (save-buffer) - (set-process-sentinel (start-process-shell-command "ocamlformat" "*ocamlformat*" - (format format-str name)) - (lambda (p event) - (when (string= event "finished\n") - (revert-buffer nil t) - (message "[ocamlformat] Finished."))))))) - (add-to-list 'compilation-error-regexp-alist-alist - `(ocaml - "[Ff]ile \\(\"\\(.*?\\)\", line \\(-?[0-9]+\\)\\(, characters \\(-?[0-9]+\\)-\\([0-9]+\\)\\)?\\)\\(:\n\\(\\(Warning .*?\\)\\|\\(Error\\)\\):\\)?" - 2 3 (5 . 6) (9 . 11) 1 (8 compilation-message-face))) - (add-to-list 'compilation-error-regexp-alist - 'ocaml) - :general - (local-leader - :keymaps 'tuareg-mode-map - "u" #'utop) - (local-leader - :keymaps 'tuareg-mode-map - :infix "e" - "r" #'utop-eval-region - "e" #'utop-eval-phrase - "b" #'utop-eval-buffer)) - -(use-package merlin-eldoc - :straight t - :after opam-user-setup - :hook - (tuareg-mode-hook . merlin-eldoc-setup) - :init - (setq merlin-eldoc-occurrences nil)) -#+end_src -** Lisp -Emacs is the greatest Lisp editor around, there are no two ways about -it. Here I setup the configuration for Emacs Lisp and Common Lisp. -*** Lisp configuration -All the general stuff I do for any other language: pretty symbols and -key bindings. -#+begin_src emacs-lisp -(use-package lisp-mode - :pretty - (lisp-mode-hook - ("lambda" . "λ") - ("nil" . "Ø") - ("<=" . "≤") - (">=" . "≥") - ("defun" . "ƒ") - ("mapcar" . "→") - ("reduce" . "↓") - ("some" . "∃") - ("every" . "∀") - ("LAMBDA" . "λ") - ("NIL" . "Ø") - ("<=" . "≤") - (">=" . "≥") - ("DEFUN" . "ƒ") - ("MAPCAR" . "→") - ("REDUCE" . "↓") - ("SOME" . "∃") - ("EVERY" . "∀")) - (emacs-lisp-mode-hook - ("lambda" . "λ") - ("nil" . "Ø") - ("defun" . "ƒ") - ("mapcar" . "→") - ("LAMBDA" . "λ") - ("NIL" . "Ø") - ("DEFUN" . "ƒ") - ("MAPCAR" . "→")) - :general - (:states '(normal motion insert visual) - :keymaps 'lisp-mode-shared-map - "C-j" #'sp-forward-slurp-sexp - "C-k" #'sp-forward-barf-sexp) - (:states '(normal motion visual) - :keymaps 'lisp-mode-shared-map - ")" #'sp-next-sexp - "(" #'sp-previous-sexp)) -#+end_src -*** Common Lisp auto insert -Like C/C++'s auto insert, but with Common Lisp comments. -#+begin_src emacs-lisp -(use-package lisp-mode - :init - (defun +lisp/copyright-notice () - (let* ((lines (split-string (+license/copyright-notice) "\n")) - (copyright-line (car lines)) - (rest (cdr lines))) - (--> - (lambda (x) - (if (string= x "") - "" - (concat ";; " x))) - (mapconcat it rest "\n") - (format ";; %s\n%s\n" - copyright-line - it)))) - :auto-insert - (("\\.lisp\\'" . "Common Lisp Skeleton") - "" - ";;; " (file-name-nondirectory (buffer-file-name)) " - " - (format-time-string "%Y-%m-%d") "\n\n" - (+lisp/copyright-notice) "\n" - ";;; Commentary:\n\n;;\n\n;;; Code:\n")) -#+end_src -*** Sly -While Emacs does an okay job for editing Common Lisp it's not amazing -for actually developing large scale projects. Considering how good an -environment Emacs is for Emacs Lisp, and how similar the two languages -are, we shouldn't need an LSP. - -Enter /SLY/. Sly is a fork of /SLIME/ and it provides the essential -components to elevate Emacs' ability to develop Common Lisp. I feel -calling the ability Sly gives you "IDE-like" a slight against it - no -IDE I have used is as capable in aiding development as Emacs + Sly. - -#+begin_src emacs-lisp -(use-package sly - :defer t - :straight t - :init - (setq inferior-lisp-program "sbcl" - sly-lisp-loop-body-forms-indentation 0) - :display - ("\\*sly-db" - (display-buffer-at-bottom) - (window-height . 0.25)) - ("\\*sly-inspector" - (display-buffer-at-bottom) - (window-height . 0.25)) - ("\\*sly-mrepl" - (display-buffer-in-side-window) - (window-width . 0.3) - (side . right)) - :config - (evil-set-initial-state 'sly-db-mode 'normal) - (with-eval-after-load "org" - (setq-default org-babel-lisp-eval-fn #'sly-eval)) - (with-eval-after-load "company" - (add-hook 'sly-mrepl-hook #'company-mode)) - :general - (shell-leader - "s" #'sly) - (nmap - :keymaps 'lisp-mode-map - "gr" #'sly-eval-buffer - "gd" #'sly-edit-definition - "gR" #'sly-who-calls) - - (local-leader - :keymaps 'lisp-mode-map - "a" #'sly-apropos - "d" #'sly-describe-symbol - "s" #'sly-mrepl-sync - "l" #'sly-load-file - "c" #'sly-compile-defun - "D" #'sly-documentation-lookup - "C" #'sly-compile-file) - (local-leader - :keymaps 'lisp-mode-map - :infix "e" - "b" #'sly-eval-buffer - "e" #'sly-eval-last-expression - "f" #'sly-eval-defun - "r" #'sly-eval-region) - (nmap - :keymaps 'sly-mrepl-mode-map - "M-j" #'sly-mrepl-next-input-or-button - "M-k" #'sly-mrepl-previous-input-or-button - "C-j" #'sly-mrepl-next-prompt - "C-k" #'sly-mrepl-previous-prompt) - (local-leader - :keymaps 'sly-mrepl-mode-map - "c" #'sly-mrepl-clear-repl - "s" #'sly-mrepl-shortcut) - (nmap - :keymaps 'sly-db-mode-map - "C-i" #'sly-db-cycle - "g?" #'describe-mode - "S" #'sly-db-show-frame-source - "e" #'sly-db-eval-in-frame - "d" #'sly-db-pprint-eval-in-frame - "D" #'sly-db-disassemble - "i" #'sly-db-inspect-in-frame - "gj" #'sly-db-down - "gk" #'sly-db-up - "C-j" #'sly-db-down - "C-k" #'sly-db-up - "]]" #'sly-db-details-down - "[[" #'sly-db-details-up - "M-j" #'sly-db-details-down - "M-k" #'sly-db-details-up - "G" #'sly-db-end-of-backtrace - "t" #'sly-db-toggle-details - "gr" #'sly-db-restart-frame - "I" #'sly-db-invoke-restart-by-name - "R" #'sly-db-return-from-frame - "c" #'sly-db-continue - "s" #'sly-db-step - "n" #'sly-db-next - "o" #'sly-db-out - "b" #'sly-db-break-on-return - "a" #'sly-db-abort - "q" #'sly-db-quit - "A" #'sly-db-break-with-system-debugger - "B" #'sly-db-break-with-default-debugger - "P" #'sly-db-print-condition - "C" #'sly-db-inspect-condition - "g:" #'sly-interactive-eval - "0" #'sly-db-invoke-restart-0 - "1" #'sly-db-invoke-restart-1 - "2" #'sly-db-invoke-restart-2 - "3" #'sly-db-invoke-restart-3 - "4" #'sly-db-invoke-restart-4 - "5" #'sly-db-invoke-restart-5 - "6" #'sly-db-invoke-restart-6 - "7" #'sly-db-invoke-restart-7 - "8" #'sly-db-invoke-restart-8 - "9" #'sly-db-invoke-restart-9) - (nmap - :keymaps 'sly-inspector-mode-map - "q" #'sly-inspector-quit)) -#+end_src -*** Lisp indent function -Add a new lisp indent function which indents newline lists more -appropriately. - -#+begin_src emacs-lisp -(use-package lisp-mode - :defer t - :config - (defun +oreo/lisp-indent-function (indent-point state) - (let ((normal-indent (current-column)) - (orig-point (point))) - (goto-char (1+ (elt state 1))) - (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) - (cond - ;; car of form doesn't seem to be a symbol, or is a keyword - ((and (elt state 2) - (or (not (looking-at "\\sw\\|\\s_")) - (looking-at ":"))) - (if (not (> (save-excursion (forward-line 1) (point)) - calculate-lisp-indent-last-sexp)) - (progn (goto-char calculate-lisp-indent-last-sexp) - (beginning-of-line) - (parse-partial-sexp (point) - calculate-lisp-indent-last-sexp 0 t))) - ;; Indent under the list or under the first sexp on the same - ;; line as calculate-lisp-indent-last-sexp. Note that first - ;; thing on that line has to be complete sexp since we are - ;; inside the innermost containing sexp. - (backward-prefix-chars) - (current-column)) - ((and (save-excursion - (goto-char indent-point) - (skip-syntax-forward " ") - (not (looking-at ":"))) - (save-excursion - (goto-char orig-point) - (looking-at ":"))) - (save-excursion - (goto-char (+ 2 (elt state 1))) - (current-column))) - (t - (let ((function (buffer-substring (point) - (progn (forward-sexp 1) (point)))) - method) - (setq method (or (function-get (intern-soft function) - 'lisp-indent-function) - (get (intern-soft function) 'lisp-indent-hook))) - (cond ((or (eq method 'defun) - (and (null method) - (> (length function) 3) - (string-match "\\`def" function))) - (lisp-indent-defform state indent-point)) - ((integerp method) - (lisp-indent-specform method state - indent-point normal-indent)) - (method - (funcall method indent-point state)))))))) - (setq-default lisp-indent-function #'+oreo/lisp-indent-function)) + :hook (org-mode-hook . evil-org-mode)) #+end_src * Applications Emacs is an operating system, now with a good text editor through [[*Evil - Vim emulation][Evil]]. Let's configure some apps for it. -** EWW -Emacs Web Wowser is the inbuilt text based web browser for Emacs. It -can render images and basic CSS styles but doesn't have a JavaScript -engine, which makes sense as it's primarily a text interface. - -#+begin_src emacs-lisp -(use-package eww - :defer t - :general - (app-leader - "w" #'eww) - (nmmap - :keymaps 'eww-mode-map - "w" #'evil-forward-word-begin - "Y" #'eww-copy-page-url)) -#+end_src ** Magit Magit is *the* git porcelain for Emacs, which perfectly encapsulates -the git CLI. It's so good that some people are use Emacs just for it. -It's difficult to describe it well without using it and it integrates -so well with Emacs that there is very little need to use the git CLI -ever. +the git CLI. It's so good that some people use Emacs just for it. +It's another one of those "so indescribably good you have to try it" +things. I've hardly touched the Git CLI since getting Magit, and it +has actively taught me _new_ things about Git. -In this case I just need to setup the bindings for it. Also, define -an auto insert for commit messages so that I don't need to write -everything myself. +In this case I just need to setup the bindings for it. #+begin_src emacs-lisp (use-package transient - :defer t - :straight (:host github :repo "magit/transient" :tag "v0.7.5")) + :straight t) (use-package magit - :straight (:host github :repo "magit/magit" :tag "v4.1.0") + :after transient + :straight t :defer t :display ("magit:.*" @@ -3088,6 +2192,34 @@ everything myself. (with-eval-after-load "evil-collection" (evil-collection-magit-setup))) #+end_src +*** Magit Forge +Imagine being able to do all the bureaucratic nonsense involved on +GitHub i.e. pull requests, issue handling, etc. all through Emacs! No +need to imagine any more, with Magit Forge. +#+begin_src emacs-lisp +(use-package forge + :after magit + :straight t + :config + (with-eval-after-load "evil-collection" + (evil-collection-forge-setup))) +#+end_src +** EWW +Emacs Web Wowser is the inbuilt text based web browser for Emacs. It +can render images and basic CSS styles but doesn't have a JavaScript +engine, which makes sense as it's primarily a text interface. + +#+begin_src emacs-lisp +(use-package eww + :defer t + :general + (app-leader + "w" #'eww) + (nmmap + :keymaps 'eww-mode-map + "w" #'evil-forward-word-begin + "Y" #'eww-copy-page-url)) +#+end_src ** Calendar Calendar is a simple inbuilt application that helps with date functionalities. I add functionality to copy dates from the calendar @@ -3329,7 +2461,7 @@ Here I setup dired with a few niceties "d" #'dired "D" #'dired-other-window "i" #'image-dired - "b" (proc (interactive) (find-file "~/Text/Books/"))) + "b" (proc-int (find-file "~/Text/Books/"))) (local-leader :keymaps 'dired-mode-map "i" #'dired-maybe-insert-subdir @@ -3531,12 +2663,12 @@ them. (local-leader :keymaps 'eshell-mode-map - "g" (proc (interactive) + "g" (proc-int (let ((buffer (current-buffer))) (eshell/goto) (with-current-buffer buffer (eshell-send-input)))) - "l" (proc (interactive) + "l" (proc-int (eshell-return-to-prompt) (insert "ls") (eshell-send-input)) @@ -3722,7 +2854,7 @@ most distribution nowadays. :config (defun +rg/project-todo () (interactive) - (rg "TODO" "*" + (rg "TODO|WIP|FIXME" "*" (if (project-current) (project-root (project-current)) default-directory))) @@ -3745,8 +2877,8 @@ to elfeed for loading the system. "s" #'elfeed-search-live-filter "<return>" #'elfeed-search-show-entry) (nmmap - :keymaps 'elfeed-show-mode-map - "M-RET" #'+elfeed/dispatch) + :keymaps '(elfeed-search-mode-map elfeed-show-mode-map) + "M-RET" #'elfeed-dispatch) :init (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/")) :config @@ -3768,13 +2900,15 @@ to elfeed for loading the system. (message "elfeed-dispatch: EMPV is not available"))))) "Options available on entering an elfeed post.") - (defun +elfeed/dispatch () + (defun elfeed-dispatch () "Provide some extra options once you've clicked on an article." (interactive) - (if (null elfeed-show-entry) + (if (not (or elfeed-show-entry (eq major-mode 'elfeed-search-mode))) (user-error "elfeed-dispatch: Not in an elfeed post.")) (let ((choice (completing-read "Choose action: " (mapcar #'car +elfeed/dispatch-options))) - (url (elfeed-entry-link elfeed-show-entry))) + (url (elfeed-entry-link (if elfeed-show-entry + elfeed-show-entry + (elfeed-search-selected :ignore-region))))) (if-let ((option (cdr (assoc choice +elfeed/dispatch-options #'string=)))) (funcall option url))))) #+end_src @@ -3942,28 +3076,65 @@ and integrates slickly into image-dired. Of course, :general (nmmap :keymaps 'image-mode-map - "+" #'image-increase-size - "-" #'image-decrease-size - "a" #'image-toggle-animation - "p+" #'image-increase-speed - "p-" #'image-increase-speed - "h" #'image-backward-hscroll - "j" #'image-next-line - "k" #'image-previous-line - "l" #'image-forward-hscroll)) + "q" #'quit-window + ;; motion + "gg" 'image-bob + "G" 'image-eob + [remap evil-forward-char] 'image-forward-hscroll + [remap evil-backward-char] 'image-backward-hscroll + [remap evil-next-line] 'image-next-line + [remap evil-previous-line] 'image-previous-line + "0" 'image-bol + "^" 'image-bol + "$" 'image-eol + (kbd "C-d") 'image-scroll-up + (kbd "SPC") 'image-scroll-up + (kbd "S-SPC") 'image-scroll-down + (kbd "<delete>") 'image-scroll-down + ;; animation + (kbd "RET") 'image-toggle-animation + "F" 'image-goto-frame + "," 'image-previous-frame ; mplayer/mpv style + "." 'image-next-frame ; mplayer/mpv style + ";" 'image-next-frame ; Evil style + "{" 'image-decrease-speed ; mplayer/mpv style + "}" 'image-increase-speed ; mplayer/mpv style + + "H" 'image-transform-fit-to-height + "W" 'image-transform-fit-to-width + + "+" 'image-increase-size + "=" 'image-increase-size + "-" 'image-decrease-size + + "[[" 'image-previous-file + "]]" 'image-next-file + "gk" 'image-previous-file + "gj" 'image-next-file + (kbd "C-k") 'image-previous-file + (kbd "C-j") 'image-next-file + + (kbd "C-c C-c") 'image-toggle-display + + ;; quit + "q" 'quit-window + "ZQ" 'evil-quit + "ZZ" 'quit-window)) #+end_src ** empv Emacs MPV bindings, with very cool controls for queuing files for playing. #+begin_src emacs-lisp (use-package empv - :straight (:host github :repo "oreodave/empv.el") + :straight t :defer t :init (setq empv-audio-dir (list (expand-file-name "~/Media/audio") - "/sshx:oldboy:/media/hdd/content/Audio") + ;; "/sshx:oldboy:/media/hdd/content/Audio" + ) empv-video-dir (list (expand-file-name "~/Media/videos") - "/sshx:oldboy:/media/hdd/content/Videos") + ;; "/sshx:oldboy:/media/hdd/content/Videos" + ) empv-playlist-dir (expand-file-name "~/Media/playlists") empv-audio-file-extensions (list "mp3" "ogg" "wav" "m4a" "flac" "aac" "opus") empv-video-file-extensions (list "mkv" "mp4" "avi" "mov" "webm") @@ -3995,9 +3166,9 @@ in an Emacs-only map. (gud-hydra (:hint nil) "Hydra for GUD" ("<" #'gud-up "Up" - :column "Control Flow") + :column "Stack") (">" #'gud-down "Down" - :column "Control Flow") + :column "Stack") ("b" #'gud-break "Break" :column "Breakpoints") ("d" #'gud-remove "Remove" @@ -4013,15 +3184,13 @@ in an Emacs-only map. ("p" #'gud-print "Print" :column "Misc") ("c" #'gud-cont "Cont" - :column "Control Flow") + :column "Breakpoints") ("s" #'gud-step "Step" :column "Control Flow") ("t" #'gud-tbreak "Tbreak" - :column "Control Flow") + :column "Breakpoints") ("u" #'gud-until "Until" :column "Control Flow") - ("v" #'gud-go "Go" - :column "Control Flow") ("w" #'gud-watch "Watch" :column "Breakpoints") ("TAB" #'gud-stepi "Stepi" @@ -4030,6 +3199,901 @@ in an Emacs-only map. (code-leader "d" #'gud-hydra/body "D" #'gud-gdb)) #+end_src +** Jira +#+begin_src emacs-lisp +(use-package jira + :straight (:host github :repo "unmonoqueteclea/jira.el") + :init + (setq jira-base-url "https://reframe.atlassian.net") + :general + (app-leader + "j" #'jira-issues) + (nmmap + :keymaps 'jira-issues-mode-map + "M-RET" #'jira-issues-actions-menu)) +#+end_src +* Languages +For a variety of (programming) languages Emacs comes with default +modes but this configures them as well as pulls any modes Emacs +doesn't come with. +** Makefile +Defines an auto-insert for Makefiles. Assumes C but it's very easy to +change it for C++. + +#+begin_src emacs-lisp +(use-package make-mode + :defer t + :auto-insert + (("[mM]akefile\\'" . "Makefile skeleton") + "" + "CC=gcc +OUT=main.out +LIBS= +ARGS= + +RELEASE=0 +GFLAGS=-Wall -Wextra -Werror -Wswitch-enum -std=c11 +DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined +RFLAGS=-O3 +ifeq ($(RELEASE), 1) +CFLAGS=$(GFLAGS) $(RFLAGS) +else +CFLAGS=$(GFLAGS) $(DFLAGS) +endif + +.PHONY: all +all: $(OUT) + +$(OUT): main.c + $(CC) $(CFLAGS) $^ -o $@ $(LIBS) + +.PHONY: run +run: $(OUT) + ./$^ $(ARGS) + +.PHONY: +clean: + rm -v $(OUT) +" + _)) +#+end_src +** WAIT SQL +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +The default SQL package provides support for connecting to common +database types (sqlite, mysql, etc) for auto completion and query +execution. I don't use SQL currently but whenever I need it it's +there. + +#+begin_src emacs-lisp +(use-package sql + :defer t + :init + (setq sql-display-sqli-buffer-function nil)) +#+end_src +** NHexl +Hexl-mode is the inbuilt package within Emacs to edit hex and binary +format buffers. There are a few problems with hexl-mode though, +including an annoying prompt on /revert-buffer/. + +Thus, nhexl-mode! It comes with a few other improvements. Check out +the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself. + +#+begin_src emacs-lisp +(use-package nhexl-mode + :straight t + :defer t + :mode ("\\.bin" "\\.out")) +#+end_src +** NASM +#+begin_src emacs-lisp +(use-package nasm-mode + :straight t + :defer t + :mode ("\\.asm" . nasm-mode)) +#+end_src +** C/C++ +Setup for C and C++ modes, using Emacs' default package: cc-mode. +*** cc-mode +Tons of stuff, namely: ++ ~auto-fill-mode~ for 80 char limit ++ Some keybindings to make evil statement movement easy ++ Lots of pretty symbols ++ Indenting options and a nice (for me) code style for C ++ Auto inserts to get a C file going + +#+begin_src emacs-lisp +(use-package cc-mode + :defer t + :hook + ((c-mode-hook c++-mode-hook) . auto-fill-mode) + :general + (:keymaps '(c-mode-map c++-mode-map) + :states '(normal motion visual) + "(" #'c-beginning-of-statement + ")" #'c-end-of-statement + "{" #'c-beginning-of-defun + "}" #'c-end-of-defun) + :init + (setq c-basic-offset 2 + c-auto-newline nil + c-default-style '((other . "user"))) + (add-hook 'c-mode-hook (proc (c-toggle-comment-style -1))) + (add-hook 'c++-mode-hook (proc (c-toggle-comment-style -1))) + (defun +cc/copyright-notice () + (let* ((lines (split-string (+license/copyright-notice) "\n")) + (copyright-line (car lines)) + (rest (cdr lines))) + (concat + "* " + copyright-line + "\n" + (mapconcat + #'(lambda (x) + (if (string= x "") + "" + (concat " * " x))) + rest + "\n")))) + :auto-insert + (("\\.c\\'" . "C skeleton") + "" + "/" (+cc/copyright-notice) "\n\n" + " * Created: " (format-time-string "%Y-%m-%d") "\n" + " * Description: " _ "\n" + " */\n" + "\n") + (("\\.cpp\\'" "C++ skeleton") + "" + "/" (+cc/copyright-notice) "\n\n" + " * Created: " (format-time-string "%Y-%m-%d") "\n" + " * Description: " _ "\n" + " */\n" + "\n") + (("\\.\\([Hh]\\|hh\\|hpp\\|hxx\\|h\\+\\+\\)\\'" . "C / C++ header") + (replace-regexp-in-string "[^A-Z0-9]" "_" + (string-replace "+" "P" + (upcase + (file-name-nondirectory buffer-file-name)))) + "/" (+cc/copyright-notice) "\n\n" + " * Created: " (format-time-string "%Y-%m-%d") "\n" + " * Description: " _ "\n" + " */\n\n" + "#ifndef " str n "#define " str "\n\n" "\n\n#endif") + :config + (c-add-style + "user" + '((c-basic-offset . 2) + (c-comment-only-line-offset . 0) + (c-hanging-braces-alist (brace-list-open) + (brace-entry-open) + (substatement-open after) + (block-close . c-snug-do-while) + (arglist-cont-nonempty)) + (c-cleanup-list brace-else-brace) + (c-offsets-alist + (statement-block-intro . +) + (substatement-open . 0) + (access-label . -) + (inline-open . 0) + (label . 0) + (statement-cont . +))))) +#+end_src +*** Clang format +clang-format is a program that formats C/C++ files. It's highly +configurable and quite fast. I have a root configuration in my +Dotfiles (check it out +[[file:~/Dotfiles/ClangFormat/).clang-format][here]]. + +Clang format comes inbuilt with clang/LLVM, so it's quite likely to be +on your machine. + +#+begin_src emacs-lisp +(use-package clang-format + :load-path "/usr/share/clang/" + :defer t + :after cc-mode + :commands (+code/clang-format-region-or-buffer + clang-format-mode) + :general + (code-leader + :keymaps '(c-mode-map c++-mode-map) + "f" #'clang-format-buffer) + :config + (define-minor-mode clang-format-mode + "On save formats the current buffer via clang-format." + :lighter nil + (let ((save-func (proc-int + (clang-format-buffer)))) + (if clang-format-mode + (add-hook 'before-save-hook save-func nil t) + (remove-hook 'before-save-hook save-func t)))) + (defun +code/clang-format-region-or-buffer () + (interactive) + (if (mark) + (clang-format-region (region-beginning) (region-end)) + (clang-format-buffer)))) +#+end_src +*** cc org babel +To ensure org-babel executes language blocks of C/C++, I need to load +it as an option in ~org-babel-load-languages~. + +#+begin_src emacs-lisp +(use-package org + :after cc-mode + :init + (org-babel-do-load-languages + 'org-babel-load-languages + '((C . t)))) +#+end_src +*** cc compile fsan +Sanitisers are a blessing for C/C++. An additional runtime on top of +the executable which catches stuff like undefined behaviour or memory +leaks make it super easy to see where and how code is failing. +However, by default, Emacs' compilation-mode doesn't understand the +logs =fsanitize= makes so you usually have to manually deal with it +yourself. + +Compilation mode uses regular expressions to figure out whether +something is an error and how to navigate to the file where that error +is located. So adding support for =-fsanitize= is as simple as making +a regular expression which captures file names and digits + +#+begin_src emacs-lisp +(use-package compile + :after cc-mode + :config + (add-to-list 'compilation-error-regexp-alist-alist + `(fsan ,(rx (and + line-start " #" digit " 0x" (1+ hex) " in " + (1+ (or word "_")) " " + (group (seq (* any) (or ".c" ".cpp" ".h" ".hpp"))) ":" + (group (+ digit)))) + + 1 2)) + (add-to-list 'compilation-error-regexp-alist + 'fsan)) +#+end_src +** Markdown +Why use Markdown when you have org-mode? Because LSP servers +sometimes format their documentation as markdown, which +[[*Eglot][Eglot]] can use to provide nicer views on docs! +#+begin_src emacs-lisp +(use-package markdown-mode + :defer t + :straight t) +#+end_src +** WAIT Rust +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +2025-02-15: Haven't needed to use Rust at all recently - but leaving +this here in case. + +Rust is the systems programming language that also does web stuff and +CLI programs and basically tries to be a jack of all trades. It's got +some interesting stuff but most importantly it's very new, so everyone +must learn it, right? + +#+begin_src emacs-lisp +(use-package rust-mode + :straight t + :defer t + :general + (code-leader + :keymaps 'rust-mode-map + "f" #'rust-format-buffer) + (local-leader + :keymaps 'rust-mode-map + "c" #'rust-run-clippy) + :init + (setq rust-format-on-save t) + (with-eval-after-load "eglot" + (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer")))) +#+end_src +** WAIT Racket +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +A scheme with lots of stuff inside it. Using it for a language design +book so it's useful to have some Emacs binds for it. + +#+begin_src emacs-lisp +(use-package racket-mode + :straight t + :defer t + :hook (racket-mode-hook . racket-xp-mode) + :display + ("\\*Racket REPL*" + (display-buffer-at-bottom) + (window-height . 0.3)) + :init + (setq racket-documentation-search-location 'local) + :general + (nmap + :keymaps 'racket-describe-mode-map + "q" #'quit-window) + (nmap + :keymaps 'racket-mode-map + "gr" #'racket-eval-last-sexp) + (local-leader + :keymaps '(racket-mode-map racket-repl-mode-map) + "d" #'racket-repl-describe) + (local-leader + :keymaps 'racket-mode-map + "r" #'racket-run + "i" #'racket-repl + "e" #'racket-send-definition + "sr" #'racket-send-region + "sd" #'racket-send-definition)) +#+end_src +** WAIT Haskell +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +2025-02-15: Haskell is a fun language so I'll leave this configuration +for now. + +Haskell is a static lazy functional programming language (what a +mouthful). It's quite a beautiful language and really learning it +will change the way you think about programming. However, my +preferred functional language is still unfortunately Lisp so no extra +brownie points there. + +Here I configure the REPL for Haskell via the +~haskell-interactive-mode~. I also load my custom package +[[file:elisp/haskell-multiedit.el][haskell-multiedit]] which allows a +user to create temporary ~haskell-mode~ buffers that, upon completion, +will run in the REPL. Even easier than making your own buffer. + +#+begin_src emacs-lisp +(use-package haskell-mode + :straight t + :defer t + :hook + (haskell-mode-hook . haskell-indentation-mode) + (haskell-mode-hook . interactive-haskell-mode) + :display + ("\\*haskell.**\\*" + (display-buffer-at-bottom) + (window-height . 0.3)) + :general + (shell-leader + "h" #'haskell-interactive-bring) + (local-leader + :keymaps 'haskell-mode-map + "c" #'haskell-compile + "t" #'haskell-process-do-type) + (nmmap + :keymaps 'haskell-mode-map + "C-c C-c" #'haskell-process-load-file) + (local-leader + :keymaps 'haskell-interactive-mode-map + "c" #'haskell-interactive-mode-clear) + (imap + :keymaps 'haskell-interactive-mode-map + "M-k" #'haskell-interactive-mode-history-previous + "M-j" #'haskell-interactive-mode-history-next) + :init + (setq haskell-interactive-prompt "[λ] " + haskell-interactive-prompt-cont "{λ} " + haskell-interactive-popup-errors nil + haskell-stylish-on-save t + haskell-process-type 'auto) + :config + (load (concat user-emacs-directory "elisp/haskell-multiedit.el"))) +#+end_src +** Python +Works well for python. If you have ~pyls~ it should be on your path, so +just run eglot if you need. But an LSP server is not necessary for a +lot of my time in python. Here I also setup org-babel for python +source code blocks. + +#+begin_src emacs-lisp +(use-package python + :defer t + :general + (nmmap + :keymaps 'python-mode-map + "C-M-x" #'python-shell-send-defun) + (local-leader + :keymaps 'python-mode-map + "c" #'python-check) + (local-leader + :keymaps 'python-mode-map + :infix "e" + "e" #'python-shell-send-statement + "r" #'python-shell-send-region + "f" #'python-shell-send-buffer) + :pretty + (python-mode-hook + ("None" . "Ø") + ("list" . "ℓ") + ("List" . "ℓ") + ("str" . "𝕊") + ("!" . "¬") + ("for" . "∀") + ("print" . "φ") + ("lambda" . "λ") + ("reduce" . "↓") + ("map" . "→") + ("return" . "≡") + ("yield" . "≈")) + :init + (setq python-indent-offset 4) + :config + (with-eval-after-load "org-mode" + (setf (alist-get 'python org-babel-load-languages) t))) +#+end_src +*** Python shell +Setup for python shell, including a toggle option + +#+begin_src emacs-lisp +(use-package python + :defer t + :commands +python/toggle-repl + :general + (shell-leader + "p" #'run-python) + :hook + (inferior-python-mode-hook . company-mode) + :display + ("\\*Python\\*" + (display-buffer-at-bottom) + (window-height . 0.3))) +#+end_src +** YAML +YAML is a data language which is useful for config files. + +#+begin_src emacs-lisp +(use-package yaml-mode + :defer t + :straight t) +#+end_src +** HTML/CSS/JS +Firstly, web mode for consistent colouring of syntax. + +#+begin_src emacs-lisp +(use-package web-mode + :straight t + :defer t + :mode ("\\.html" . web-mode) + :mode ("\\.css" . web-mode) + :custom + ((web-mode-code-indent-offset 2) + (web-mode-markup-indent-offset 2) + (web-mode-css-indent-offset 2))) +#+end_src +*** Emmet +Emmet for super speed code writing. + +#+begin_src emacs-lisp +(use-package emmet-mode + :straight t + :defer t + :hook (web-mode-hook . emmet-mode) + :general + (imap + :keymaps 'emmet-mode-keymap + "TAB" #'emmet-expand-line + "M-j" #'emmet-next-edit-point + "M-k" #'emmet-prev-edit-point)) +#+end_src +*** HTML Auto insert +An auto-insert for HTML buffers, which just adds some nice stuff. + +#+begin_src emacs-lisp +(use-package web-mode + :defer t + :auto-insert + (("\\.html\\'" . "HTML Skeleton") + "" + "<!doctype html> +<html lang=''> + <head> + <meta charset='utf-8'> + <title>"(read-string "Enter title: ") | """</title> + <meta name='description' content='" (read-string "Enter description: ") | "" "'> + <meta name='author' content='"user-full-name"'/> + <meta name='viewport' content='width=device-width, initial-scale=1'> + + <link rel='apple-touch-icon' href='/apple-touch-icon.png'> + <link rel='shortcut icon' href='/favicon.ico'/> + </head> + <body> +" + _ + " </body> +</html>")) +#+end_src +*** Javascript Mode +A better mode for JavaScript that also has automatic integration with +eglot. + +#+begin_src emacs-lisp +(use-package js + :defer t + :mode ("\\.js" . js-mode) + :hook (js-mode-hook . auto-fill-mode) + :init + (setq js-indent-level 2)) +#+end_src +*** Typescript +A language that adds a build step to JavaScript projects for "static" +typing. It's nice because it adds good auto completion. + +#+begin_src emacs-lisp +(use-package typescript-mode + :straight t + :defer t + :init + (setq typescript-indent-level 2)) +#+end_src +** Scheme +Another Lisp but simpler than the rest. A beauty of engineering and +fun to write programs in. Here I setup ~geiser~, which is the +premiere way to interact with scheme REPLs. + +#+begin_src emacs-lisp +(use-package geiser + :defer t + :straight t + :display + ("\\*Geiser.*" + (display-buffer-reuse-mode-window display-buffer-at-bottom) + (window-height . 0.3)) + :general + (shell-leader + "S" #'geiser) + (local-leader + :keymaps 'scheme-mode-map + "t" #'geiser + "m" #'geiser-doc-look-up-manual + "d" #'geiser-doc-symbol-at-point) + (local-leader + :keymaps 'scheme-mode-map + :infix "e" + "e" #'geiser-eval-last-sexp + "b" #'geiser-eval-buffer + "d" #'geiser-eval-definition + "r" #'geiser-eval-region) + :init + (with-eval-after-load "evil" + (evil-set-initial-state 'geiser-debug-mode-map 'emacs))) + +(use-package geiser-guile + :defer t + :straight t) +#+end_src +** WAIT Ocaml +:PROPERTIES: +:header-args:emacs-lisp: :tangle no :results none +:END: +*** Ocaml Setup +Firstly, install ~opam~ and ~ocaml~. Then run the following script: +#+begin_src sh +opam install tuareg ocamlformat odoc utop merlin user-setup; +opam user-setup install; +mv ~/.emacs.d/opam-user-setup.el ~/.config/emacs/elisp; +rm -rf ~/.emacs.d ~/.emacs; +#+end_src + +This sets up the necessary packages (particularly Emacs Lisp) and some +configuration that ensures Emacs is consistent with the user +installation. Notice the moving of =opam-user-setup.el= into +=~/.config/emacs/elisp=, which we'll use to setup the ocaml +experience. +*** Ocaml Configuration +Here I load the =opam-user-setup= package setup earlier, with some +neat tips from the default =~/.emacs= generated by ~opam user-setup +install~. +#+begin_src emacs-lisp +(use-package opam-user-setup + :defer t + :load-path "elisp/" + :mode ("\\.ml" . tuareg-mode) + :hook (tuareg-mode-hook . whitespace-mode) + :display + ("\\*utop\\*" + (display-buffer-at-bottom) + (window-height . 0.3)) + :general + (code-leader + :keymaps 'tuareg-mode-map + "f" #'+ocaml/format-buffer) + :config + (defun +ocaml/format-buffer () + (interactive) + (when (eq major-mode 'tuareg-mode) + (let ((name (buffer-file-name (current-buffer))) + (format-str "ocamlformat -i --enable-outside-detected-project %s")) + (save-buffer) + (set-process-sentinel (start-process-shell-command "ocamlformat" "*ocamlformat*" + (format format-str name)) + (lambda (p event) + (when (string= event "finished\n") + (revert-buffer nil t) + (message "[ocamlformat] Finished."))))))) + (add-to-list 'compilation-error-regexp-alist-alist + `(ocaml + "[Ff]ile \\(\"\\(.*?\\)\", line \\(-?[0-9]+\\)\\(, characters \\(-?[0-9]+\\)-\\([0-9]+\\)\\)?\\)\\(:\n\\(\\(Warning .*?\\)\\|\\(Error\\)\\):\\)?" + 2 3 (5 . 6) (9 . 11) 1 (8 compilation-message-face))) + (add-to-list 'compilation-error-regexp-alist + 'ocaml) + :general + (local-leader + :keymaps 'tuareg-mode-map + "u" #'utop) + (local-leader + :keymaps 'tuareg-mode-map + :infix "e" + "r" #'utop-eval-region + "e" #'utop-eval-phrase + "b" #'utop-eval-buffer)) + +(use-package merlin-eldoc + :straight t + :after opam-user-setup + :hook + (tuareg-mode-hook . merlin-eldoc-setup) + :init + (setq merlin-eldoc-occurrences nil)) +#+end_src +** Lisp +Emacs is the greatest Lisp editor around, there are no two ways about +it. Here I setup the configuration for Emacs Lisp and Common Lisp. +*** Lisp configuration +All the general stuff I do for any other language: pretty symbols and +key bindings. +#+begin_src emacs-lisp +(use-package lisp-mode + :pretty + (lisp-mode-hook + ("lambda" . "λ") + ("nil" . "Ø") + ("<=" . "≤") + (">=" . "≥") + ("defun" . "ƒ") + ("mapcar" . "→") + ("reduce" . "↓") + ("some" . "∃") + ("every" . "∀") + ("LAMBDA" . "λ") + ("NIL" . "Ø") + ("<=" . "≤") + (">=" . "≥") + ("DEFUN" . "ƒ") + ("MAPCAR" . "→") + ("REDUCE" . "↓") + ("SOME" . "∃") + ("EVERY" . "∀")) + (emacs-lisp-mode-hook + ("lambda" . "λ") + ("nil" . "Ø") + ("defun" . "ƒ") + ("mapcar" . "→") + ("LAMBDA" . "λ") + ("NIL" . "Ø") + ("DEFUN" . "ƒ") + ("MAPCAR" . "→")) + :general + (:states '(normal motion insert visual) + :keymaps 'lisp-mode-shared-map + "C-j" #'sp-forward-slurp-sexp + "C-k" #'sp-forward-barf-sexp + "C-S-j" #'sp-backward-barf-sexp + "C-S-k" #'sp-backward-slurp-sexp + "M-h" #'sp-previous-sexp + "M-j" #'sp-down-sexp + "M-k" #'sp-backward-up-sexp + "M-l" #'sp-next-sexp + "M-S-j" #'sp-up-sexp + "M-S-k" #'sp-backward-down-sexp)) +#+end_src +*** Common Lisp auto insert +Like C/C++'s auto insert, but with Common Lisp comments. +#+begin_src emacs-lisp +(use-package lisp-mode + :init + (defun +lisp/copyright-notice () + (let* ((lines (split-string (+license/copyright-notice) "\n")) + (copyright-line (car lines)) + (rest (cdr lines))) + (--> + (lambda (x) + (if (string= x "") + "" + (concat ";; " x))) + (mapconcat it rest "\n") + (format ";; %s\n%s\n" + copyright-line + it)))) + :auto-insert + (("\\.lisp\\'" . "Common Lisp Skeleton") + "" + ";;; " (file-name-nondirectory (buffer-file-name)) " - " + (format-time-string "%Y-%m-%d") "\n\n" + (+lisp/copyright-notice) "\n" + ";;; Commentary:\n\n;;\n\n;;; Code:\n")) +#+end_src +*** Sly +While Emacs does an okay job for editing Common Lisp it's not amazing +for actually developing large scale projects. Considering how good an +environment Emacs is for Emacs Lisp, and how similar the two languages +are, we shouldn't need an LSP. + +Enter /SLY/. Sly is a fork of /SLIME/ and it provides the essential +components to elevate Emacs' ability to develop Common Lisp. I feel +calling the ability Sly gives you "IDE-like" a slight against it - no +IDE I have used is as capable in aiding development as Emacs + Sly. + +#+begin_src emacs-lisp +(use-package sly + :defer t + :straight t + :init + (setq inferior-lisp-program "sbcl" + sly-lisp-loop-body-forms-indentation 0) + :display + ("\\*sly-db" + (display-buffer-at-bottom) + (window-height . 0.25)) + ("\\*sly-inspector" + (display-buffer-at-bottom) + (window-height . 0.25)) + ("\\*sly-mrepl" + (display-buffer-in-side-window) + (window-width . 0.35) + (side . right)) + :config + (evil-set-initial-state 'sly-db-mode 'normal) + (with-eval-after-load "org" + (setq-default org-babel-lisp-eval-fn #'sly-eval)) + (with-eval-after-load "company" + (add-hook 'sly-mrepl-hook #'company-mode)) + :general + (shell-leader + "s" #'sly) + (nmap + :keymaps 'lisp-mode-map + "gr" #'sly-eval-buffer + "gd" #'sly-edit-definition + "gR" #'sly-who-calls) + (local-leader + :keymaps 'lisp-mode-map + "a" #'sly-apropos + "d" #'sly-describe-symbol + "s" #'sly-mrepl-sync + "l" #'sly-load-file + "c" #'sly-compile-defun + "D" #'sly-documentation-lookup + "C" #'sly-compile-file) + (local-leader + :keymaps 'lisp-mode-map + :infix "e" + "b" #'sly-eval-buffer + "e" #'sly-eval-last-expression + "f" #'sly-eval-defun + "r" #'sly-eval-region) + (nmap + :keymaps 'sly-mrepl-mode-map + "M-j" #'sly-mrepl-next-input-or-button + "M-k" #'sly-mrepl-previous-input-or-button + "C-j" #'sly-mrepl-next-prompt + "C-k" #'sly-mrepl-previous-prompt) + (local-leader + :keymaps 'sly-mrepl-mode-map + "c" #'sly-mrepl-clear-repl + "s" #'sly-mrepl-shortcut + "l" #'sly-load-file) + (nmap + :keymaps 'sly-db-mode-map + "C-i" #'sly-db-cycle + "g?" #'describe-mode + "S" #'sly-db-show-frame-source + "e" #'sly-db-eval-in-frame + "d" #'sly-db-pprint-eval-in-frame + "D" #'sly-db-disassemble + "i" #'sly-db-inspect-in-frame + "gj" #'sly-db-down + "gk" #'sly-db-up + "C-j" #'sly-db-down + "C-k" #'sly-db-up + "]]" #'sly-db-details-down + "[[" #'sly-db-details-up + "M-j" #'sly-db-details-down + "M-k" #'sly-db-details-up + "G" #'sly-db-end-of-backtrace + "t" #'sly-db-toggle-details + "gr" #'sly-db-restart-frame + "I" #'sly-db-invoke-restart-by-name + "R" #'sly-db-return-from-frame + "c" #'sly-db-continue + "s" #'sly-db-step + "n" #'sly-db-next + "o" #'sly-db-out + "b" #'sly-db-break-on-return + "a" #'sly-db-abort + "q" #'sly-db-quit + "A" #'sly-db-break-with-system-debugger + "B" #'sly-db-break-with-default-debugger + "P" #'sly-db-print-condition + "C" #'sly-db-inspect-condition + "g:" #'sly-interactive-eval + "0" #'sly-db-invoke-restart-0 + "1" #'sly-db-invoke-restart-1 + "2" #'sly-db-invoke-restart-2 + "3" #'sly-db-invoke-restart-3 + "4" #'sly-db-invoke-restart-4 + "5" #'sly-db-invoke-restart-5 + "6" #'sly-db-invoke-restart-6 + "7" #'sly-db-invoke-restart-7 + "8" #'sly-db-invoke-restart-8 + "9" #'sly-db-invoke-restart-9) + (nmap + :keymaps 'sly-inspector-mode-map + "q" #'sly-inspector-quit)) +#+end_src +*** Lisp indent function +Add a new lisp indent function which indents newline lists more +appropriately. + +#+begin_src emacs-lisp +(use-package lisp-mode + :defer t + :config + (defun +oreo/lisp-indent-function (indent-point state) + (let ((normal-indent (current-column)) + (orig-point (point))) + (goto-char (1+ (elt state 1))) + (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) + (cond + ;; car of form doesn't seem to be a symbol, or is a keyword + ((and (elt state 2) + (or (not (looking-at "\\sw\\|\\s_")) + (looking-at ":"))) + (if (not (> (save-excursion (forward-line 1) (point)) + calculate-lisp-indent-last-sexp)) + (progn (goto-char calculate-lisp-indent-last-sexp) + (beginning-of-line) + (parse-partial-sexp (point) + calculate-lisp-indent-last-sexp 0 t))) + ;; Indent under the list or under the first sexp on the same + ;; line as calculate-lisp-indent-last-sexp. Note that first + ;; thing on that line has to be complete sexp since we are + ;; inside the innermost containing sexp. + (backward-prefix-chars) + (current-column)) + ((and (save-excursion + (goto-char indent-point) + (skip-syntax-forward " ") + (not (looking-at ":"))) + (save-excursion + (goto-char orig-point) + (looking-at ":"))) + (save-excursion + (goto-char (+ 2 (elt state 1))) + (current-column))) + (t + (let ((function (buffer-substring (point) + (progn (forward-sexp 1) (point)))) + method) + (setq method (or (function-get (intern-soft function) + 'lisp-indent-function) + (get (intern-soft function) 'lisp-indent-hook))) + (cond ((or (eq method 'defun) + (and (null method) + (> (length function) 3) + (string-match "\\`def" function))) + (lisp-indent-defform state indent-point)) + ((integerp method) + (lisp-indent-specform method state + indent-point normal-indent)) + (method + (funcall method indent-point state)))))))) + (setq-default lisp-indent-function #'+oreo/lisp-indent-function)) +#+end_src * Miscellaneous ** Evil additions Additional packages that add the functionality of plugins in Vim I @@ -4055,24 +4119,12 @@ A port of vim-commentary, providing generalised commenting of objects. :config (evil-commentary-mode)) #+end_src -*** Evil multi cursor -Setup for multi cursors in Evil mode, which is a bit of very nice -functionality. 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 - :straight t - :init - (setq evil-mc-cursors-keymap-prefix "gz") - :config - (global-evil-mc-mode)) -#+end_src *** Evil multi edit Evil-ME provides a simpler parallel editing experience within the same -buffer. I use it in-tandem with Evil-MC, where I use Evil-ME for -textual changes and Evil-MC for more complex motions. +buffer. I now use it exclusively over evil multi-cursor: this is +designed better, its less buggy, and doesn't try to over complicate +things. There are many things it can't do, but normal Vim motions +can. It's useful for trialing #+begin_src emacs-lisp (use-package evil-multiedit @@ -4097,8 +4149,8 @@ I may disagree with some. So I use it in a mode to mode basis. :hook (after-init-hook . evil-collection-init) :straight t :init - (setq evil-collection-mode-list - '(flycheck eww magit calendar notmuch ibuffer proced calc))) + (setq evil-collection-mode-list '(eww flycheck magit calendar notmuch + ibuffer proced calc image))) #+end_src *** Evil number Increment/decrement a number at point like Vim does, but use bindings @@ -4161,14 +4213,14 @@ effectively. "R" #'tab-rename "c" #'tab-close "d" #'tab-close - "f" #'tab-detach "h" #'tab-move-to "j" #'tab-next "k" #'tab-previous "l" #'tab-move "n" #'tab-new "r" #'tab-switch - "w" #'tab-window-detach)) + "w" #'tab-window-detach + "b" (proc-int (switch-to-buffer-other-tab (current-buffer))))) #+end_src ** Registers Registers are essentially an alist of symbols mapped to some Lisp @@ -4212,6 +4264,21 @@ but I prefer Emacs' hence the configuration here. "m" #'point-to-register "'" #'jump-to-register)) #+end_src +** Bookmarks +Bookmarks are like persistent registers. Like registers, they can +kinda work anywhere in Emacs: from remote files via =tramp= to +webpages with [[*EWW][EWW]]. Since they're persistent, they'll live +regardless of the current Emacs session - and because they're like +registers, they'll remember the exact context (position in buffer, +time since last updated, etc). +#+begin_src emacs-lisp +(use-package bookmark + :general + (leader "x" bookmark-map) + :init + (setq bookmark-watch-bookmark-file t + bookmark-save-flag 1)) +#+end_src ** Recentf Recentf provides a method of keeping track of recently opened files. |