1668 lines
54 KiB
Org Mode
1668 lines
54 KiB
Org Mode
#+title: Emacs configuration
|
|
#+author: Aryadev Chavali
|
|
#+description: My Emacs configuration
|
|
#+property: header-args:emacs-lisp :tangle config.el :comments link :results none
|
|
#+startup: noindent
|
|
#+options: toc:t num:t
|
|
#+latex_header:\usepackage[margin=1.0in]{geometry}
|
|
#+latex_class: article
|
|
#+latex_class_options: [a4paper,12pt]
|
|
|
|
* Introduction
|
|
Welcome to my Emacs configuration. This thing is quite big, but a lot
|
|
of it has been "write and forget" i.e. I've only needed to configure
|
|
it once. Sections tagged =WAIT= are currently unused, usually with
|
|
some reasoning given.
|
|
|
|
Some sections border on blog posts justifying why I think they're good
|
|
applications or giving some greater reasoning about my specific
|
|
configuration of a package. If you don't really want that, you may
|
|
tangle this file, [[file:core.org][the core file]] and
|
|
[[file:app.org][the app file]] and just read their source code.
|
|
* Basics
|
|
Firstly, set full name and mail address. This is used in encryption
|
|
and mailing.
|
|
#+begin_src emacs-lisp
|
|
(setq user-full-name "Aryadev Chavali"
|
|
user-mail-address "aryadev@aryadevchavali.com")
|
|
#+end_src
|
|
|
|
Let's set all yes or no questions to single letter responses.
|
|
#+begin_src emacs-lisp
|
|
(fset 'yes-or-no-p 'y-or-n-p)
|
|
#+end_src
|
|
|
|
Set the encoding to UTF-8-Unix by default.
|
|
#+begin_src emacs-lisp
|
|
(use-package emacs
|
|
:straight nil
|
|
:init
|
|
(setq-default buffer-file-coding-system 'utf-8-unix
|
|
save-buffer-coding-system 'utf-8-unix))
|
|
#+end_src
|
|
** File saves and custom file
|
|
Setup automatic saving for files (in case of system failure) and
|
|
auto-revert-mode (which refreshes the buffer on changes to the
|
|
underlying file). Along with that, set the custom-file (which holds
|
|
temporary customisation) in the etc folder.
|
|
#+begin_src emacs-lisp
|
|
(use-package emacs
|
|
:straight nil
|
|
:init
|
|
(setq backup-directory-alist `(("." . ,(no-littering-expand-var-file-name "saves/")))
|
|
global-auto-revert-non-file-buffers t
|
|
auto-revert-verbose nil)
|
|
:config
|
|
(global-auto-revert-mode 1))
|
|
#+end_src
|
|
* Custom functionality
|
|
Functions that don't require a packages to work other than Emacs,
|
|
which means I can define them early. These are used much later in the
|
|
config.
|
|
** WAIT Toggle buffer
|
|
:PROPERTIES:
|
|
:header-args:emacs-lisp: :tangle no
|
|
:END:
|
|
Like VSCode's toggling feature for just the terminal but now for
|
|
any buffer of choice, as long as I can generate it via a command.
|
|
|
|
2024-04-23: Don't need this anymore due to
|
|
~switch-to-buffer-obey-display-actions~.
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load "window"
|
|
(defmacro +oreo/create-toggle-function (func-name buf-name
|
|
buf-create
|
|
&optional accept-numeric)
|
|
"Generate a function named FUNC-NAME that toggles the buffer with
|
|
name BUF-NAME, using BUF-CREATE to generate it if buffer BUF-NAME
|
|
does not exist already.
|
|
|
|
BUF-NAME cannot be a regexp, it must be a fixed name.
|
|
|
|
ACCEPT-NUMERIC modifies the function to allow numeric arguments
|
|
via C-u. Mostly used in Eshell."
|
|
(let ((interactive-arg
|
|
(if accept-numeric '(interactive "p") '(interactive)))
|
|
(arguments
|
|
(if accept-numeric '(&optional arg) nil))
|
|
(buffer-name (if accept-numeric
|
|
`(if (= arg 1)
|
|
,buf-name
|
|
(concat ,buf-name "<" (int-to-string arg) ">"))
|
|
buf-name))
|
|
(buffer-create (if accept-numeric
|
|
`(if (= arg 1)
|
|
(,buf-create)
|
|
(,buf-create arg))
|
|
`(,buf-create))))
|
|
`(defun ,func-name ,arguments
|
|
,interactive-arg
|
|
(let* ((buffer (or (get-buffer ,buffer-name)
|
|
,buffer-create))
|
|
(displayed (get-buffer-window buffer)))
|
|
(if displayed
|
|
(delete-window displayed)
|
|
(display-buffer buffer)
|
|
(select-window (get-buffer-window buffer))))))))
|
|
#+end_src
|
|
** Auto-run command after-save-hook
|
|
Define a macro which creates hooks into the ~after-save-hook~. On
|
|
certain ~conditions~ being met, ~to-run~ is evaluated.
|
|
#+begin_src emacs-lisp
|
|
(use-package simple
|
|
:straight nil
|
|
:config
|
|
(defmacro +oreo/create-auto-save (conditions &rest to-run)
|
|
"Create a hook for after saves, where (on CONDITIONS being met)
|
|
TO-RUN is evaluated. "
|
|
`(add-hook 'after-save-hook #'(lambda ()
|
|
(interactive)
|
|
(when ,conditions
|
|
,@to-run)))))
|
|
#+end_src
|
|
** Procedure
|
|
A ~lambda~ which takes no arguments is a procedure. This macro
|
|
generates procedures, with the parameters of the macro being the body
|
|
of the procedure. It returns it in quoted form, as that is the most
|
|
common use of this macro.
|
|
|
|
(You may notice ~proc~ is used where the return value is irrelevant).
|
|
#+begin_src emacs-lisp
|
|
(defmacro proc (&rest BODY)
|
|
"For a given list of forms BODY, return a quoted 0 argument
|
|
lambda."
|
|
`(quote (lambda nil ,@BODY)))
|
|
#+end_src
|
|
** System specificity
|
|
A macro that acts as a switch case on ~(system-name)~ which allows the
|
|
writing of system specific code. For me this is for my desktop and
|
|
laptop, particularly for font sizes. Though there may be an easier
|
|
solution than this, this seems simple enough.
|
|
#+begin_src emacs-lisp
|
|
(defmacro +oreo/sys-name-cond (&rest pairs)
|
|
"Switch case on result of function `system-name'.
|
|
|
|
Each pair in PAIRS is typed as: (string . (forms...)) where the
|
|
string represents the system name to test, and forms being the
|
|
consequence if true."
|
|
`(cond
|
|
,@(mapcar #'(lambda (pair)
|
|
;; (str . forms..) -> ((string= str (system-name))
|
|
;; forms...)
|
|
(let ((name (car pair))
|
|
(body (cdr pair)))
|
|
`((string= ,name (system-name)) ,@body)))
|
|
pairs)))
|
|
#+end_src
|
|
|
|
In [[file:early-init.el][early-init.el]] I set the number of
|
|
native-workers to 4, which isn't necessarily optimal when
|
|
loading/compiling the rest of this file depending on the machine I
|
|
use:
|
|
- On my laptop (=spiderboy=) I'd prefer to have it use 2-3 threads so
|
|
I can actually use the rest of the laptop while waiting for
|
|
compilation
|
|
- On my desktop (=oldboy=) I'd prefer to use 4-6 threads as I can
|
|
afford more, so I can get a faster load up.
|
|
#+begin_src emacs-lisp
|
|
(+oreo/sys-name-cond
|
|
("spiderboy"
|
|
(setq native-comp-async-jobs-number 3))
|
|
("oldboy"
|
|
(setq native-comp-async-jobs-number 6)))
|
|
#+end_src
|
|
** Clean buffer list
|
|
Instead of cleaning my buffer list manually, selectively preserving
|
|
some fixed set of buffers, this function does it for me. Preserves
|
|
any buffers in ~+oreo/keep-buffer~ and kills the rest.
|
|
#+begin_src emacs-lisp
|
|
(defconst +oreo/keep-buffers
|
|
(list "config.org" "*scratch*"
|
|
"*dashboard*" "*Messages*"
|
|
"*Warnings*" "*eshell*")
|
|
"List of buffer names to preserve.")
|
|
|
|
(defun +oreo/clean-buffer-list ()
|
|
"Kill all buffers except any with names in +oreo/keep-buffers."
|
|
(interactive)
|
|
(mapcar #'(lambda (buf)
|
|
(if (not (member (buffer-name buf) +oreo/keep-buffers))
|
|
(kill-buffer buf)))
|
|
(buffer-list)))
|
|
#+end_src
|
|
* Aesthetics
|
|
General look and feel of Emacs (mostly disabling stuff I don't like).
|
|
** Themes
|
|
*** Dark theme
|
|
My preferred dark theme is my own "personal-primary" theme which is
|
|
stored in the Emacs lisp folder (look at
|
|
[[file:elisp/personal-primary-theme.el][this file]]). It tries to use
|
|
the primary colours for everything, leading to a colour -> meaning
|
|
relation.
|
|
|
|
I have an older version of this theme that uses a homogeneous colour
|
|
scheme ([[file:elisp/personal-theme.el][this file]])
|
|
#+begin_src emacs-lisp
|
|
(use-package custom
|
|
:demand t
|
|
:straight nil
|
|
:init
|
|
(setq custom-theme-directory (concat user-emacs-directory "elisp/"))
|
|
:config
|
|
(load-theme 'personal-primary t))
|
|
#+end_src
|
|
*** Light theme
|
|
I'm not very good at designing light themes as I don't really use
|
|
them. However they are necessary in high light situations where a
|
|
dark mode would strain the eyes too much. So I built a custom theme
|
|
on top of the default Emacs theme, "personal-light" (look at
|
|
[[file:elisp/personal-light-theme.el][this file]]).
|
|
|
|
I don't use it by default but I may need to switch between light and
|
|
dark easily, so here's a command to switch between them.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package custom
|
|
:defer t
|
|
:straight nil
|
|
:commands +oreo/switch-theme
|
|
:init
|
|
(defvar +oreo/theme 'dark)
|
|
:config
|
|
(defun +oreo/switch-theme ()
|
|
(interactive)
|
|
(cond
|
|
((eq +oreo/theme 'dark)
|
|
(load-theme 'personal-light t)
|
|
(setq +oreo/theme 'light))
|
|
((eq +oreo/theme 'light)
|
|
(load-theme 'personal-primary t)
|
|
(setq +oreo/theme 'dark)))))
|
|
#+end_src
|
|
** Font size
|
|
Set font size to 140 if on my desktop (oldboy) or 175 if on my laptop
|
|
(spiderboy).
|
|
#+begin_src emacs-lisp
|
|
(use-package faces
|
|
:straight nil
|
|
:config
|
|
(+oreo/sys-name-cond
|
|
("spiderboy" (set-face-attribute 'default nil :height 145))
|
|
("oldboy" (set-face-attribute 'default nil :height 140))))
|
|
#+end_src
|
|
** Startup screen
|
|
The default startup screen is quite bad in all honesty, great for
|
|
first time users who have no idea what is going on but terrible for
|
|
regular users.
|
|
|
|
The scratch buffer is an interaction buffer made when Emacs is first
|
|
started, as a way to quickly prototype Emacs Lisp code. When startup
|
|
screen is disabled, this buffer is the first thing presented on boot
|
|
for Emacs. So we can use it to store some useful information.
|
|
|
|
As I use [[*Org mode][org-mode]] to compile my Emacs, it is available
|
|
essentially at startup, so I use it for the scratch buffer. That way,
|
|
I can use all the abilities of org-mode (particularly writing a system
|
|
of code using =#+RESULTS=) in an ephemeral buffer at startup!
|
|
#+begin_src emacs-lisp
|
|
(use-package emacs
|
|
:straight nil
|
|
:init
|
|
(setq
|
|
inhibit-startup-screen t
|
|
initial-major-mode 'org-mode
|
|
initial-scratch-message ""
|
|
ring-bell-function 'ignore)
|
|
(add-hook 'after-init-hook (proc
|
|
(with-current-buffer "*scratch*"
|
|
(goto-char (point-max))
|
|
(insert (format "#+title: Scratch buffer
|
|
,#+author: %s
|
|
,#+description: Emacs v%s
|
|
|
|
Booted in %s
|
|
" user-full-name emacs-version (emacs-init-time)))))))
|
|
|
|
#+end_src
|
|
** Blinking cursor
|
|
Turn off blinking-cursor-mode as [[*Hl-line][hl-line]] is better.
|
|
#+begin_src emacs-lisp
|
|
(use-package frame
|
|
:straight nil
|
|
:config
|
|
(blink-cursor-mode 0))
|
|
#+end_src
|
|
** Fringes
|
|
Turning off borders in my window manager was a good idea, so turn off
|
|
the borders for Emacs.
|
|
#+begin_src emacs-lisp
|
|
(use-package fringe
|
|
:after dashboard
|
|
:straight nil
|
|
:config
|
|
(fringe-mode 0))
|
|
#+end_src
|
|
** Mode line
|
|
A mode line in an editor can provide a LOT of information, or very
|
|
little. I customised the Emacs modeline to give me a bit of info,
|
|
~telephone-line~ to give me a lot.
|
|
|
|
Currently I use the default mode line with some customisation;
|
|
simplicity is above all.
|
|
*** Emacs Mode-line
|
|
#+begin_src emacs-lisp
|
|
(defun +mode-line/generate-padding ()
|
|
(let ((wid (frame-width))
|
|
(str ""))
|
|
(dotimes (n (floor (/ wid 7)))
|
|
(setq str (concat str " ")))
|
|
str))
|
|
|
|
(setq-default
|
|
mode-line-format
|
|
(list
|
|
"%l:%c " ;; Line and column
|
|
"%p[" ;; Where in file + Evil state
|
|
'(:eval (with-eval-after-load "evil"
|
|
(upcase
|
|
(substring
|
|
(format "%s" (if (bound-and-true-p evil-state)
|
|
evil-state
|
|
" "))
|
|
0 1))))
|
|
"] "
|
|
"%+%b("
|
|
'(:eval (format "%s" major-mode))
|
|
") "
|
|
"%I "
|
|
'(:eval (+mode-line/generate-padding))
|
|
'(vc-mode vc-mode)
|
|
mode-line-misc-info
|
|
mode-line-end-spaces))
|
|
#+end_src
|
|
*** WAIT Telephone-line
|
|
:PROPERTIES:
|
|
:header-args:emacs-lisp: :tangle no
|
|
:END:
|
|
Telephone-line is a mode-line package for Emacs which prioritises
|
|
extensibility. It also looks much nicer than the default mode line
|
|
with colouring and a ton of presentations to choose from.
|
|
#+begin_src emacs-lisp
|
|
(use-package telephone-line
|
|
:init
|
|
(defface +telephone/position-face '((t (:foreground "red" :background "grey10"))) "")
|
|
(defface +telephone/mode-face '((t (:foreground "white" :background "dark green"))) "")
|
|
(defface +telephone/file-info-face '((t (:foreground "white" :background "Dark Blue"))) "")
|
|
:custom
|
|
(telephone-line-faces
|
|
'((evil . telephone-line-modal-face)
|
|
(modal . telephone-line-modal-face)
|
|
(ryo . telephone-line-ryo-modal-face)
|
|
(accent . (telephone-line-accent-active . telephone-line-accent-inactive))
|
|
(nil . (mode-line . mode-line-inactive))
|
|
(position . (+telephone/position-face . mode-line-inactive))
|
|
(mode . (+telephone/mode-face . mode-line-inactive))
|
|
(file-info . (+telephone/file-info-face . mode-line-inactive))))
|
|
(telephone-line-primary-left-separator 'telephone-line-halfcos-left)
|
|
(telephone-line-secondary-left-separator 'telephone-line-halfcos-hollow-left)
|
|
(telephone-line-primary-right-separator 'telephone-line-identity-right)
|
|
(telephone-line-secondary-right-separator 'telephone-line-identity-hollow-right)
|
|
(telephone-line-height 24)
|
|
(telephone-line-evil-use-short-tag nil)
|
|
:config
|
|
(telephone-line-defsegment +telephone/buffer-or-filename ()
|
|
(cond
|
|
((buffer-file-name)
|
|
(if (and (fboundp 'projectile-project-name)
|
|
(fboundp 'projectile-project-p)
|
|
(projectile-project-p))
|
|
(list ""
|
|
(funcall (telephone-line-projectile-segment) face)
|
|
(propertize
|
|
(concat "/"
|
|
(file-relative-name (file-truename (buffer-file-name))
|
|
(projectile-project-root)))
|
|
'help-echo (buffer-file-name)))
|
|
(buffer-file-name)))
|
|
(t (buffer-name))))
|
|
|
|
(telephone-line-defsegment +telephone/get-position ()
|
|
`(,(concat "%lL:%cC"
|
|
(if (not mark-active)
|
|
""
|
|
(format " | %dc" (- (+ 1 (region-end)) (region-beginning)))))))
|
|
|
|
(setq-default
|
|
telephone-line-lhs '((mode telephone-line-major-mode-segment)
|
|
(file-info telephone-line-input-info-segment)
|
|
(position +telephone/get-position)
|
|
(accent +telephone/buffer-or-filename
|
|
telephone-line-process-segment))
|
|
telephone-line-rhs '((accent telephone-line-flycheck-segment telephone-line-misc-info-segment
|
|
telephone-line-projectile-segment)
|
|
(file-info telephone-line-filesize-segment)
|
|
(evil telephone-line-evil-tag-segment)))
|
|
(telephone-line-mode))
|
|
#+end_src
|
|
** Mouse
|
|
Who uses a mouse? 🤮
|
|
#+begin_src emacs-lisp
|
|
(setq-default use-file-dialog nil)
|
|
#+end_src
|
|
* Core packages (loading)
|
|
For my core packages, whose configuration doesn't change much anyway,
|
|
I have a [[file:core.org][separate file]]. Here I'll load it up for
|
|
usage later on.
|
|
#+begin_src emacs-lisp
|
|
(load-file (concat user-emacs-directory "core.el"))
|
|
#+end_src
|
|
* Small packages
|
|
** Info
|
|
Info is GNU's attempt at better man pages. Most Emacs packages have
|
|
info pages so I'd like nice navigation options.
|
|
#+begin_src emacs-lisp
|
|
(use-package info
|
|
:straight nil
|
|
:general
|
|
(nmmap
|
|
:keymaps 'Info-mode-map
|
|
"h" #'evil-backward-char
|
|
"k" #'evil-previous-line
|
|
"l" #'evil-forward-char
|
|
"H" #'Info-history-back
|
|
"L" #'Info-history-forward
|
|
"RET" #'Info-follow-nearest-node))
|
|
#+end_src
|
|
** Display line numbers
|
|
I don't really like line numbers, I find them similar to [[*Fringes][fringes]] as
|
|
useless space, but at least it provides some information. Sometimes
|
|
it can help with doing repeated commands so a toggle option is
|
|
necessary.
|
|
#+begin_src emacs-lisp
|
|
(use-package display-line-numbers
|
|
:straight nil
|
|
:commands display-line-numbers-mode
|
|
:general
|
|
(mode-leader
|
|
"l" #'display-line-numbers-mode)
|
|
:init
|
|
(setq-default display-line-numbers-type 'relative))
|
|
#+end_src
|
|
** WAIT esup
|
|
:PROPERTIES:
|
|
:header-args:emacs-lisp: :tangle no
|
|
:END:
|
|
I used to be able to just use
|
|
[[file:elisp/profiler-dotemacs.el][profile-dotemacs.el]], when my
|
|
Emacs config was smaller, but now it tells me very little information
|
|
about where my setup is inefficient due to the literate config. Just
|
|
found this ~esup~ thing and it works perfectly, exactly how I would
|
|
prefer getting this kind of information. It runs an external Emacs
|
|
instance and collects information from it, so it doesn't require
|
|
restarting Emacs to profile, and I can compile my configuration in my
|
|
current instance to test it immediately.
|
|
|
|
2023-10-16: Unless I'm doing some optimisations or tests, I don't
|
|
really need this in my config at all times. Enable when needed.
|
|
#+begin_src emacs-lisp
|
|
(use-package esup
|
|
:defer t)
|
|
#+end_src
|
|
** xref
|
|
Find definitions, references and general objects using tags without
|
|
external packages. Provided by default in Emacs and just requires a
|
|
way of generating a =TAGS= file for your project. Helps with minimal
|
|
setups for programming without heavier packages like [[*Eglot][Eglot]].
|
|
|
|
[[*Projectile][Projectile]] provides a nice way to generate tags.
|
|
#+begin_src emacs-lisp
|
|
(use-package xref
|
|
:straight nil
|
|
:display
|
|
("\\*xref\\*"
|
|
(display-buffer-at-bottom)
|
|
(inhibit-duplicate-buffer . t)
|
|
(window-height . 0.25))
|
|
:general
|
|
(code-leader
|
|
"t" '(nil :which-key "Tags"))
|
|
(code-leader
|
|
:infix "t"
|
|
"t" #'xref-find-apropos
|
|
"d" #'xref-find-definitions
|
|
"r" #'xref-find-references)
|
|
(nmmap
|
|
:keymaps 'xref--xref-buffer-mode-map
|
|
"RET" #'xref-goto-xref
|
|
"J" #'xref-next-line
|
|
"K" #'xref-prev-line
|
|
"g" #'xref-revert-buffer
|
|
"q" #'quit-window))
|
|
#+end_src
|
|
** Hl-line
|
|
Highlights the current line, much better than a blinking cursor.
|
|
#+begin_src emacs-lisp
|
|
(use-package hl-line
|
|
:straight t
|
|
:hook (text-mode-hook . hl-line-mode)
|
|
:hook (prog-mode-hook . hl-line-mode))
|
|
#+end_src
|
|
** Recentf
|
|
Recentf provides a method of keeping track of recently opened files.
|
|
#+begin_src emacs-lisp
|
|
(use-package recentf
|
|
:straight nil
|
|
:hook (emacs-startup-hook . recentf-mode))
|
|
#+end_src
|
|
** WAIT Projectile
|
|
:PROPERTIES:
|
|
:header-args:emacs-lisp: :tangle no
|
|
:END:
|
|
Projectile is a project management package which integrates with Emacs
|
|
very well. It essentially provides alternative Emacs commands scoped
|
|
to the current 'project', based on differing signs that a directory is
|
|
a 'project'.
|
|
#+begin_src emacs-lisp
|
|
(use-package projectile
|
|
:after evil
|
|
:hook (emacs-startup-hook . projectile-mode)
|
|
:general
|
|
(general-def
|
|
:keymaps 'projectile-command-map
|
|
"t" #'projectile-test-project
|
|
"r" #'projectile-run-project
|
|
"q" #'projectile-replace-regexp)
|
|
(leader
|
|
"p" '(projectile-command-map :which-key "Projectile"))
|
|
:init
|
|
(setq projectile-tags-command "ctags -Re -f \"%s\" %s \"%s\""
|
|
projectile-enable-caching t))
|
|
#+end_src
|
|
*** Counsel projectile
|
|
Counsel integration for projectile commands, very nice.
|
|
#+begin_src emacs-lisp
|
|
(use-package counsel-projectile
|
|
:after (projectile counsel)
|
|
:config
|
|
(counsel-projectile-mode +1))
|
|
#+end_src
|
|
** Avy
|
|
Setup avy with leader. As I use ~avy-goto-char-timer~ a lot, use the
|
|
~C-s~ bind which replaces isearch. Switch isearch to M-s in case I
|
|
need to use it.
|
|
#+begin_src emacs-lisp
|
|
(use-package avy
|
|
:after evil
|
|
:general
|
|
(nmmap
|
|
:keymaps 'override
|
|
"C-s" #'avy-goto-char-timer
|
|
"M-s" #'isearch-forward
|
|
"gp" #'avy-move-region
|
|
"gl" #'avy-goto-line
|
|
"gw" #'avy-goto-word-1))
|
|
#+end_src
|
|
** Ace window
|
|
Though evil provides a great many features in terms of window
|
|
management, ace window can provide some nicer chords for higher
|
|
management of windows (closing, switching, etc).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ace-window
|
|
:after evil
|
|
:custom
|
|
(aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
|
|
:general
|
|
(nmmap
|
|
[remap evil-window-next] #'ace-window))
|
|
#+end_src
|
|
** Ace link
|
|
Avy-style link following!
|
|
#+begin_src emacs-lisp
|
|
(use-package ace-link
|
|
:straight t
|
|
:general
|
|
(nmmap
|
|
:keymaps 'override
|
|
"gL" #'ace-link))
|
|
#+end_src
|
|
** Helpful
|
|
Helpful provides a modernised interface for some common help
|
|
commands. I replace ~describe-function~, ~describe-variable~ and
|
|
~describe-key~ by their helpful counterparts.
|
|
#+begin_src emacs-lisp
|
|
(use-package helpful
|
|
:after ivy
|
|
:commands (helpful-callable helpful-variable)
|
|
:general
|
|
(general-def
|
|
[remap describe-function] #'helpful-callable
|
|
[remap describe-variable] #'helpful-variable
|
|
[remap describe-key] #'helpful-key)
|
|
:display
|
|
("\\*helpful.*"
|
|
(display-buffer-at-bottom)
|
|
(inhibit-duplicate-buffer . t)
|
|
(window-height . 0.25))
|
|
:config
|
|
(evil-define-key 'normal helpful-mode-map "q" #'quit-window))
|
|
#+end_src
|
|
** Which-key
|
|
Which key uses the minibuffer when performing a keybind to provide
|
|
possible options for the next key.
|
|
#+begin_src emacs-lisp
|
|
(use-package which-key
|
|
:config
|
|
(which-key-mode))
|
|
#+end_src
|
|
** WAIT Keychord
|
|
:PROPERTIES:
|
|
:header-args:emacs-lisp: :tangle no
|
|
:END:
|
|
Keychord is only really here for this one chord I wish to define: "jk"
|
|
for exiting insert state.
|
|
#+begin_src emacs-lisp
|
|
(use-package key-chord
|
|
:after evil
|
|
:config
|
|
(key-chord-define evil-insert-state-map "jk" #'evil-normal-state)
|
|
(key-chord-mode))
|
|
#+end_src
|
|
** (Rip)grep
|
|
Grep is a great piece of software, a necessary tool in any Linux
|
|
user's inventory. By default Emacs has a family of functions to use
|
|
grep, presenting results in a ~compilation~ style. ~grep~ searches
|
|
files, ~rgrep~ searches in a directory using the ~find~ program and
|
|
~zgrep~ searches archives. This is a great solution for a general
|
|
computer environment; essentially all Linux installs will have ~grep~
|
|
and ~find~ installed.
|
|
|
|
Ripgrep is a Rust program that attempts to perform better than grep,
|
|
and it actually does. This is because of a set of optimisations, such
|
|
as checking the =.gitignore= to exclude certain files from being
|
|
searched. The ripgrep package provides utilities to ripgrep projects
|
|
and files for strings. Though [[file:core.org::*Ivy][ivy]] comes with
|
|
~counsel-rg~, it uses Ivy's completion framework rather than the
|
|
~compilation~ style buffers, which sometimes proves very useful.
|
|
|
|
Of course, this requires installing the rg binary which is available
|
|
in most repositories nowadays.
|
|
*** Grep
|
|
I have no use for standard 'grep'; ~counsel-swiper~ does the same
|
|
thing faster and within Emacs lisp. ~rgrep~ is useful though.
|
|
#+begin_src emacs-lisp
|
|
(use-package grep
|
|
:after evil
|
|
:straight nil
|
|
:display
|
|
("^\\*grep.*"
|
|
(display-buffer-at-bottom display-buffer-reuse-window)
|
|
(window-height . 0.35)
|
|
(reusable-frames . t))
|
|
:general
|
|
(search-leader
|
|
"d" #'rgrep)
|
|
(nmmap
|
|
:keymaps 'grep-mode-map
|
|
"0" #'evil-beginning-of-line
|
|
"q" #'quit-window
|
|
"i" #'wgrep-change-to-wgrep-mode
|
|
"c" #'recompile)
|
|
(nmmap
|
|
:keymaps 'wgrep-mode-map
|
|
"q" #'evil-record-macro
|
|
"Zz" #'wgrep-finish-edit
|
|
"Zq" #'wgrep-abort-changes)
|
|
:config
|
|
;; Without this wgrep doesn't work properly
|
|
(evil-set-initial-state 'grep-mode 'normal))
|
|
#+end_src
|
|
*** rg
|
|
#+begin_src emacs-lisp
|
|
(use-package rg
|
|
:after grep
|
|
:display
|
|
("^\\*\\*ripgrep\\*\\*"
|
|
(display-buffer-at-bottom display-buffer-reuse-window)
|
|
(window-height . 0.35)
|
|
(reusable-frames . t))
|
|
:general
|
|
(search-leader
|
|
"r" #'rg)
|
|
(nmmap
|
|
:keymaps 'rg-mode-map
|
|
"c" #'rg-recompile
|
|
"C" #'rg-rerun-toggle-case
|
|
"]]" #'rg-next-file
|
|
"[[" #'rg-prev-file
|
|
"q" #'quit-window
|
|
"i" #'wgrep-change-to-wgrep-mode)
|
|
:init
|
|
(setq rg-group-result t
|
|
rg-hide-command t
|
|
rg-show-columns nil
|
|
rg-show-header t
|
|
rg-custom-type-aliases nil
|
|
rg-default-alias-fallback "all"
|
|
rg-buffer-name "*ripgrep*")
|
|
:config
|
|
(evil-set-initial-state 'rg-mode 'normal))
|
|
#+end_src
|
|
*** Searching Lisp config
|
|
Using grep search all the elisp files. With ~wgrep~ this provides
|
|
another method of refactoring.
|
|
#+begin_src emacs-lisp
|
|
(use-package grep
|
|
:general
|
|
(search-leader
|
|
"c" #'+grep/search-config)
|
|
:config
|
|
(defun +grep/grep-cmd-str (str files)
|
|
(cl-reduce
|
|
#'concat
|
|
(append (list grep-command "\"" str "\" ")
|
|
(mapcar (lambda (x) (concat x " ")) files))))
|
|
|
|
(defun +grep/search-config ()
|
|
(interactive)
|
|
(grep (+grep/grep-cmd-str (read-string "Search?: ")
|
|
+literate/el-files))))
|
|
#+end_src
|
|
** Olivetti
|
|
Olivetti provides a focus mode for Emacs, which makes it look a bit
|
|
nicer with fringes. I also define ~+olivetti-mode~ which will
|
|
remember and clear up any window configurations on the frame, then
|
|
when turned off will reinsert them - provides a nice way to quickly
|
|
focus on a buffer.
|
|
#+begin_src emacs-lisp
|
|
(use-package olivetti
|
|
:commands (+olivetti-mode)
|
|
:general
|
|
(mode-leader
|
|
"o" #'+olivetti-mode)
|
|
:init
|
|
(setq-default olivetti-body-width 0.6)
|
|
(setq olivetti-style 'fancy)
|
|
(add-hook 'olivetti-mode-on-hook
|
|
(proc (interactive) (text-scale-increase 1)))
|
|
(add-hook 'olivetti-mode-off-hook
|
|
(proc (interactive) (text-scale-decrease 1)))
|
|
:config
|
|
(defun +olivetti-mode ()
|
|
(interactive)
|
|
(if (not olivetti-mode)
|
|
(progn
|
|
(window-configuration-to-register 1)
|
|
(delete-other-windows)
|
|
(olivetti-mode t))
|
|
(jump-to-register 1)
|
|
(olivetti-mode 0))))
|
|
#+end_src
|
|
*** Presentation mode
|
|
A simple presentation system using org-mode and olivetti.
|
|
#+begin_src emacs-lisp
|
|
(use-package olivetti
|
|
:defer t
|
|
:config
|
|
(defun +presentation/prev-slide ()
|
|
(interactive)
|
|
(when presentation-mode
|
|
(widen)
|
|
(outline-previous-visible-heading 1)
|
|
(end-of-line)
|
|
(if (org-fold-folded-p)
|
|
(org-cycle))
|
|
(org-narrow-to-subtree)))
|
|
(defun +presentation/next-slide ()
|
|
(interactive)
|
|
(when presentation-mode
|
|
(widen)
|
|
(outline-next-visible-heading 1)
|
|
(end-of-line)
|
|
(if (org-fold-folded-p)
|
|
(org-cycle))
|
|
(org-narrow-to-subtree)))
|
|
(defvar presentation-mode-map (make-sparse-keymap))
|
|
(define-minor-mode presentation-mode
|
|
"When in org-mode, use each heading like a slide!"
|
|
:lighter nil
|
|
:keymap presentation-mode-map
|
|
(cond
|
|
(presentation-mode
|
|
(olivetti-mode t)
|
|
(outline-show-heading)
|
|
(org-narrow-to-subtree))
|
|
(t
|
|
(olivetti-mode -1)
|
|
(widen))))
|
|
:general
|
|
(leader
|
|
:states 'normal
|
|
:keymaps 'presentation-mode-map
|
|
"j" #'+presentation/next-slide
|
|
"k" #'+presentation/prev-slide)
|
|
(local-leader
|
|
:keymaps 'org-mode-map
|
|
"P" #'presentation-mode))
|
|
#+end_src
|
|
** All the Icons
|
|
Nice set of icons with a great user interface to manage them.
|
|
#+begin_src emacs-lisp
|
|
(use-package all-the-icons
|
|
:straight t
|
|
:defer t
|
|
:commands (all-the-icons-insert)
|
|
:general
|
|
(insert-leader
|
|
"e" #'all-the-icons-insert))
|
|
#+end_src
|
|
** Hide mode line
|
|
Custom minor mode to toggle the mode line. Check it out at
|
|
[[file:elisp/hide-mode-line.el][elisp/hide-mode-line.el]].
|
|
#+begin_src emacs-lisp
|
|
(use-package hide-mode-line
|
|
:straight nil
|
|
:load-path "elisp/"
|
|
:defer t
|
|
:general
|
|
(mode-leader
|
|
"m" #'hide-mode-line-mode))
|
|
#+end_src
|
|
** Save place
|
|
Saves current place in a buffer permanently, so on revisiting the file
|
|
(even in a different Emacs instance) you go back to the place you were
|
|
at last.
|
|
#+begin_src emacs-lisp
|
|
(use-package saveplace
|
|
:straight nil
|
|
:config
|
|
(save-place-mode))
|
|
#+end_src
|
|
** Rot13
|
|
ROT13 encoding is a pretty simple cipher; fun to make decoders and
|
|
encoders for. Emacs has default support for it, to the point where it
|
|
can display files with the encoding without changing the underlying
|
|
text. That's what this is mainly for.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package rot13
|
|
:straight nil
|
|
:general
|
|
(mode-leader
|
|
"r" #'toggle-rot13-mode))
|
|
#+end_src
|
|
** Licensing
|
|
Loads [[file:elisp/license.el][license.el]] for inserting licenses.
|
|
Licenses are important for distribution and attribution to be defined
|
|
clearly.
|
|
#+begin_src emacs-lisp
|
|
(use-package license
|
|
:straight nil
|
|
:load-path "elisp/"
|
|
:demand t
|
|
:general
|
|
(insert-leader
|
|
"l" #'+license/insert-copyright-notice
|
|
"L" #'+license/insert-complete-license))
|
|
#+end_src
|
|
** Memory-report
|
|
New feature of Emacs-29, gives a rough report of memory usage with
|
|
some details. Useful to know on a long Emacs instance what could be
|
|
eating up memory.
|
|
#+begin_src emacs-lisp
|
|
(use-package memory-report
|
|
:straight nil
|
|
:general
|
|
(leader
|
|
"qm" #'memory-report))
|
|
#+end_src
|
|
** Save minibuffer history
|
|
#+begin_src emacs-lisp
|
|
(use-package savehist
|
|
:straight nil
|
|
:config
|
|
(savehist-mode t))
|
|
#+end_src
|
|
** Drag Stuff
|
|
#+begin_src emacs-lisp
|
|
(use-package drag-stuff
|
|
:straight t
|
|
:general
|
|
(nmmap
|
|
"C-M-h" #'drag-stuff-left
|
|
"C-M-j" #'drag-stuff-down
|
|
"C-M-k" #'drag-stuff-up
|
|
"C-M-l" #'drag-stuff-right))
|
|
#+end_src
|
|
* Applications (loading)
|
|
Emacs is basically an operating system whose primary datatype is text.
|
|
Applications are interfaces/environments which serve a variety of
|
|
purposes, but provide a lot of capability. I have a
|
|
[[file:app.org][separate file]] for such configuration (2023-09-29:
|
|
mainly because it was so goddamn huge).
|
|
|
|
#+begin_src emacs-lisp
|
|
(load-file (concat user-emacs-directory "app.el"))
|
|
#+end_src
|
|
* Text modes
|
|
Standard packages and configurations for text-mode and its derived
|
|
modes.
|
|
** Flyspell
|
|
Flyspell allows me to quickly spell check text documents. I use
|
|
flyspell primarily in org mode, as that is my preferred prose writing
|
|
software, but I also need it in commit messages and so on. So
|
|
flyspell-mode should be hooked to text-mode.
|
|
#+begin_src emacs-lisp
|
|
(use-package flyspell
|
|
:hook (text-mode-hook . flyspell-mode)
|
|
:general
|
|
(nmmap
|
|
:keymaps 'text-mode-map
|
|
(kbd "M-C") #'flyspell-correct-word-before-point
|
|
(kbd "M-c") #'flyspell-auto-correct-word)
|
|
(mode-leader
|
|
"s" #'flyspell-mode))
|
|
#+end_src
|
|
** Undo tree
|
|
Undo tree sits on top of the incredible Emacs undo capabilities.
|
|
Provides a nice visual for edits and a great way to produce branches
|
|
of edits. Also allows saving of undo trees, which makes Emacs a quasi
|
|
version control system in and of itself! The only extra necessary
|
|
would be describing changes...
|
|
#+begin_src emacs-lisp
|
|
(use-package undo-tree
|
|
:straight t
|
|
:hook (after-init-hook . global-undo-tree-mode)
|
|
:init
|
|
(setq undo-tree-auto-save-history t
|
|
undo-tree-history-directory-alist backup-directory-alist)
|
|
:general
|
|
(leader
|
|
"u" #'undo-tree-visualize))
|
|
#+end_src
|
|
** Whitespace
|
|
Deleting whitespace, highlighting when going beyond the 80th character
|
|
limit, all good stuff. I don't want to highlight whitespace for
|
|
general mode categories (Lisp shouldn't really have an 80 character
|
|
limit), so set it for specific modes need the help.
|
|
#+begin_src emacs-lisp
|
|
(use-package whitespace
|
|
:straight nil
|
|
:general
|
|
(nmmap
|
|
"M--" #'whitespace-cleanup)
|
|
(mode-leader
|
|
"w" #'whitespace-mode)
|
|
:hook
|
|
(before-save-hook . whitespace-cleanup)
|
|
(c-mode-hook . whitespace-mode)
|
|
(c++-mode-hook . whitespace-mode)
|
|
(haskell-mode-hook . whitespace-mode)
|
|
(python-mode-hook . whitespace-mode)
|
|
(org-mode-hook . whitespace-mode)
|
|
(text-mode-hook . whitespace-mode)
|
|
:init
|
|
(setq whitespace-style '(face lines-tail spaces tabs tab-mark trailing newline)
|
|
whitespace-line-column 80))
|
|
#+end_src
|
|
** Set auto-fill-mode for all text-modes
|
|
Auto fill mode automatically newlines text on 80 characters, which
|
|
looks nice and integrates well with Evil's sentence and paragraph text
|
|
objects.
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'text-mode-hook #'auto-fill-mode)
|
|
#+end_src
|
|
** Show-paren-mode
|
|
Show parenthesis for Emacs
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'prog-mode-hook #'show-paren-mode)
|
|
#+end_src
|
|
** Smartparens
|
|
Smartparens is a smarter electric-parens, it's much more aware of
|
|
context and easier to use.
|
|
#+begin_src emacs-lisp
|
|
(use-package smartparens
|
|
:hook
|
|
(prog-mode-hook . smartparens-mode)
|
|
(text-mode-hook . smartparens-mode)
|
|
:after evil
|
|
:config
|
|
(setq sp-highlight-pair-overlay nil
|
|
sp-highlight-wrap-overlay t
|
|
sp-highlight-wrap-tag-overlay t)
|
|
|
|
(let ((unless-list '(sp-point-before-word-p
|
|
sp-point-after-word-p
|
|
sp-point-before-same-p)))
|
|
(sp-pair "'" nil :unless unless-list)
|
|
(sp-pair "\"" nil :unless unless-list))
|
|
(sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p))
|
|
(require 'smartparens-config))
|
|
#+end_src
|
|
** Thesaurus
|
|
=le-thesaurus= is a great extension for quickly searching up words for
|
|
synonyms or antonyms. I may need it anywhere so I bind it to all
|
|
keymaps. Same with dictionary searching.
|
|
#+begin_src emacs-lisp
|
|
(use-package le-thesaurus
|
|
:straight t
|
|
:display
|
|
("\\*Dictionary\\*"
|
|
(display-buffer-reuse-window display-buffer-same-window)
|
|
(reusable-frames . t))
|
|
:init
|
|
(setq dictionary-server "dict.org")
|
|
:general
|
|
(search-leader
|
|
:infix "w"
|
|
"s" #'le-thesaurus-get-synonyms
|
|
"a" #'le-thesaurus-get-antonyms
|
|
"d" #'dictionary-search))
|
|
#+end_src
|
|
* Programming packages
|
|
Packages that help with programming in general, providing IDE like
|
|
capabilities.
|
|
** Eldoc
|
|
Eldoc presents documentation to the user upon placing ones cursor upon
|
|
any symbol. This is very useful when programming as it:
|
|
- presents the arguments of functions while writing calls for them
|
|
- presents typing and documentation of variables
|
|
|
|
Eldoc box makes the help buffer a hovering box instead of printing it
|
|
in the minibuffer. A lot cleaner.
|
|
#+begin_src emacs-lisp
|
|
(use-package eldoc
|
|
:straight nil
|
|
:hook (prog-mode-hook . eldoc-mode)
|
|
:init
|
|
(global-eldoc-mode 1)
|
|
:general
|
|
(leader
|
|
"h>" #'eldoc-doc-buffer))
|
|
|
|
(use-package eldoc-box
|
|
:hook (eldoc-mode-hook . eldoc-box-hover-mode)
|
|
:init
|
|
(setq eldoc-box-position-function #'eldoc-box--default-upper-corner-position-function
|
|
eldoc-box-clear-with-C-g t)
|
|
:general
|
|
(leader
|
|
"h." #'eldoc-box-help-at-point))
|
|
#+end_src
|
|
** Flycheck
|
|
Flycheck is the checking system for Emacs. I don't necessarily like
|
|
having all my code checked all the time, so I haven't added a hook to
|
|
prog-mode as it would be better for me to decide when I want checking
|
|
and when I don't.
|
|
|
|
I've added it to C/C++ mode because I use them regularly and flycheck
|
|
has very little overhead to work there.
|
|
#+begin_src emacs-lisp
|
|
(use-package flycheck
|
|
:commands (flycheck-mode flycheck-list-errors)
|
|
:hook
|
|
(c-mode-hook . flycheck-mode)
|
|
(c++-mode-hook . flycheck-mode)
|
|
:general
|
|
(mode-leader
|
|
"f" #'flycheck-mode)
|
|
(code-leader
|
|
"x" #'flycheck-list-errors
|
|
"J" #'flycheck-next-error
|
|
"K" #'flycheck-previous-error)
|
|
:display
|
|
("\\*Flycheck.*"
|
|
(display-buffer-at-bottom)
|
|
(window-height . 0.25))
|
|
:config
|
|
(with-eval-after-load "evil-collection"
|
|
(evil-collection-flycheck-setup)))
|
|
#+end_src
|
|
** Eglot
|
|
Eglot is package to communicate with LSP servers for better
|
|
programming capabilities. Interactions with a server provide results
|
|
to the client, done through JSON.
|
|
|
|
NOTE: Emacs 28.1 comes with better JSON parsing, which makes Eglot
|
|
much faster.
|
|
|
|
2023-03-26: I've found Eglot to be useful sometimes, but many of the
|
|
projects I work on don't require a heavy server setup to efficiently
|
|
edit and check for errors; Emacs provides a lot of functionality. So
|
|
by default I've disabled it, using =M-x eglot= to startup the LSP
|
|
server when I need it.
|
|
#+begin_src emacs-lisp
|
|
(use-package eglot
|
|
:after project
|
|
:defer t
|
|
:general
|
|
(code-leader
|
|
:keymaps 'eglot-mode-map
|
|
"f" #'eglot-format
|
|
"a" #'eglot-code-actions
|
|
"r" #'eglot-rename
|
|
"R" #'eglot-reconnect)
|
|
:init
|
|
(setq eglot-stay-out-of '(flymake))
|
|
:config
|
|
(add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd")))
|
|
#+end_src
|
|
*** Flycheck-Eglot
|
|
By default Eglot uses the integrated flymake package for error
|
|
reporting. I don't mind flymake, and I think an integrated solution
|
|
which doesn't rely on external packages is always a great idea.
|
|
However, I just personally prefer flycheck and it's become part of my
|
|
mental model when programming. So here's a package which will
|
|
integrate flycheck into Eglot's error reporting.
|
|
|
|
(Funny but also kind of depressing is this issue in Eglot where
|
|
someone requested this integration, which caused a bit of a flame war.
|
|
People are stupid.
|
|
[[https://github.com/joaotavora/eglot/issues/42][no opinion on
|
|
flymake]])
|
|
#+begin_src emacs-lisp
|
|
(use-package flycheck-eglot
|
|
:straight t
|
|
:after (flycheck eglot)
|
|
:hook (eglot-managed-mode-hook . flycheck-eglot-mode))
|
|
#+end_src
|
|
** Indentation
|
|
By default, turn off tabs and set the tab width to two.
|
|
#+begin_src emacs-lisp
|
|
(setq-default indent-tabs-mode nil
|
|
tab-width 2)
|
|
#+end_src
|
|
|
|
However, if necessary later, define a function that may activate tabs locally.
|
|
#+begin_src emacs-lisp
|
|
(defun +oreo/use-tabs ()
|
|
(interactive)
|
|
(setq-local indent-tabs-mode t))
|
|
#+end_src
|
|
** Highlight todo items
|
|
TODO items are highlighted in org-mode, but not necessarily in every
|
|
mode. This minor mode highlights all TODO like items via a list of
|
|
strings to match. It also configures faces to use when highlighting.
|
|
I hook it to prog-mode.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package hl-todo
|
|
:after prog-mode
|
|
:hook (prog-mode-hook . hl-todo-mode)
|
|
:init
|
|
(setq hl-todo-keyword-faces
|
|
'(("TODO" . "#E50000")
|
|
("WIP" . "#ffa500")
|
|
("NOTE" . "#00CC00")
|
|
("FIXME" . "#d02090"))))
|
|
#+end_src
|
|
** Hide-show mode
|
|
Turn on ~hs-minor-mode~ for all prog-mode. This provides folds for
|
|
free.
|
|
#+begin_src emacs-lisp
|
|
(use-package hideshow
|
|
:straight nil
|
|
:hook (prog-mode-hook . hs-minor-mode))
|
|
#+end_src
|
|
** Aggressive indenting
|
|
Essentially my dream editing experience: when I type stuff in, try and
|
|
indent it for me on the fly. Just checkout the
|
|
[[https://github.com/Malabarba/aggressive-indent-mode][page]], any
|
|
description I give won't do it justice.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package aggressive-indent
|
|
:straight t
|
|
:demand t
|
|
:config
|
|
(add-to-list 'aggressive-indent-excluded-modes
|
|
'c-mode)
|
|
(add-to-list 'aggressive-indent-excluded-modes
|
|
'c++-mode)
|
|
(add-to-list 'aggressive-indent-excluded-modes
|
|
'cc-mode)
|
|
(global-aggressive-indent-mode))
|
|
#+end_src
|
|
** Compilation
|
|
Colourising the compilation buffer so ANSI colour codes get computed.
|
|
#+begin_src emacs-lisp
|
|
(use-package compile
|
|
:straight nil
|
|
:general
|
|
(code-leader
|
|
"j" #'next-error
|
|
"k" #'previous-error
|
|
"c" #'compile
|
|
"C" #'recompile)
|
|
(nmmap
|
|
:keymaps 'compilation-mode-map
|
|
"c" #'recompile)
|
|
(general-def
|
|
:keymaps 'compilation-mode-map
|
|
"g" nil) ;; by default this is recompile
|
|
:display
|
|
("\\*compilation\\*"
|
|
(display-buffer-reuse-window display-buffer-at-bottom)
|
|
(reusable-frames . t)
|
|
(window-height . 0.25))
|
|
:config
|
|
(defun +compile/colourise ()
|
|
"Colourise the emacs compilation buffer."
|
|
(interactive)
|
|
(let ((inhibit-read-only t))
|
|
(ansi-color-apply-on-region (point-min) (point-max))))
|
|
(add-hook 'compilation-filter-hook #'+compile/colourise))
|
|
#+end_src
|
|
* Org mode
|
|
2023-03-30: finally decided to give org mode its own section.
|
|
|
|
Org is, at its most basic, a markup language. Files use the ".org"
|
|
extension and use =org-mode= to write text, with the ability to export
|
|
to a few formats, all within Emacs. Some other features include:
|
|
+ A complete spreadsheet system, with formulas (including
|
|
[[file:app.org::*Calculator][calc-mode]] integration)
|
|
+ Evaluation of code blocks, even using the results of them in exports
|
|
(to, say, a $\LaTeX$ or HTML document)
|
|
+ This includes exporting code blocks to a code file. All the
|
|
emacs-lisp code blocks in this file are compiled to =config.el=
|
|
([[file:elisp/literate.el][literate]])
|
|
+ Complete calendar/todo system with deadlines, scheduling and
|
|
repeaters
|
|
+ Export to a variety of formats or make your own export engine using
|
|
the org AST!
|
|
+ Writing $\LaTeX$ inline, with the ability to render the fragments on
|
|
demand
|
|
** Org Essentials
|
|
Org has a ton of settings to tweak, which change your experience quite
|
|
a bit. Here are mine, but this took a lot of just reading other
|
|
people's configurations and testing. I don't do a good job of
|
|
explaining how this works in all honesty, but it works well for me so
|
|
I'm not very bothered.
|
|
|
|
+ By default =~/Text= is my directory for text files. I actually have
|
|
a repository that manages this directory for agenda files and other
|
|
documents
|
|
+ Indentation in file should not be allowed, i.e. text indentation, as
|
|
that forces other editors to read it a certain way as well. It's
|
|
obtrusive hence it's off.
|
|
+ Org startup indented is on by default as most documents do benefit
|
|
from the indentation, but I do turn it off for some files via
|
|
~#+startup:noindent~
|
|
+ When opening an org document there can be a lot of headings, so I
|
|
set folding to just content
|
|
+ Org documents can also have a lot of latex previews, which make
|
|
opening some after a while a massive hassle. If I want to see the
|
|
preview, I'll do it myself, so turn it off.
|
|
+ Org manages windowing itself, to some extent, so I set those options
|
|
to be as unobtrusive as possible
|
|
+ Load languages I use in =src= blocks in org-mode (Emacs-lisp for
|
|
this configuration, C and Python)
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:defer t
|
|
:straight t
|
|
:init
|
|
(setq org-directory "~/Text"
|
|
org-adapt-indentation nil
|
|
org-indent-mode nil
|
|
org-startup-indented t
|
|
org-startup-folded 'content
|
|
org-startup-with-latex-preview nil
|
|
org-imenu-depth 10
|
|
org-src-window-setup 'current-window
|
|
org-indirect-buffer-display 'current-window
|
|
org-link-frame-setup '((vm . vm-visit-folder-other-frame)
|
|
(vm-imap . vm-visit-imap-folder-other-frame)
|
|
(file . find-file))
|
|
org-babel-load-languages '((emacs-lisp . t)
|
|
(lisp . t)
|
|
(shell . t))))
|
|
#+end_src
|
|
** Org Latex
|
|
Org mode has deep integration with latex, can export to PDF and even
|
|
display latex fragments in the document directly. I setup the
|
|
pdf-process, code listing options via minted and the format options
|
|
for latex fragments.
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:defer t
|
|
:init
|
|
(setq org-format-latex-options
|
|
'(:foreground default :background default :scale 2
|
|
:html-foreground "Black" :html-background "Transparent"
|
|
:html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\["))
|
|
org-latex-src-block-backend 'minted
|
|
org-latex-minted-langs '((emacs-lisp "common-lisp")
|
|
(ledger "text")
|
|
(cc "c++")
|
|
(cperl "perl")
|
|
(shell-script "bash")
|
|
(caml "ocaml"))
|
|
org-latex-packages-alist '(("" "minted"))
|
|
org-latex-pdf-process
|
|
(list (concat "latexmk -f -bibtex -pdf "
|
|
"-shell-escape -%latex -interaction=nonstopmode "
|
|
"-output-directory=%o %f"))
|
|
org-latex-minted-options
|
|
'(("style" "colorful")
|
|
("linenos")
|
|
("frame" "single")
|
|
("mathescape")
|
|
("fontfamily" "courier")
|
|
("samepage" "false")
|
|
("breaklines" "true")
|
|
("breakanywhere" "true"))))
|
|
#+end_src
|
|
** Org Core Variables
|
|
Tons of variables for org-mode, including a ton of latex ones. Can't
|
|
really explain because it sets up quite a lot of local stuff. Also I
|
|
copy pasted the majority of this, tweaking it till it felt good. Doom
|
|
Emacs was very helpful here.
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:defer t
|
|
:init
|
|
(setq org-edit-src-content-indentation 0
|
|
org-goto-interface 'outline
|
|
org-imenu-depth 10
|
|
org-export-backends '(ascii html latex odt icalendar)
|
|
org-eldoc-breadcrumb-separator " → "
|
|
org-enforce-todo-dependencies t
|
|
org-fontify-quote-and-verse-blocks t
|
|
org-fontify-whole-heading-line t
|
|
org-footnote-auto-label t
|
|
org-hide-leading-stars t
|
|
org-hide-emphasis-markers nil
|
|
org-image-actual-width nil
|
|
org-priority-faces '((?A . error) (?B . warning) (?C . success))
|
|
org-link-descriptive nil
|
|
org-tags-column 0
|
|
org-todo-keywords
|
|
'((sequence "TODO" "WIP" "DONE")
|
|
(sequence "PROJ" "WAIT" "COMPLETE"))
|
|
org-use-sub-superscripts '{}))
|
|
#+end_src
|
|
** Org Core Functionality
|
|
Hooks, prettify-symbols and records for auto insertion.
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:hook
|
|
(org-mode-hook . prettify-symbols-mode)
|
|
:display
|
|
("\\*Org Src.*"
|
|
(display-buffer-same-window))
|
|
:pretty
|
|
(org-mode-hook
|
|
("#+begin_src" . "≫")
|
|
("#+end_src" . "≪"))
|
|
:init
|
|
(with-eval-after-load "autoinsert"
|
|
(define-auto-insert '("\\.org\\'" . "Org skeleton")
|
|
'("Enter title: "
|
|
"#+title: " str | (buffer-file-name) "\n"
|
|
"#+author: " (read-string "Enter author: ") | user-full-name "\n"
|
|
"#+description: " (read-string "Enter description: ") | "Description" "\n"
|
|
"#+date: " (format-time-string "%Y-%m-%d" (current-time)) "\n"
|
|
"* " _))))
|
|
#+end_src
|
|
** Org Core Bindings
|
|
Some bindings for org mode.
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:general
|
|
(file-leader
|
|
"l" #'org-store-link
|
|
"i" #'org-insert-last-stored-link)
|
|
(code-leader
|
|
:keymaps 'emacs-lisp-mode-map
|
|
"D" #'org-babel-detangle)
|
|
(local-leader
|
|
:keymaps 'org-mode-map
|
|
"l" '(nil :which-key "Links")
|
|
"'" '(nil :which-key "Tables")
|
|
"c" '(nil :which-key "Clocks")
|
|
"r" #'org-refile
|
|
"d" #'org-date-from-calendar
|
|
"t" #'org-todo
|
|
"," #'org-priority
|
|
"T" #'org-babel-tangle
|
|
"i" #'org-insert-structure-template
|
|
"p" #'org-latex-preview
|
|
"s" #'org-property-action
|
|
"e" #'org-export-dispatch
|
|
"o" #'org-edit-special)
|
|
(local-leader
|
|
:keymaps 'org-mode-map
|
|
:infix "l"
|
|
"i" #'org-insert-link
|
|
"l" #'org-open-at-point
|
|
"f" #'org-footnote-action)
|
|
(local-leader
|
|
:keymaps 'org-mode-map
|
|
:infix "'"
|
|
"a" #'org-table-align
|
|
"c" #'org-table-create
|
|
"f" #'org-table-edit-formulas
|
|
"t" #'org-table-toggle-coordinate-overlays
|
|
"s" #'org-table-sum
|
|
"e" #'org-table-calc-current-TBLFM
|
|
"E" #'org-table-eval-formula))
|
|
#+end_src
|
|
** Searching org files
|
|
The default ~imenu~ support for Org-mode is god-awful. ~Imenu~ for
|
|
org-mode should show me a list of headings and provide a
|
|
completing-read interface to search them.
|
|
|
|
[[file:core.org::*Counsel][Counsel]] has me covered for this as I can
|
|
just provide it a regex as an initial prompt to narrow the candidates
|
|
down to just the headings then let the user go from there. I use
|
|
~swiper~ when considering just the local file (a la ~imenu~) and
|
|
~counsel-rg~ to search multiple org-files.
|
|
|
|
The cherry on top is ~+org/search-config-headings~ which searches the
|
|
org files in ~user-emacs-directory~ and provides the headings for
|
|
them. This allows me to search my configuration pretty quickly.
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:after counsel
|
|
:config
|
|
(defun +org/swiper-goto ()
|
|
(interactive)
|
|
(swiper "^\\* "))
|
|
|
|
(defun +org/search-headings ()
|
|
"Searches directory (of buffer) for org headings via counsel-rg"
|
|
(interactive)
|
|
(counsel-rg "^\\* " (file-name-directory (buffer-file-name))))
|
|
|
|
(defun +org/search-config-headings ()
|
|
"Searches USER-EMACS-DIRECTORY for org headings via counsel-rg"
|
|
(interactive)
|
|
(counsel-rg "^\\* "
|
|
(substring user-emacs-directory 0
|
|
(- (length user-emacs-directory) 1))
|
|
"--max-depth=1"))
|
|
:general
|
|
(file-leader
|
|
"p" #'+org/search-config-headings)
|
|
(search-leader
|
|
:keymaps 'org-mode-map
|
|
"I" #'+org/search-headings)
|
|
(nmmap
|
|
:keymaps 'org-mode-map
|
|
[remap imenu] #'+org/swiper-goto))
|
|
|
|
#+end_src
|
|
** Org Agenda
|
|
Org agenda provides a nice viewing for schedules. With org mode it's
|
|
a very tidy way to manage your time.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-agenda
|
|
:after org
|
|
:straight nil
|
|
:init
|
|
(defconst +org/agenda-root "~/Text"
|
|
"Root directory for all agenda files")
|
|
(setq org-agenda-files (list (expand-file-name +org/agenda-root))
|
|
org-agenda-window-setup 'current-window
|
|
org-agenda-skip-deadline-prewarning-if-scheduled t
|
|
org-agenda-skip-scheduled-if-done t
|
|
org-agenda-skip-deadline-if-done t
|
|
org-agenda-start-with-entry-text-mode nil)
|
|
:config
|
|
(evil-set-initial-state 'org-agenda-mode 'normal)
|
|
:general
|
|
(file-leader
|
|
"a" `(,(proc (interactive)
|
|
(find-file (completing-read "Enter directory: " org-agenda-files nil t)))
|
|
:which-key "Open agenda directory"))
|
|
|
|
(app-leader
|
|
"a" #'org-agenda)
|
|
|
|
(nmmap
|
|
:keymaps 'org-agenda-mode-map
|
|
"zd" #'org-agenda-day-view
|
|
"zw" #'org-agenda-week-view
|
|
"zm" #'org-agenda-month-view
|
|
"gd" #'org-agenda-goto-date
|
|
"RET" #'org-agenda-switch-to
|
|
"J" #'org-agenda-later
|
|
"K" #'org-agenda-earlier
|
|
"t" #'org-agenda-todo
|
|
"." #'org-agenda-goto-today
|
|
"," #'org-agenda-goto-date
|
|
"q" #'org-agenda-quit
|
|
"r" #'org-agenda-redo))
|
|
#+end_src
|
|
** Org capture
|
|
2024-04-24: I actually need to clean this up, in particular explain
|
|
what org-capture does.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-capture
|
|
:straight nil
|
|
:init
|
|
(setq
|
|
org-capture-templates
|
|
'(("t" "A todo" entry
|
|
(file "")
|
|
"* TODO %?
|
|
%T
|
|
%a"))
|
|
org-default-notes-file (concat org-directory "/todo.org"))
|
|
:general
|
|
(file-leader
|
|
"w" #'org-capture))
|
|
#+end_src
|
|
** Org clock-in
|
|
Org provides a nice timekeeping system that allows for managing how
|
|
much time is taken per task. It even has an extensive reporting
|
|
system to see how much time you spend on specific tasks or overall.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-clock
|
|
:after org
|
|
:straight nil
|
|
:init
|
|
(defvar +org/clock-out-toggle-report nil
|
|
"Non-nil means update the first clock report in the file every
|
|
time a clock out occurs.")
|
|
:config
|
|
(advice-add #'org-clock-out
|
|
:after
|
|
(proc (interactive)
|
|
(if +org/clock-out-toggle-report
|
|
(org-clock-report t))))
|
|
:general
|
|
(local-leader
|
|
:keymaps 'org-mode-map
|
|
:infix "c"
|
|
"d" #'org-clock-display
|
|
"c" #'org-clock-in
|
|
"o" #'org-clock-out
|
|
"r" #'org-clock-report
|
|
"t" (proc (interactive)
|
|
(setq-local +org/clock-out-toggle-report
|
|
(not +org/clock-out-toggle-report)))))
|
|
#+end_src
|
|
** Org compile to PDF on save
|
|
If ~+org/compile-to-pdf-on-save-p~ is non-nil, then compile to
|
|
\(\LaTeX\) and run an async process to compile it to a PDF. Doesn't
|
|
make Emacs hang (like ~org-latex-export-to-pdf~) and doesn't randomly
|
|
crash (like the async handler for org-export). Works really well with
|
|
~pdf-view-mode~.
|
|
#+begin_src emacs-lisp
|
|
(use-package org
|
|
:defer t
|
|
:init
|
|
(defvar +org/compile-to-pdf-on-save-p
|
|
nil
|
|
"Non-nil to activate compile functionality.")
|
|
:general
|
|
(local-leader
|
|
:keymaps 'org-mode-map
|
|
"C" (proc (interactive)
|
|
(if (+org/compile-to-pdf-on-save-f)
|
|
(setq-local +org/compile-to-pdf-on-save-p nil)
|
|
(setq-local +org/compile-to-pdf-on-save-p t))))
|
|
:config
|
|
(+oreo/create-auto-save
|
|
(and (eq major-mode 'org-mode) +org/compile-to-pdf-on-save-p)
|
|
(start-process-shell-command "" "*pdflatex*" (concat "pdflatex -shell-escape "
|
|
(org-latex-export-to-latex)))))
|
|
#+end_src
|
|
** Org ref
|
|
For bibliographic stuff in $\LaTeX$ export.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-ref
|
|
:straight t
|
|
:defer t
|
|
:init
|
|
(setq bibtex-files '("~/Text/bibliography.bib")
|
|
bibtex-completion-bibliography '("~/Text/bibliography.bib")
|
|
bibtex-completion-additional-search-fields '(keywords)))
|
|
#+end_src
|
|
*** Org ref ivy integration
|
|
Org ref requires ivy-bibtex to work properly with ivy, so we need to
|
|
set that up as well
|
|
#+begin_src emacs-lisp
|
|
(use-package ivy-bibtex
|
|
:straight t
|
|
:after org-ref
|
|
:config
|
|
(require 'org-ref-ivy))
|
|
#+end_src
|
|
** Org message
|
|
Org message allows for the use of org mode when composing mails,
|
|
generating HTML multipart emails. This integrates the WYSIWYG
|
|
experience with mail in Emacs while also providing powerful text
|
|
features with basically no learning curve (as long as you've already
|
|
learnt the basics of org).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-msg
|
|
:hook
|
|
(message-mode-hook . org-msg-mode)
|
|
(notmuch-message-mode-hook . org-msg-mode)
|
|
:config
|
|
(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")))))
|
|
#+end_src
|
|
** Org for evil
|
|
Evil org for some nice bindings.
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-org
|
|
:hook (org-mode-hook . evil-org-mode))
|
|
#+end_src
|
|
** Org reveal
|
|
Org reveal allows one to export org files as HTML presentations via
|
|
reveal.js. Pretty nifty and it's easy to use.
|
|
#+begin_src emacs-lisp
|
|
(use-package ox-reveal
|
|
:defer t
|
|
:init
|
|
(setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"
|
|
org-reveal-theme "sky"))
|
|
#+end_src
|
|
** WAIT Org fragtog
|
|
:PROPERTIES:
|
|
:header-args:emacs-lisp: :tangle no
|
|
:END:
|
|
Toggle latex fragments in org mode so you get fancy maths symbols. I
|
|
use latex a bit in org mode as it is the premier way of getting
|
|
mathematical symbols rendered, but org mode > latex.
|
|
|
|
Delimited environments are aplenty, escaped brackets and dollar signs
|
|
are my favourite. Here's a snippet:
|
|
$\int_{-\infty}^{\infty}e^{-x^2}dx = \sqrt{\pi}$.
|
|
|
|
[2023-09-10 Sun] Emacs 29 complains constantly about this, probably
|
|
because this isn't implemented that well. Regardless it wasn't that
|
|
necessary anyway, just a nice feature to have.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-fragtog
|
|
:hook (org-mode-hook . org-fragtog-mode))
|
|
#+end_src
|
|
** Org superstar
|
|
Org superstar adds unicode symbols for headers, much better than the
|
|
default asterisks.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-superstar
|
|
:hook (org-mode-hook . org-superstar-mode))
|
|
#+end_src
|
|
* Languages (loading)
|
|
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. I have a [[file:lang.org][separate file]] for this
|
|
configuration as it's quite large.
|
|
#+begin_src emacs-lisp
|
|
(load-file (concat user-emacs-directory "lang.el"))
|
|
#+end_src
|