:END:"
:empty-lines 1
:jump-to-captured t
:immediate-finish t
)
("r" "Reply to this mail" entry (file+olp+datetree "~/data/notes/notes.org") "* TODO Reply to %:fromname—%:subject :mail:
:PROPERTIES:
:MAIL_TO: %:from
:MAIL_FROM: %:to
:MAIL_SUBJECT: %:subject
:MAIL_IN_REPLY_TO: %:message-id
:END:
%?%(noa/org-capture-get-mail-body)\n"
:empty-lines 1
:immediate-finish t)))
#+end_src
#+begin_src elisp
(setopt org-agenda-block-separator nil)
(setopt org-agenda-include-deadlines t)
;; Always start the weekly agenda for a week from today
(setopt org-agenda-start-on-weekday nil)
;; Show week of agenda by default
(setopt org-agenda-span 'week)
(setopt org-agenda-prefix-format '((agenda . " %i %?-12t% s")
(todo . " %i ")
(tags . " %i ")
(search . " %i ")))
#+end_src
The default agenda time grid has thick lines which i don't think add anything to the output. Similarly, the arrow pointing to the current time has a long line which helps me to find it more easily, but by getting rid of the excess of colour and making it bold, i never struggle to find it anyway.
#+begin_src elisp
(setopt org-agenda-time-grid '((daily today require-timed)
(800 1000 1200 1400 1600 1800 2000)
" " " "))
(setopt org-agenda-current-time-string "◀ you are here")
(setopt org-support-shift-select 'always)
#+end_src
Show calendar agenda and todos in the same buffer
#+begin_src elisp
(setopt org-agenda-custom-commands
'(("n" "Todo list and agenda"
((alltodo "")
(agenda "")))))
#+end_src
#+begin_src elisp
;; Make org html export more pleasant by default
(setopt org-export-with-smart-quotes t)
(setopt org-export-with-entities nil)
(setopt org-export-headline-levels 5)
(setopt org-export-with-toc nil)
(setopt org-export-with-section-numbers nil)
(setopt org-html-doctype "html5")
(setopt org-html-html5-fancy t)
(setopt org-html-container-element "section")
(setopt org-html-divs '((preamble "header" "preamble")
(content "main" "content")
(postamble "footer" "postamble")))
(setopt org-export-with-sub-superscripts t)
(setopt org-html-head-include-default-style nil
org-html-head-include-scripts nil
org-html-validation-link "")
#+end_src
It's a controversial opinion, but i want hard line breaks to only appear because i want a hard line break in the finished document. This is not quite so respectful of the content/presentation distinction, but it makes some things work in a much more pleasant way for me.
#+begin_src elisp
(setopt org-export-preserve-breaks t)
#+end_src
This doesn't work with code blocks at the moment.
#+begin_src elisp
(setopt org-html-indent nil)
#+end_src
#+begin_src elisp
(global-set-key (kbd "C-c c") #'org-capture)
(defun noa/org-agenda ()
"Directly open the agenda with todo list."
(interactive)
(org-agenda nil "n"))
(global-set-key (kbd "C-c a") #'noa/org-agenda)
(global-set-key (kbd "C-c l") #'org-store-link)
#+end_src
#+begin_src elisp :tangle no
;; (package-ensure 'org-ql)
;; (package-ensure 'org-super-links)
;; (setopt org-super-links-search-function #'helm-org-ql)
#+end_src
#+begin_src elisp :tangle no
(package-ensure 'org-modern)
(add-hook 'org-mode-hook #'org-modern-mode)
;; (add-hook 'org-agenda-finalize-hook #'org-modern-agenda)
#+end_src
There are three ways to make bullet lists in org mode, which seems a bit excessive to me. I almost always only use the hyphen, but i like my bullet points to look like bullets, so here i overwrite the hyphen display to show a bullet point. While i'm at it, i overwrite the others too, because they are functionally identical so should probably look the same too.
#+begin_src elisp
(setopt org-modern-list '((?+ . " • ")
(?- . " • ")
(?* . " • ")))
#+end_src
This doesn't respect my timestamp custom format configured above.
#+begin_src elisp
(setopt org-modern-timestamp nil)
#+end_src
#+begin_src elisp
(setopt org-modern-star nil)
(setopt org-modern-keyword nil)
(setopt org-modern-checkbox '((88 . "☑")
(45 . #("□–" 0 2
(composition
((2)))))
(32 . "□")))
(setopt org-modern-table nil)
(setopt org-modern-block-name nil)
(setopt org-modern-internal-target nil)
(setopt org-modern-tag nil)
#+end_src
#+begin_src elisp
(setopt org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
#+end_src
*** Nicer org-mode tables
#+begin_src elisp
(package-ensure 'valign)
(add-hook 'markdown-mode-hook #'valign-mode)
(add-hook 'org-mode-hook #'valign-mode)
(setopt valign-fancy-bar t)
(setopt valign-max-table-size 0)
#+end_src
** Markdown
#+begin_src elisp
(package-ensure 'markdown-mode)
(add-to-list 'auto-mode-alist
'("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode))
#+end_src
** Autocorrection
Abbrev mode expands one string into another string. I use it as a simple autocorrect mode. If i misspell a word, i run C-x a i g which will prompt me for what to expand the previous word into. I type the correct spelling, and whenever i make that mistake again, it will automatically be corrected. It's important to be careful not to set something that could be a typo for two words though, because otherwise it gets even more annoying. Luckily it's easy to update the abbrevs which are stored in ~/.config/emacs/abbrev_defs. M-x list-abbrevs is also a nice command which shows all the saved abbrevs and how many times they've been expanded.
#+begin_src elisp
(add-hook 'text-mode-hook #'abbrev-mode)
#+end_src
** Spellcheck
Jinx is a package for spellchecking. Previously i used [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Spelling.html][flyspell]], which is built in, and [[https://codeberg.org/ideasman42/emacs-spell-fu][spell-fu]]. Flyspell is not ideal because it only checks the word under the point. Furthermore, the correction interface is not pleasant to look at with a proportional font as it uses spaces to align the candidates. Spell-fu checks all the words that are visible, thereby behaving much more like a traditional spell checker. Jinx improves on spell-fu by interacting with the spellcheck process in a more efficient way, and has a nicer interface to corrections.
#+begin_src elisp
(package-ensure 'jinx)
(setopt global-jinx-mode t)
#+end_src
Replace the default spellcheck binding with jinx
#+begin_src elisp
(keymap-global-set "M-$" #'jinx-correct)
(keymap-global-set "C-M-$" #'jinx-languages)
#+end_src
*** Add corrected words to abbrev
This is a cool function i took from the jinx wiki. It automatically creates an abbrev for words i correct, so if i make the same error again, it gets fixed without me having to do anything!
#+begin_src elisp
(defun jinx--add-to-abbrev (overlay word)
"Add abbreviation to `global-abbrev-table'.
The misspelled word is taken from OVERLAY. WORD is the corrected word."
(let ((abbrev (buffer-substring-no-properties
(overlay-start overlay)
(overlay-end overlay))))
(message "Abbrev: %s -> %s" abbrev word)
(define-abbrev global-abbrev-table abbrev word)))
(advice-add 'jinx--correct-replace :before #'jinx--add-to-abbrev)
#+end_src
*** Consistent face on hover
Almost everywhere else, the ~highlight~ face is used for interactive text on mouse over. However, jinx reuses its own ~jinx-highlight~ face, which i don't really like. This face clashes with the face i use for the active region, which means selecting text across a misspelled word can be a bit confusing. If i change the ~jinx-highlight~ face, it looks wrong when i've activated jinx to correct the word.
#+begin_src elisp
(put 'jinx-overlay 'mouse-face '(jinx-misspelled highlight))
#+end_src
I mentioned that i thought this wasn't the best default behaviour [[https://github.com/minad/jinx/discussions/184][in the jinx repository]], but Daniel seems happy with it. I appreciate that he's made it so easy to change the behaviour to something i prefer.
* My website
I generate my website using org mode. I use the built in ox-publish subsystem for convenience. However, it isn't all that convenient, as it doesn't actually work very well at the moment.
#+begin_src elisp
(defun read-file-as-string (filename)
"Read file contents from FILENAME."
(with-temp-buffer
(insert-file-contents filename)
(buffer-string)))
#+end_src
#+begin_src elisp
;; (setq noa/website-header (read-file-as-string "/home/noa/projects/org-website/templates/header.html"))
(setq noa/website-footer "noa.pub ·
cc by-sa ·
feed ·
archive ·
friends
")
#+end_src
#+begin_src elisp
(setq org-publish-project-alist
`(("website"
:components ("website-pages" "website-assets" "website-feed"))
("website-pages"
:publishing-function org-html-publish-to-html
:base-directory "~/data/website"
:publishing-directory "~/data/public_html"
:base-extension "org"
:with-drawers t
:html-link-home "/"
:html-head-include-default-style nil
:html-head-include-scripts nil
:html-doctype "html5"
;; :html-validation-link nil
:html-preamble ""
:html-postamble ,noa/website-footer
:html-home/up-format ""
:html-link-up ""
:html-html5-fancy t
:html-indent nil ;; breaks tag
:html-head "
"
:auto-sitemap t
:sitemap-filename "~/data/website/archive.org"
:sitemap-title "Archive"
:sitemap-style list
:sitemap-sort-folders ignore
:sitemap-ignore-case t
)
("website-assets"
:publishing-function org-publish-attachment
:base-directory "~/data/website"
:publishing-directory "/home/noa/data/public_html"
:base-extension "css\\|js\\|png|\\jpg|\\ico"
:recursive t)
("website-feed"
:base-directory "~/data/website"
:base-extension "org"
:publishing-directory "~/data/public_html"
:publishing-function (org-rss-publish-to-rss)
:html-link-home "https://noa.pub/"
:html-link-use-abs-url t
:exclude ".*"
:include ("index.org"))))
#+end_src
We also need to install a newer version of htmlize so that we can properly convert syntax highlighted code into html. I wish there was a way to turn this off, but i couldn't easily find it, so we're stuck with it for now.
#+begin_src elisp
(package-ensure 'htmlize)
#+end_src
* Completing-read everywhere with consult
Consult is a package to provide navigation commands that take advantage of completing-read. I set up a nice completing-read environment earlier with vertico. There are a lot of commands built in to consult, and it's possible to define more. But i use it very simply.
#+begin_src elisp
(package-ensure 'consult)
(package-activate 'consult)
#+end_src
Consult buffer can be used instead of the default buffer menu. It lists recently used files and bookmarks as well as open buffers.
#+begin_src elisp
(autoload #'consult-buffer "consult" nil t)
(global-set-key [remap switch-to-buffer] #'consult-buffer)
#+end_src
These are some other almost default functions but with extra interactivity.
#+begin_src elisp
(global-set-key [remap yank-pop] #'consult-yank-pop)
(global-set-key [remap goto-line] #'consult-goto-line)
(global-set-key [remap imenu] #'consult-imenu)
(with-eval-after-load 'org
(bind-key [remap imenu] #'consult-org-heading #'org-mode-map))
#+end_src
#+begin_src elisp
(global-set-key [remap info] #'consult-info)
#+end_src
* Annotations for completing-read
Marginalia provides us with annotations for candidates in completing read functions. This is things like docstrings for functions, file permissions in find-file, and so on. It's a small quality of life improvement.
#+begin_src elisp
(package-ensure 'marginalia)
(setopt marginalia-mode t)
#+end_src
We want to always show the relative age of a file. By default, files which haven't been modified for more than two weeks will display an absolute date.
#+begin_src elisp
(setopt marginalia-max-relative-age most-positive-fixnum)
#+end_src
My keyboard has a tab key and an i key. For legacy reasons, by default emacs converts C-i to mean the same thing as the tab key, but i don't really want that. The tab key is called and it gets translated to TAB. C-i is TAB, but i'd rather it by C-i. That's what this decode line does.
#+begin_src elisp
(define-key input-decode-map [?\C-i] [C-i])
#+end_src
Now that tab and C-i are properly distinguished, i can bind C-i to completion at point.
#+begin_src elisp
(global-set-key (kbd "") 'completion-at-point)
#+end_src
I also want to make the completion at point function a bit more friendly than the default, so i ask consult to provide the completion functionality.
#+begin_src elisp
(setopt completion-in-region-function 'consult-completion-in-region)
#+end_src
* Getting help
Emacs is great because of its great built in help system!
** Close help buffers more easily
By default, opening a help buffer keeps the original window active, but this means that i have to go into the help buffer to close it. Some people might like to always have the help buffer hanging around, but i like my screen empty of distractions. Luckily there's a simple setting to select the help window so that i can easily close it again with q.
#+begin_src elisp
(setopt help-window-select t)
#+end_src
Also to that end, we use the same window as the help buffer to open source files.
#+begin_src elisp
(setopt help-window-keep-selected t)
#+end_src
** Better aesthetics
Help buffers put quotes around links in the buffer. I don't think this adds anything to the output.
#+begin_src elisp
(setopt help-clean-buttons t)
#+end_src
The arguments are shown in upper case. I don't want to be shouted at! I already define them to be bold, this is all the attention they need.
#+begin_src elisp
(setopt help-downcase-arguments t)
#+end_src
** Edit variables from help buffers
Often, i will look at the documentation for a variable and immediately want to play with it. This removes one step between wanting that and being able to do that.
#+begin_src elisp
(setopt help-enable-variable-value-editing t)
#+end_src
* Web browsing in emacs
I hear it's now possible to run a full fat browser inside emacs. But this is surely quite heavyweight, and doesn't get to take advantage of things like ublock origin. When it comes to alternative browsers, it's always the same story; i like the concept, but most websites are a horrific experience without a good ad blocker.
Instead, i use eww, a browser more closely aligned with browsers for the terminal. Despite the name, eww is a delight to use for text-heavy websites. If a website doesn't render well in it, because it uses fancy layout tricks or lots of javascript, we can press ~&~ to open the url in firefox.
#+begin_src elisp
(setopt browse-url-browser-function 'eww-browse-url
browse-url-secondary-browser-function 'browse-url-default-browser)
#+end_src
For the kind of sites i use eww to visit, i've not had a use for cookies. We can tell emacs that we don't trust cookies from any sites, we don't trust cookies from all sites, and frankly, we don't want to use cookies.
#+begin_src elisp
(setopt url-cookie-trusted-urls '()
url-cookie-untrusted-urls '(".*")
shr-cookie-policy nil)
#+end_src
Eww has rudimentary support for colours. But i don't want web pages to be able to specify their own colours, because i like the colours i already have set.
#+begin_src elisp
(setopt shr-use-colors nil)
#+end_src
Shr has the ability to break paragraphs to fit on the screen. Instead of this, we set it to not break any lines, and use visual-fill-column-mode to do this for us instead.
#+begin_src elisp
(setopt shr-max-width nil)
#+end_src
We can set what the maximum size of an image in a window should be. This is a fraction of the total window width or height, and if the image would be bigger than this, it'll be resized to fit. It's useful to have it smaller because emacs still sort of chokes on scrolling when there are large images in a buffer. This is the default value of this option.
#+begin_src elisp
(setopt shr-max-image-proportion 0.9
shr-discard-aria-hidden t)
#+end_src
#+begin_src elisp
(setopt shr-bullet " • ")
#+end_src
The default name for the eww buffer is *eww*. This is unhelpful because it makes having more than one eww buffer open a bit of a chore to navigate. We can set it to 'url, 'title, or a function. I set it to 'title because marginalia already shows me the url. However, this means that i can't search for a url name when switching buffers. See the help for this variable for an example of a function which gives the page title and the url.
#+begin_src elisp
(setopt eww-auto-rename-buffer 'title)
#+end_src
Goto address mode makes urls and email address in a buffer clickable. I want these clickable links to look like links, because that's what they are. The two mouse face variables are what face is used on hover, which at the moment i ignore. It might also be worth setting them to 'highlight.
#+begin_src elisp
(setopt global-goto-address-mode t)
(setopt goto-address-mail-face 'link)
(setopt goto-address-mail-mouse-face 'highlight)
(setopt goto-address-url-face 'link)
(setopt goto-address-url-mouse-face 'highlight)
#+end_src
#+begin_src elisp
(setopt global-eldoc-mode t)
#+end_src
Use a bar cursor and blink it and don't stop blinking it. i don't know how i feel about this yet to be honest, but it helps me know which window is active so for now i'm keeping it
#+begin_src elisp
(setopt
cursor-type 'bar
blink-cursor-mode t
blink-cursor-interval 0.7)
#+end_src
* Dired
Dired is a really nice package which, as with a lot of emacs, has some dodgy defaults. Here we round off some of the sharp edges to make it more enjoyable to use.
By default, dired permanently deletes files. But i have quite a bit of storage and also make bad decisions regularly, so it seems fitting to make use of the wonderful invention that is the trash. People who have used systems from the last forty years or so will likely be familiar with this innovation.
#+begin_src elisp
(setopt delete-by-moving-to-trash t)
#+end_src
It's not fun to be asked every time whether we want to delete a directory recursively. It's an understandable default for safety reasons, but because we are not deleting permanently but rather just moving to the trash, it's not such a concern.
#+begin_src elisp
(setopt dired-recursive-deletes 'always)
#+end_src
Recursive copying isn't even destructive, so i definitely don't want to be asked about that.
#+begin_src elisp
(setopt dired-recursive-copies 'always)
#+end_src
After we delete some files or directories, it makes sense to get rid of any buffers which are looking at those files or directories.
#+begin_src elisp
(setopt dired-clean-up-buffers-too nil)
#+end_src
With this set, if we have two dired buffers open next to one another, a rename operation in one will default to the directory shown in the other. In this way, we can pretend we are using some kind of norton commander like file browser instead of slumming it in emacs.
#+begin_src elisp
(setopt dired-dwim-target t)
#+end_src
These are some useful ls switches. We have to keep -l. To show dotfiles as well, we use -a. To sort numbers by number order instead of lumping together ones, twos, and so on, we use -v. Because we don't have colour, it's nice to have a clear indicator of what is a file and what is a directory, as well as other different things like symlinks which i never remember. By using -F, a forward slash is appended to every directory. And to get more easily understandable file sizes, we use -h, which will tell us the file size in kilobytes or megabytes rather than a huge number that means nothing to me. I won't explain the meaning of the long flag.
#+begin_src elisp
(setopt dired-listing-switches "-alvFh --group-directories-first")
#+end_src
By default, don't show dired details
#+begin_src elisp
(add-hook 'dired-mode-hook #'dired-hide-details-mode)
#+end_src
I find it useful to see the recursive sizes of directories. This can be a little slow, so setting it as always on might not be the best idea, but the longest i've had to wait is about a second, and that's only if i run it on my home directory, so i think it's worth it at the moment.
#+begin_src elisp
(package-ensure 'dired-du)
(setopt dired-du-size-format t)
(add-hook 'dired-mode-hook #'dired-du-mode)
#+end_src
* Behaviour
** Switching buffers
There is a distinction in emacs between manual buffer switching that i initiate, and automatic buffer switching when emacs wants to show a buffer. In practice this means that any rules i write for where to display buffers get ignored if i try to show that buffer myself. This line changes that behaviour.
#+begin_src elisp
(setopt switch-to-buffer-obey-display-actions t)
#+end_src
** History
#+begin_src elisp
(setopt history-length 250
kill-ring-max 25)
#+end_src
#+begin_src elisp
(setopt savehist-file "~/.config/emacs/savehist")
(setopt savehist-additional-variables
'(kill-ring
command-history
set-variable-value-history
custom-variable-history
query-replace-history
read-expression-history
minibuffer-history
read-char-history
face-name-history
bookmark-history
file-name-history))
(setopt savehist-mode t)
#+end_src
#+begin_src elisp
(setopt window-divider-mode t)
(setopt window-divider-default-right-width 1)
(setopt window-divider-default-bottom-width 1)
(setopt window-divider-default-places t)
#+end_src
Taken from configuration for the vertico stack:
Add prompt indicator to `completing-read-multiple'.
We display [CRM], e.g., [CRM,] if the separator is a comma.
#+begin_src elisp
(defun crm-indicator (args)
(cons (format "[CRM%s] %s"
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
#+end_src
Do not allow the cursor in the minibuffer prompt
#+begin_src elisp
(setopt minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
(setopt battery-mode-line-format "🔋%p%b ")
(setopt display-battery-mode t)
(setopt display-time-default-load-average nil)
(setopt display-time-24hr-format t)
(setopt display-time-mode t)
#+end_src
Support opening new minibuffers from inside existing minibuffers.
#+begin_src elisp
(setopt enable-recursive-minibuffers t)
#+end_src
Whether to drop into the debugger on any error. This seems cool, but in practice is a bit annoying.
#+begin_src elisp
(setopt debug-on-error nil)
#+end_src
Hide commands in M-x which do not work in the current mode.
#+begin_src elisp
(setopt read-extended-command-predicate 'command-completion-default-include-p)
#+end_src
#+begin_src elisp
(setopt recentf-max-menu-items 25
recentf-save-file "~/.config/emacs/recentf"
recentf-mode t
bookmark-default-file "~/.config/emacs/bookmarks")
#+end_src
** Undo
Undo is on C-/ and redo is on C-S-/. It's not standard, but these bindings are easier to remember. And with this setting, it behaves for the most part like undo in other programs, which isn't as good as i'd really want, but is something i can reason about much more easily than the default undo.
#+begin_src elisp
(setopt undo-no-redo t)
#+end_src
** Saving
Backups are pointless in long emacs sessions imo, but autosaves are useful.
#+begin_src elisp
(setopt remote-file-name-inhibit-auto-save t)
(setopt remote-file-name-inhibit-auto-save-visited t)
(setopt backup-directory-alist '(("." . "~/.config/emacs/backups/"))
make-backup-files nil
backup-by-copying t
create-lockfiles nil
auto-save-mode t
auto-save-interval 6 ;; every six keystrokes
auto-save-timeout 5 ;; every 5 seconds
auto-save-default t
auto-save-no-message t
save-silently t
version-control t
;; this will auto save to the current file
auto-save-visited-mode t)
(add-hook 'focus-out-hook (lambda () (interactive) (save-some-buffers t)))
(add-hook 'mouse-leave-buffer-hook (lambda () (interactive) (save-some-buffers t)))
#+end_src
** Unfill commands
#+begin_src elisp
(defun unfill-paragraph ()
"Takes a multi-line paragraph and makes it into a single line of text."
(interactive)
(let ((fill-column (point-max)))
(fill-paragraph nil)))
(global-set-key (kbd "M-Q") #'unfill-paragraph)
#+end_src
The following functions were written by acdw for use with buffers like dict and help, where it's often ugly to read them with their hard wrapping
#+begin_src elisp
;; unfill/refill a buffer
;; unfill makes all paragraphs 1 line
(defun unfill-region (beg end)
(let ((fill-column most-positive-fixnum))
(fill-region beg end)))
;; this command is what you'd run in a hook
;; visual-line-mode makes it so it doesn't look shit
(defun unfill-buffer-force ()
(interactive)
(let ((buffer-read-only nil))
(unfill-region (point-min) (point-max))
(visual-line-mode)))
;; refill makes the width equal to the window-width minus 2
;; (you could change it ofc)
(defun refill-region (beg end)
(let ((fill-column (- (window-width) 2)))
(fill-region beg end)))
;; this command is what you'd run in a hook or w/e
(defun refill-buffer-force ()
(interactive)
(let ((buffer-read-only nil))
(refill-region (point-min) (point-max))))
#+end_src
** Better control l
C-l goes in order, rather than first centering the cursor. This is particularly pleasant with a ~scroll-margin~ greater than the default of zero, which serves to keep a line of context at each edge of the screen, as well as triggering a scroll when the point is that far away from the screen edge.
#+begin_src elisp
(setopt recenter-positions '(top middle bottom))
(setopt scroll-margin 1)
#+end_src
** Smooth scrolling
Emacs uses choppy scrolling by default. If i scoll with my trackpad, it's nice to have it move tiny amounts at the same time as my fingers, which pixel-scroll-precision-mode allows for. This also has the benefit of making scrolling over images a little bit of a nicer experience.
#+begin_src elisp
(setopt pixel-scroll-precision-mode t
pixel-scroll-precision-use-momentum t)
#+end_src
** Remember my position in files
#+begin_src elisp
(setopt save-place-mode t)
#+end_src
** More pleasant prompts
The former means that when given a list of choices, we can use single character abbreviations to answer. The latter is a fancy way of defaliasing yes-or-no-p to y-or-n-p.
#+begin_src elisp
(setopt read-answer-short t)
(setopt use-short-answers t)
#+end_src
** Disable disabled commands
#+begin_src elisp
(setq disabled-command-function nil)
#+end_src
** Don't save changes in the customize interface
#+begin_src elisp
(setopt custom-file (make-temp-file "custom"))
#+end_src
** Scrolling in compilation
Scroll along with text in compilation mode, and stop scrolling at the first error.
#+begin_src elisp
(setopt compilation-scroll-output 'first-error)
#+end_src
** Don't advertise gnu on startup
#+begin_src elisp
(setq inhibit-startup-echo-area-message "noa") ;; #userfreedom
#+end_src
** Better buffer naming
#+begin_src elisp
(setopt uniquify-after-kill-buffer-p t
uniquify-buffer-name-style 'forward
uniquify-ignore-buffers-re "^\\*"
uniquify-separator "/")
#+end_src
** Emacs server
#+begin_src elisp :tangle no
(unless (server-running-p) (server-start)))
#+end_src
** Ignore the bell
Ignore the bell.
#+begin_src elisp
(setopt ring-bell-function 'ignore)
#+end_src
** Automatic help at point
This puts some help in the minibuffer when we leave the point on some interactive text.
#+begin_src elisp
(setopt help-at-pt-display-when-idle 'never)
#+end_src
** Long lines
Better support for long lines.
#+begin_src elisp
(setopt global-so-long-mode t)
#+end_src
** Kill processes without asking
This will stop us being prompted before killing a buffer with a running process:
#+begin_src elisp
(setopt kill-buffer-query-functions
(remq 'process-kill-buffer-query-function
kill-buffer-query-functions))
#+end_src
** Automatically revert buffers
Automatically revert buffers when they change on disk. This doesn't apply to tramp.
#+begin_src elisp
(setopt global-auto-revert-mode t)
#+end_src
** Visit symlinks
This behaviour changes how we visit symlinks.
#+begin_src elisp
(setopt find-file-visit-truename t)
(setopt vc-follow-symlinks 'ask)
#+end_src
** Ibuffer
Use ibuffer instead of list-buffers
#+begin_src elisp
(global-set-key [remap list-buffers] 'ibuffer)
#+end_src
** Clicking around
By default, clicking on a character will always put the point in front of that character. But it generally feels nicer for a click to put the point on the nearest side of the character to where the mouse clicked, to allow for slightly sloppier clicking.
#+begin_src elisp
(setopt mouse-prefer-closest-glyph t)
#+end_src
* Writing code
** Indentation: tabs and whitespace settings
In general, my rules for inserting tabs are that the tab key should insert tabs. I personally prefer tabs to spaces, because tabs work reasonably well whatever font or tab width one chooses to set, whereas spaces are the same width for everyone, except when someone uses a proportional font in which case they are narrower than expected. Furthermore, people tend to use spaces for alignment, which looks bad when you can't rely on every character being the same width.
However, i'm in the minority, and fighting with the very complicated emacs indentation systems is simply not fun. That said, i refuse to use a monospaced font. Luckily the minority is more than one and someone has already done the hard work for me of writing a mode to make spaces for indentation work reasonably well with a proportional font. That mode is elastic-indent-mode, and it very simply makes leading whitespace characters the same width as the characters on the line above. It's a simple solution but most of the time it does what i want.
#+begin_src elisp
(require 'elastic-indent)
(add-hook 'prog-mode-hook #'elastic-indent-mode)
#+end_src
Elastic-table-mode is similar; for tab characters within lines, ensure that they change width to make subsequent lines form a table-like layout.
#+begin_src elisp
(require 'elastic-table)
(add-hook 'prog-mode-hook #'elastic-table-mode)
#+end_src
Previously i used a function to naïvely copy the whitespace from the line above. This is the way that vi, nano, and acme all implement auto-indentation. However, for now i'm experimenting with using the built-in indentation functions again. I'm leaving this defun here for posterity.
#+begin_src elisp :tangle no
(defun noa/naive-return-and-indent ()
"Insert a newline and copy the indentation of the previous line, vi/nano style."
(interactive)
(open-line 1)
(let* ((start (progn (beginning-of-line) (point)))
(indent (progn (back-to-indentation) (point)))
(end (progn (end-of-line) (point)))
(whitespace (buffer-substring start indent)))
(delete-trailing-whitespace start end)
(beginning-of-line 2)
(insert whitespace)))
#+end_src
We will only be trying to indent at the start of a line, and sometimes we will want to insert a standard tab character. We can also set this option to 'complete, which will run completion at point if the region is already indented.
#+begin_src elisp
(setopt tab-always-indent nil)
#+end_src
Usually, we want indentation to be done with tabs. Some modes make more sense to use spaces to indent. Lisp is a particular example, and emacs's default behaviour of converting tabs into spaces is frankly horrific. I've taken the below code from acdw to use spaces in these modes.
#+begin_src elisp
(defvar space-indent-modes '(emacs-lisp-mode
lisp-interaction-mode
lisp-mode
scheme-mode
python-mode)
"Modes to indent with spaces, not tabs.")
(add-hook 'prog-mode-hook
(defun indent-tabs-mode-maybe ()
(setq indent-tabs-mode
(if (apply #'derived-mode-p space-indent-modes) nil t))))
#+end_src
I want to ensure that indentation is always correct. The builtin electric indent mode works /sometimes/, but the aggressive indent mode package is more reliable.
#+begin_src elisp
(package-ensure 'aggressive-indent)
(setopt global-aggressive-indent-mode t)
#+end_src
* Sentences
I prefer to double space sentences. But it seems that most other people do not, and the sentence navigation commands still work for my sentences with this set to nil, but don't work for other people's with it set to t. There are of course some little errors with this, like ending a title with a full stop, but for the most part it's fine.
#+begin_src elisp
(setopt sentence-end-double-space nil)
#+end_src
If i write a script, i will always run chmod +x after saving it. This command means i don't have to do that.
#+begin_src elisp
(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
#+end_src
We are on a unix system, so it makes sense to end files in the unix system way. I'm surprised this isn't the default.
#+begin_src elisp
(setopt require-final-newline t)
#+end_src
#+begin_src elisp
(setopt window-min-height 1
window-combination-resize t
window-resize-pixelwise t
frame-resize-pixelwise t)
#+end_src elisp
* Tramp
Some tramp settings.
#+begin_src elisp
(setopt remote-file-name-inhibit-locks t)
(setopt tramp-inline-compress-start-size 1000)
(setopt tramp-verbose 3)
;; (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
#+end_src
The version control system will try each of these methods in order. Because almost everything source controlled i do uses git, i put it first in the list. But at the moment, because i don't think i actually use any of the other methods, i remove the rest of them from the list.
#+begin_src elisp
(setopt vc-handled-backends '(Git))
;; (setopt vc-handled-backends '(Git RCS CVS SVN SCCS SRC Bzr Hg))
#+end_src
It seems that tramp can also be made faster with these .ssh/config settings.
#+begin_src text :tangle no
Host *
ControlMaster auto
ControlPath ~/.ssh/master-%h:%p
ControlPersist 10m
ForwardAgent yes
ServerAliveInterval 60
#+end_src
* I-ching
I don't know why but it seems cool
#+begin_src elisp
;; (package-ensure 'i-ching)
#+end_src
* Keybindings
** Zap up to char
#+begin_src elisp
(global-set-key (kbd "M-z") 'zap-up-to-char)
#+end_src
* Search in buffer
Isearch is good, but it has some rough edges. The easiest way forward was just to use ctrlf, which fixes most of them. But i still had some gripes with ctrlf, like that it doesn't play well with a lot of other commands and packages and the general ecosystem built around isearch. So i've tried to fix as many of the issues as i can while keeping real isearch.
It makes more sense to go to the start of the match, because i start searching where i want to be.
#+begin_src elisp
(defun isearch-exit-at-front ()
"always exit isearch, at the front of search match."
(interactive)
(isearch-exit)
(when isearch-forward
(goto-char isearch-other-end)))
(defun isearch-exit-at-end ()
"Always exit isearch, at the end of search match."
(interactive)
(isearch-exit)
(when (not isearch-forward)
(goto-char isearch-other-end)))
#+end_src
My preferred behaviour is for the point to be at the start of the match. Because the search is incremental, usually i won't finish typing something useful before exiting the search, but i always start searching at a place i can reason about. However, i can't figure out how to get this to work along with isearch-mb
#+begin_src elisp
;; (define-key isearch-mb-minibuffer-map (kbd "") #'isearch-exit-at-front)
;; (define-key isearch-mb-minibuffer-map (kbd "C-") #'isearch-exit-at-end)
;; Make isearch always quit on C-g
(define-key isearch-mode-map (kbd "C-g") #'isearch-cancel)
(define-key isearch-mode-map (kbd "C-o") #'isearch-occur)
(setopt search-whitespace-regexp ".*?")
(setopt isearch-lax-whitespace t)
(setopt isearch-lazy-count t)
(setopt isearch-allow-motion t)
(setopt isearch-repeat-on-direction-change t)
(setopt isearch-wrap-pause 'no)
#+end_src
Okay, i lied about being vanilla. I want isearch to use the minibuffer like a good normal part of emacs
#+begin_src elisp
(package-ensure 'isearch-mb)
(setopt isearch-mb-mode t)
#+end_src
#+begin_src elisp
(global-set-key (kbd "M-o") 'other-window)
(global-set-key (kbd "C-x k") 'kill-current-buffer)
#+end_src
** Smart scan
This is a nice package to easily jump between identical things at point, like the * operator in vim. I disabled it because i'm trying out using the same keybindings to jump between compile mode errors.
#+begin_src elisp :tangle no
(use-package smartscan)
(global-set-key (kbd "M-n") #'smartscan-symbol-go-forward)
(global-set-key (kbd "M-p") #'smartscan-symbol-go-backward)
(global-set-key (kbd "M-n") #'next-error)
(global-set-key (kbd "M-p") #'previous-error)
#+end_src
** Jef Raskin's leap
As a concept, i really like the canon cat. As described [[https://news.ycombinator.com/item?id=33286408][here]], it has two leap keys to navigate the buffer. [[https://dercuano.github.io/notes/eink-design.html][According to Kragen Javier Sitaker]], you could move side to side with the arrow keys, but not up and down. The idea was that any up and down movement would be better served by incremental search. This is something that i want to explore.
#+begin_quote
If you press and release the LEAP key, it advances the cursor one character forward (or backwards if you hit the left leap key.)
If you press down (but do not release) the LEAP key you enter a search semi-mode. As you type a search term in this semi-mode, the cursor moves to the first instance of that search term it finds. After moving to the first instance of the search term, you release the leap key to exit the search semi-mode.
If you want to move the cursor to a subsequent instance of the search term, you press (and do not release) the "USE FRONT" key and press the leap key again (whose key front is labeled "Leap Again.")
#+end_quote
Semi-modes seem hard to implement in emacs, although in a lot of ways they are already here. I am not a fan of modes in the vi-sense at all, and isearch in emacs is a mode like this. That is, keybindings work differently when isearching, and you have to press C-g or RET to go back to the normal editing flow.
** Replace
Anzu provides real-time updates to the replacement string when running query-replace.
#+begin_src elisp
(package-ensure 'anzu)
(global-set-key [remap query-replace] 'anzu-query-replace)
(global-set-key [remap query-replace-regexp] 'anzu-query-replace-regexp)
#+end_src
My computer has a small screen, so i find that it's more beneficial for me to split the frame into columns, so i get more context. However, splitting in this way only gives me a (window-width) of 61, so emacs will always split into vertically stacked windows. By setting this to 80, the first split should always be vertical.
#+begin_src elisp
(setopt split-width-threshold 80)
#+end_src
Define a handy function that allows me to do a full text search of every file in my home directory. For the most part, this works well; ripgrep avoids binary files. However, in some files with embedded images, it can add a lot of junk to the output.
#+begin_src elisp
(defun noa/consult-rg-home ()
(interactive)
(consult-ripgrep "~/"))
(global-set-key (kbd "M-") #'noa/consult-rg-home)
#+end_src
#+begin_src elisp
(setopt shell-file-name "/bin/sh")
#+end_src
#+begin_src elisp
(defun snarf-song (url)
(interactive "sYoutube url:")
(async-shell-command
(concat "yt-dlp -x --audio-format=mp3 -o "
(shell-quote-argument "~/media/music/%(title)s [%(id)s].%(ext)s")
" "
(shell-quote-argument url))))
#+end_src
Put a quote in the scratch buffer
#+begin_src elisp
(setopt cookie-file "~/data/quotes")
(setopt initial-scratch-message
(concat (with-temp-buffer
(emacs-lisp-mode)
(insert (cookie cookie-file))
(mark-whole-buffer)
(comment-region (mark) (point))
(buffer-substring (mark) (point)))
"\n\n"))
#+end_src
* Eshell
Eshell is a command shell written in elisp. It integrates with emacs in a more consistent manner than the other shells available, although it still has its own quirks.
In general, i try and avoid using a shell if possible, because i think that bespoke emacs interfaces to different commands tend to have more pleasant interaction methods. But there are still lots of things which are simply easier to do with a shell.
** Set the eshell banner
This is equivalent to the message of the day present in some shells. I wanted it to print a new quote every time eshell opened, but when i tried that eshell refused to load. Probably some mistake on my end.
#+begin_src elisp
(defun noa/eshell-banner-message ()
(concat (cookie cookie-file)
"\n\n"))
(setopt eshell-banner-message (noa/eshell-banner-message))
#+end_src
** Environment variables
#+begin_src elisp
(setenv "PAGER" "cat")
(setenv "TERM" "dumb")
(setenv "NO_COLOR")
(setenv "GPG_AGENT_INFO" nil)
#+end_src
* Password management
I mostly don't use emacs for passage management. Instead i use the wonderful [[https://keepassxc.org/][keepassxc]]. Keepassxc has many great features i make use of, including one time passwords, an ssh agent, and checking if my password appears in leaks.
I used keepassxc for over a year, before deciding to throw the towel in and move to bitwarden for the convenience of having my passwords easily on multiple devices. It turns out that things i took for granted with keepassxc, like not having to pay for one time password support, the ssh agent, browser integration that worked well all the time, and a decent desktop application, were not present in bitwarden. Instead of running back to bitwarden, i moved again, first to pass, the so-called standard unix password manager, and then to passage, which does the same thing but uses age encryption instead of the fire-engine-on-fire that is gpg.
The motivation for this move was because i wanted to start using a hardware token as extra protection for the passwords. But sadly, the infrastructure around this solution just isn't there and it became a pile of jank. I stopped accessing my email and xmpp from my computer, because it felt like too much of a headache.
And so i'm back with keepassxc. Luckily keepassxc supports the secret service api, and so does emacs's very own auth-source package, so everything should be perfect. Right?
Not quite. The keys that auth-source expects don't quite align with the keys that keepassxc has. So any passwords i want emacs to be able to easily deal with have to be moved around a little. Luckily the passwords key itself is just where it should be, but i had to go the advanced options in my keepassxc entry and add things in the :user, :host, and :port slots. I probably won't be updating these very often, so it's not a big deal. But i'm writing it here because otherwise i will forget.
#+begin_src elisp
(require 'secrets t nil)
;; (secrets-open-session)
(setopt auth-sources '("secrets:Passwords"))
(auth-source-forget-all-cached)
#+end_src
* Other
#+begin_src elisp
(setopt confirm-kill-emacs 'y-or-n-p)
#+end_src
#+begin_src elisp
(global-set-key (kbd "C-=") #'calc)
#+end_src
* Version control
I don't use magit. I tried it once, but my use of version control is very limited to just making some changes and then committing them. I don't work in programming and my projects are simple.
* Email
I like to have my email offline. Of course my preference is also to have it inside of emacs for consistency with everything else. Currently i use mu4e as my mail user agent, which relies on an external tool. I also use two small command line programs to download and send mail.
#+begin_src sh :tangle no
sudo apt install maildir-utils isync msmtp
#+end_src
** Fetching mail with mbsync
Mbsync is downloaded with the isync package. The configuration is not complicated.
I use fastmail to host my email.
#+begin_src text :tangle no
IMAPAccount fastmail
Host imap.fastmail.com
User noa@gaiwan.org
#+end_src
The ~passcmd~ is run to fetch my password. I should put this into pass, but this isn't an ordinary password. It's an app password, and needs to be updated on every new system.
#+begin_src text :tangle no
PassCmd "cat ~/.config/mbsync-pass"
#+end_src
#+begin_src text :tangle no
SSLType IMAPS
IMAPStore fastmail-remote
Account fastmail
MaildirStore fastmail-local
SubFolders Verbatim
Path "~/mail/"
Inbox "~/mail/Inbox"
Channel fastmail
Far :fastmail-remote:
Near :fastmail-local:
Patterns *
Create Near
Expunge Near
SyncState *
CopyArrivalDate yes
#+end_src
** Reading mail with mu4e
#+begin_src elisp
(setopt mu4e-headers-skip-duplicates t)
(setopt mu4e-view-show-images t)
(setopt mu4e-view-show-addresses t)
#+end_src
In theory i like format flowed, but what i like even more is just not hard wrapping messages and dealing with hacks to get around that to begin with. If software supported soft wrapping at an arbitrary column, the world would be a better place.
#+begin_src elisp
(setopt mu4e-compose-format-flowed nil)
(setopt mu4e-change-filenames-when-moving t)
(setopt mu4e-use-fancy-chars nil)
(setopt mu4e-confirm-quit nil)
(setopt mu4e-headers-leave-behavior 'apply)
(setopt mu4e-headers-precise-alignment t)
(setopt mu4e-headers-fields '((:flags . 6)
(:from . 32)
(:subject)))
(setopt mu4e-search-threads nil)
(setopt mu4e-hide-index-messages t)
(setopt mu4e-get-mail-command "mbsync -c ~/.config/mbsyncrc fastmail")
(setopt mu4e-maildir "~/mail")
(setopt mu4e-drafts-folder "/Drafts")
(setopt mu4e-sent-folder "/Sent")
(setopt mu4e-refile-folder "/Archive")
(setopt mu4e-trash-folder "/Trash")
(setopt mu4e-bookmarks '((:name "Inbox" :query "maildir:/Inbox" :key ?i)
(:name "Feeds" :query "maildir:/Feeds" :key ?f)
(:name "Paper trail" :query "\"maildir:/Paper trail\"" :key ?p)))
#+end_src
function to move mails to trash
#+begin_src elisp
(with-eval-after-load 'mu4e
(bind-key (kbd "d") #'noa/move-to-trash 'mu4e-headers-mode-map)
(bind-key (kbd "d") #'noa/move-to-trash 'mu4e-view-mode-map)
(fset 'noa/move-to-trash "mTrash"))
#+end_src
The first string is for when fancy characters are disabled, and the second is for when they are enabled. But i set them all manually because some of the other characters are ugly in fancy mode, but i like my unicode thread icons.
#+begin_src elisp
(setq mu4e-headers-thread-connection-prefix '("│ " . "│ ")
mu4e-headers-thread-last-child-prefix '("└ " . "└ ")
mu4e-headers-thread-blank-prefix '(" " . " ")
mu4e-headers-thread-root-prefix '("□ " . "□ ")
mu4e-headers-thread-child-prefix '("│ " . "│ ")
mu4e-headers-thread-orphan-prefix '("♢ " . "♢ ")
mu4e-headers-thread-duplicate-prefix '("≡ " . "≡ ")
mu4e-headers-thread-first-child-prefix '("⚬ " . "⚬ ")
mu4e-headers-thread-single-orphan-prefix '("♢ " . "♢ "))
#+end_src
Setting this to nil stops auto-fill from being automatically enabled in message buffers.
#+begin_src elisp
(setopt message-fill-column nil)
#+end_src
It's nice to have a message signature. I want the signature to be loaded from a file, which is store in my configuration directory.
#+begin_src elisp
(setopt message-signature t)
(setopt message-signature-file "~/.config/signature")
#+end_src
The contents of the signature file is listed below. Currently i only have two bullet points, when i think three would look nicer, but i don't have three things to say.
#+begin_src text :tangle ~/.config/signature
~noa (https://noa.pub)
• I try to reply to formal emails in three sentences or fewer; excuse my brevity.
• I queue replies and batch send them at intervals; excuse my untimeliness.
#+end_src
#+begin_src elisp
(add-hook 'mu4e-view-mode-hook #'visual-line-fill-column-mode)
#+end_src
** Reading mail with rmail
I have experimented with lots of different methods of reading mail, both in and out of emacs. But i keep coming back to rmail, despite its many, many warts.
#+begin_src elisp
(setopt rmail-primary-inbox-list
(directory-files "~/data/mail/inbox" t "^[^\.]"))
(setopt rmail-file-name "~/data/mail/rmail.mbox")
(setopt rmail-user-mail-address-regexp
(rx "noa@noa.pub"))
(setopt rmail-mime-prefer-html nil)
(setopt rmail-mime-attachment-dirs-alist '(("" "~/media")))
(setopt rmail-displayed-headers
(rx bol (or "To" "Cc" "From" "Date" "Subject") ":"))
(setopt rmail-secondary-file-directory "~/data/mail/archive/")
(setopt rmail-secondary-file-regexp "\\.mbox\\'")
(setopt rmail-delete-after-output t)
(setopt rmail-default-file "~/data/mail/archive/")
(setopt mail-dont-reply-to-names rmail-user-mail-address-regexp)
(add-hook 'rmail-show-message-hook #'visual-line-fill-column-mode)
(setopt rmail-display-summary nil)
(setopt rmail-redisplay-redisplay-summary t)
(setopt rmail-summary-line-count-flag nil)
(setopt rmail-summary-window-size 12)
(defun noa/message-default-headers ()
(format "Fcc: ~/data/mail/outbox/%s.mbox"
(format-time-string "%Y-%m")))
(setopt message-default-headers #'noa/message-default-headers)
#+end_src
#+begin_src elisp
(setopt mairix-file-path "~/data/mail/mairix/")
(setopt mairix-search-file "search.mbox")
#+end_src
** Composing mail from an org buffer
As i keep mentioning, i have a big org file which i put everything into. This includes interesting web articles, which i will paraphrase or quote. However, this does not include mail, because mail is a bit of a beast.
I have written this function as the first step in bridging that gap. I can compose an email in org, via an org-capture template that will prompt me for a recipient and subject. Then i can send the subtree to the mail buffer.
At the moment, i am still recording sent mail in the outbox directory of my mail directory. This is because at the moment i don't have a way to move whole emails from rmail to org (although i think there are ways to do that, and i have added the in_reply_to header for the future).
#+begin_src elisp
(defun noa/org-mail-subtree ()
"Prepare a mail buffer to send the current subtree as an email. Respects the headline properties MAIL_SUBJECT MAIL_TO MAIL_CC MAIL_BCC MAIL_FROM."
(interactive)
(save-excursion
(let* ((subject (org-entry-get nil "MAIL_SUBJECT" nil))
(to (org-entry-get nil "MAIL_TO" nil))
(from (org-entry-get nil "MAIL_FROM"))
(cc (org-entry-get nil "MAIL_CC" nil))
(bcc (org-entry-get nil "MAIL_BCC" nil))
(in-reply-to (org-entry-get nil "MAIL_IN_REPLY_TO" nil))
(other-headers (list (cons "Cc" cc)
(cons "Bcc" bcc)
(cons "From" from )
(cons "In-Reply-To" in-reply-to)))
(body (save-restriction (org-narrow-to-subtree)
(beginning-of-buffer)
(search-forward-regexp ":END:\n*")
(buffer-substring (point) (point-max)))))
(compose-mail to subject other-headers)
(message-goto-body)
(insert body))
(message-goto-to)))
#+end_src
#+begin_src elisp
(defun noa/org-compose-mail ()
"Launch an org buffer to compose a mail."
(interactive)
(org-capture nil "m"))
(defun noa/org-todo-reply-mail ()
"Capture the current mail as a todo ready to be replied to."
(interactive)
(org-capture nil "r"))
#+end_src
** Sending mail with msmtp
#+begin_src elisp
(setopt message-send-mail-function 'message-send-mail-with-sendmail)
(setopt sendmail-program (executable-find "msmtpq"))
(setopt message-sendmail-extra-arguments '("--read-envelope-from"))
(setopt message-sendmail-envelope-from 'header)
(setopt message-kill-buffer-on-exit t)
(setopt message-sendmail-f-is-evil t)
(setopt message-forward-as-mime t)
(setopt message-interactive t)
#+end_src
* Dictionary
Emacs has built in support for interfacing with dictd. With dictd and some dictionaries installed on debian, this works fine out of the box.
Unfortunately, dictionaries in this format tend to be hard wrapped and there isn't a lot of coverage outside of english
#+begin_src elisp
(setopt dictionary-search-interface nil)
#+end_src
Dictionary tooltip mode lets me hover over a word to view the definition.
#+begin_src elisp
(setopt dictionary-tooltip-mode t)
#+end_src
* Unsorted
Just a few settings i haven't put into another category yet.
#+begin_src elisp
(setopt save-interprogram-paste-before-kill t
mouse-yank-at-point t
require-final-newline t
load-prefer-newer t
ediff-window-setup-function 'ediff-setup-windows-plain)
#+end_src