aboutsummaryrefslogtreecommitdiff
path: root/Emacs/.config/emacs/config.org
diff options
context:
space:
mode:
Diffstat (limited to 'Emacs/.config/emacs/config.org')
-rw-r--r--Emacs/.config/emacs/config.org2143
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.