diff options
Diffstat (limited to 'config.org')
-rw-r--r-- | config.org | 1112 |
1 files changed, 584 insertions, 528 deletions
diff --git a/config.org b/config.org index 2944ce8..aaad553 100644 --- a/config.org +++ b/config.org @@ -74,7 +74,7 @@ Properly distinguish these chords from their ascii legacy * Packages For a long time i used use-package to manage emacs package configuration. Over time, i decided it was a bit too magic for me. The syntax is quite different to everything else in the configuration, and most of the options i was using were fairly simple. -I've taken a little defun from acdw to install packages from source. +I had a very brief excursion into using ~package-vc-install~ to manage packages directly from source, but i found this was too fragile, especially without relying on use-package. For now, i've modified a little defun from acdw i used for that purpose to simply install packages using package.el. #+begin_src elisp (defun package-ensure (pkg) @@ -82,15 +82,7 @@ I've taken a little defun from acdw to install packages from source. (package-install pkg))) #+end_src -** Colour theme -This is my emacs theme. It's a monochrome theme which, unlike most monochrome themes, really does have only two colours. I define a few faces, and set every other face as one of them. There are a few things i want to do with it before i make it properly public: make the colours configurable and able to update on the fly, and in general iron out some of the janky parts. A few things defined it are quite specific to this configuration, like the way i define the borders for the tab and header bars, and there is no mode line configuration because i don't use it. - -#+begin_src elisp - (require 'tubthumping-theme) - (load-theme 'tubthumping t) -#+end_src - -** Executing commands +* Executing commands I make my caps lock a menu key, so i can open the command palette with it: #+begin_src elisp @@ -110,7 +102,7 @@ It is often useful to be able to run a command while i am already in the process (setopt minibuffer-depth-indicate-mode t) #+end_src -** Minibuffer candidate completion +* Minibuffer candidate completion Vertico is a package for a nice minibuffer completion experience. It displays a vertical list of candidates. It integrates well with the emacs ecosystem and lets me use other packages that also play nicely. #+begin_src elisp @@ -158,6 +150,18 @@ If i type ~/ etc in a find-file prompt, get rid of the preceding directory names (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy) #+end_src +** Better default completion + +Some settings for nicer completion with the default emacs completion buffer. I don't use this, because i use vertico. + +#+begin_src elisp :tangle no + (setopt completion-auto-help 'lazy + completion-auto-select 'second-tab + completion-show-help nil + completions-sort nil + completions-header-format nil) +#+end_src + * Completion styles When given a prompt to select from a list of candidates, there are quite a lot of things we can tweak to improve the experience. @@ -182,7 +186,61 @@ By default, emacs overrides the completion styles for email address, but i'm hap (setopt completion-category-defaults nil) #+end_src -* Replace the mode line with a header line +* Aesthetic changes +** Colour theme +This is my emacs theme. It's a monochrome theme which, unlike most monochrome themes, really does have only two colours. I define a few faces, and set every other face as one of them. There are a few things i want to do with it before i make it properly public: make the colours configurable and able to update on the fly, and in general iron out some of the janky parts. A few things defined it are quite specific to this configuration, like the way i define the borders for the tab and header bars, and there is no mode line configuration because i don't use it. + +#+begin_src elisp + (require 'tubthumping-theme) + (load-theme 'tubthumping t) +#+end_src + +** Fonts +My current favourite font is sn pro, which feels like comic sans for grown ups. It's friendly but consistent and well thought out. However, it's also a proportional font, which obviously is the right way to do things, but emacs is very old and comes from a time before the innovation of legibility. As a result, there are some things that require a monospaced font, so i set one here. I chose go mono for two reasons: the first is because i think it looks really nice; the second is because it has serifs and is very visually distinct from sn pro, so i can notice and shame those buffers which require a fixed width font to operate properly. + +#+begin_src elisp +(custom-set-faces + '(fixed-pitch ((t (:family "Go Mono" :height 110)))) + '(variable-pitch ((t (:family "SN Pro" :height 110))))) +#+end_src + +For some frustrating reason, emacs does not respect fontconfig font settings. What this means in practice is that emacs by default draws cjk characters with the korean variant. Luckily emacs has its own obscure and poorly documented way of doing things, so i can iterate over the relevant charsets and set the font specifically for those characters. + +#+begin_src elisp +(dolist (charset '(han cjk-misc)) + (set-fontset-font t charset (font-spec :family "Noto Sans CJK SC"))) +#+end_src + +Similar to the above, we have to manually set the font we want to be used for emoji. I like the cute style of the emoji in fsd emoji, but it doesn't have very good coverage, so we also set noto emoji as the backup. Note that noto emoji is not the same as noto color emoji, which uses coloured emoji. That's clearly against the vibe of this emacs! + +#+begin_src elisp +(set-fontset-font t 'emoji (font-spec :family "FSD Emoji") nil 'prepend) +(set-fontset-font t 'emoji (font-spec :family "Noto Emoji") nil 'append) +#+end_src + +While we're here, let's set up emoji input. + +#+begin_src elisp + (global-set-key (kbd "C-.") #'emoji-search) +#+end_src + +Describe a key based on a string like "C-SPC" + +#+begin_src elisp +(defun describe-key-shortcut (shortcut) + (interactive "MShortcut: ") + (describe-key (kbd shortcut))) +#+end_src + +Update the calendar. We want weeks to start on a monday, the first day of the week. Holidays should be highlighted, and the date format should put the year first. + +#+begin_src elisp +(setopt calendar-week-start-day 1 + calendar-mark-holidays-flag t + calendar-date-style 'iso) +#+end_src + +** Replace the mode line with a header line First, we set the mode line to nil. On my graphical display, this collapses it so all i get is a thin black line separating the buffer from the echo area. #+begin_src elisp @@ -285,239 +343,207 @@ I'd like to change the face, but ~pulse-highlight-face~ has *DO NOT CUSTOMIZE* i (advice-add command :after #'pulse-line)) #+end_src -** Xmpp -Jabber.el is an xmpp client in emacs. I don't actually use xmpp as xmpp that much. But i do use it to connect to irc, and this package lets me do that. Unfortunately, it's not a particularly well-behaved package; by default it clobbers some keybindings and floods the echo area with unhelpful messages. +** Make wide windows narrow with visual-fill-column +Reading prose with long lines is a chore. Luckily there are several packages in emacs to make the windows squeeze text into a more legible sliver. Previously i used olivetti, which is a really nice package, but it's quite heavyweight and broke on me with no explanation more frequently than i appreciated. I've switched to visual-fill-column and been quite satisfied. #+begin_src elisp - (package-ensure 'jabber) - (setopt jabber-account-list '(("noa@hmm.st"))) + (package-ensure 'visual-fill-column) + (setopt visual-fill-column-enable-sensible-window-split t) + (setopt visual-fill-column-center-text t) + (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust) + + (add-hook 'text-mode-hook #'visual-line-fill-column-mode) + (add-hook 'eww-after-render-hook #'visual-line-fill-column-mode) + (add-hook 'nov-post-html-render-hook #'visual-line-fill-column-mode) #+end_src -So now what we're going to do is get it to stop showing a bunch of channels in the mode line, because there will always be new activity and i want to drop in when i feel like it, and not always have a reminder of that fact. The defun below is copied from jabber-activity-show-p-default but with an extra condition plopped in. +The fringes of a window can show useful icons relating to the text in the buffer. For example, when using org modern, blocks get a line in the left fringe to show their content more clearly, Because of this, i prefer for them to be right next to the buffer text. #+begin_src elisp - (defcustom noa/jabber-activity-dont-show - '("#tildetown%town@irc.hmm.st" - "#meta%tilde.chat@irc.hmm.st" - "hmm@conference.hmm.st") - "List of JIDs not to show in the modeline." - :group 'jabber-activity)(defun noa/jabber-activity-show-p (jid) - "Return non-nil if JID should be hidden. - A JID should be hidden when there is an invisible buffer for JID, - when JID is not in `noa/jabber-activity-dont-show', - and when JID is not in `jabber-activity-banned'." - (let ((buffer (jabber-activity-find-buffer-name jid))) - (and (buffer-live-p buffer) - (not (get-buffer-window buffer 'visible)) - (not (cl-dolist (entry jabber-activity-banned) - (when (string-match entry jid) - (cl-return t)))) - (not (cl-dolist (entry noa/jabber-activity-dont-show) - (when (string-match entry jid) - (cl-return t)))))))(setopt jabber-activity-show-p #'noa/jabber-activity-show-p) + (setopt visual-fill-column-fringes-outside-margins nil) #+end_src -I'm on a laptop, so whenever i shut it i get disconnected. Jabber can query auth-source for my password, so automatically reconnecting is useful and doesn't need me to do anything. +Adaptive wrap will indent visually wrapped text to match the indent at the start of the line, for example in lists. This works... fine. However it's adapting the prefix, it doesn't indent nicely with proportional fonts, but you can't win them all. #+begin_src elisp -(setopt jabber-auto-reconnect t) + ;; (use-package adaptive-wrap :ensure t) + ;; (add-hook 'visual-fill-column-mode-hook #'adaptive-wrap-prefix-mode) #+end_src -Because my xmpp server supports message history, i don't need to worry about exiting without seeing all messages, as they'll be there when i get back. +** Interface +I want to make sure that various bits of the interface are hidden. but this isn't an "all gui chrome is useless" rampage. I personally think the scrollbar is useful, i like the visual indication it gives of how far i am through a file. -#+begin_src elisp -(setopt jabber-activity-query-unread nil) +At the moment, explicitly disabling the menu bar and tool bar does nothing, because i already set there to be no lines displayed for the tool and menu bars in my early-init.el file. + +#+begin_src elisp :tangle no + (setopt menu-bar-mode nil + tool-bar-mode nil) #+end_src -The default buffer names are a bit ugly to look at, so i change them to a similar format as eww. Which is still pretty ugly, welcome to emacs +Tooltips are little popups next to the mouse cursor. I think this information is helpful, but i like it to appear in a more consistent position, because i find it frustrating when popups cover parts of the ui that i wanted to see. By disabling tooltip-mode, the contents that would be in a popup is instead shown in the echo area. #+begin_src elisp -(setopt jabber-chat-buffer-format "*%n | jabber*" - jabber-groupchat-buffer-format "*%n | jabber*") +(setopt tooltip-mode nil) #+end_src -As alluded to above, jabber.el also has a terrible terrible habit of sending a message to the echo area for every change in online state of my contacts, and every single message in any channel. Obviously this gets annoying fast, especially if i'm using the minibuffer at that time. Thank you to acdw for pointing me towards these helpful hooks to remove. +I see no reason not to immediately show which chords in a key sequence i have already pressed. Emacs does, however, and instead of letting me set the value of echo-keystrokes to zero to wait zero seconds to show that information, it repurposes zero as a method of disabling the functionality altogether, and provides no special functionality for setting it to nil that would explain why that's not an acceptable method of disabling a feature. Instead, i have to deal with setting it to nearly zero, and luckily i can't tell the difference. #+begin_src elisp -(remove-hook 'jabber-alert-muc-hooks #'jabber-muc-echo) -(remove-hook 'jabber-alert-presence-hooks #'jabber-presence-echo) +(setopt echo-keystrokes 0.1) #+end_src -Also stop jabber from clobbering the dired-jump binding and instead use something on C-c like a good child. +A useful feature when programming is to show matching parentheses. Show-paren-mode is a global mode. By default it runs in all buffers except those inheriting from special mode. #+begin_src elisp -(with-eval-after-load 'jabber - (keymap-global-set "C-x C-j" #'dired-jump)) +(setopt show-paren-mode t) #+end_src -Finally, have a binding to jump to a buffer in which there's been some new activity. Better than always using switch-to-buffer to get there. +This variable means that if there is no non-whitespace character in between the point and the paren, it will be highlighted. It's useful to highlight parentheses if the point is at the start of the line and the paren is indented. #+begin_src elisp -(keymap-global-set "C-c C-j" #'jabber-activity-switch-to) +(setopt show-paren-when-point-in-periphery t) #+end_src -** Nov.el +By default, the point has to be after a paren for it to be highlighted. But often the point will be just inside, in which case it's also helpful for the pair to be highlighted. + #+begin_src elisp - (package-ensure 'nov) - (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)) +(setopt show-paren-when-point-inside-paren t) #+end_src -* Better prose -The majority of the work i do in emacs is writing documents, so it's nice to know that the words coming out of my fingers are the ones i expect them to be. - -** 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) +(setopt tab-bar-mode t) +(setopt tab-bar-format '(tab-bar-format-menu-bar + tab-bar-format-align-right + tab-bar-format-global)) #+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) +(setopt global-font-lock-mode t) +(setopt font-lock-maximum-decoration nil) #+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) + (setopt inhibit-startup-screen t + mouse-drag-and-drop-region nil + mouse-yank-at-point t + ;; deleting should be an explicit action + delete-selection-mode nil) #+end_src -*** Improve the corrections grid - -Show the corrections in a grid, so i can see more of them at once. - -*** 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) +(global-set-key (kbd "C-t") 'tab-new) #+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. +Shift click to select region with the mouse. This annoyingly rings the bell for an error. It also interferes with my input method switcher, which doesn't notice the mouse click and thinks i've pressed shift with no other keys. #+begin_src elisp - (put 'jinx-overlay 'mouse-face '(jinx-misspelled highlight)) +(global-unset-key (kbd "S-<down-mouse-1>")) +(global-set-key (kbd "S-<down-mouse-1>") 'mouse-save-then-kill) #+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. +** Monospace font fallback -** 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. +This is my own version of fixed-pitch, which has some changes to it. No hooks are added by default. Updating the whitelist automatically applies the hooks. And there is no functionality for changing the cursor type. #+begin_src elisp - (package-ensure 'consult) - (package-activate 'consult) + (use-package fixed-pitch) + (setopt fixed-pitch-whitelist-hooks '(calendar-mode-hook + dired-mode-hook + ibuffer-mode-hook + magit-mode-hook + profiler-report-mode-hook + jabber-roster-mode-hook + mu4e-headers-mode-hook)) #+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 +** Transient variable font -These are some other almost default functions but with extra interactivity. +While we're at it, ensure that transients look nice with a variable pitch font. #+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)) - (global-set-key [remap info] #'consult-info) +(setopt transient-align-variable-pitch t) #+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. +* Xmpp +Jabber.el is an xmpp client in emacs. I don't actually use xmpp as xmpp that much. But i do use it to connect to irc, and this package lets me do that. Unfortunately, it's not a particularly well-behaved package; by default it clobbers some keybindings and floods the echo area with unhelpful messages. #+begin_src elisp - (package-ensure 'marginalia) - (setopt marginalia-mode t) + (package-ensure 'jabber) + (setopt jabber-account-list '(("noa@hmm.st"))) #+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. +So now what we're going to do is get it to stop showing a bunch of channels in the mode line, because there will always be new activity and i want to drop in when i feel like it, and not always have a reminder of that fact. The defun below is copied from jabber-activity-show-p-default but with an extra condition plopped in. #+begin_src elisp -(setopt marginalia-max-relative-age most-positive-fixnum) + (defcustom noa/jabber-activity-dont-show + '("#tildetown%town@irc.hmm.st" + "#meta%tilde.chat@irc.hmm.st" + "hmm@conference.hmm.st") + "List of JIDs not to show in the modeline." + :group 'jabber-activity)(defun noa/jabber-activity-show-p (jid) + "Return non-nil if JID should be hidden. + A JID should be hidden when there is an invisible buffer for JID, + when JID is not in `noa/jabber-activity-dont-show', + and when JID is not in `jabber-activity-banned'." + (let ((buffer (jabber-activity-find-buffer-name jid))) + (and (buffer-live-p buffer) + (not (get-buffer-window buffer 'visible)) + (not (cl-dolist (entry jabber-activity-banned) + (when (string-match entry jid) + (cl-return t)))) + (not (cl-dolist (entry noa/jabber-activity-dont-show) + (when (string-match entry jid) + (cl-return t)))))))(setopt jabber-activity-show-p #'noa/jabber-activity-show-p) #+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 <tab> 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. +I'm on a laptop, so whenever i shut it i get disconnected. Jabber can query auth-source for my password, so automatically reconnecting is useful and doesn't need me to do anything. #+begin_src elisp -(define-key input-decode-map [?\C-i] [C-i]) +(setopt jabber-auto-reconnect t) #+end_src -Now that tab and C-i are properly distinguished, i can bind C-i to completion at point. +Because my xmpp server supports message history, i don't need to worry about exiting without seeing all messages, as they'll be there when i get back. #+begin_src elisp -(global-set-key (kbd "<C-i>") 'completion-at-point) +(setopt jabber-activity-query-unread nil) #+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. +The default buffer names are a bit ugly to look at, so i change them to a similar format as eww. Which is still pretty ugly, welcome to emacs #+begin_src elisp -(setopt completion-in-region-function 'consult-completion-in-region) +(setopt jabber-chat-buffer-format "*%n | jabber*" + jabber-groupchat-buffer-format "*%n | jabber*") #+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. +As alluded to above, jabber.el also has a terrible terrible habit of sending a message to the echo area for every change in online state of my contacts, and every single message in any channel. Obviously this gets annoying fast, especially if i'm using the minibuffer at that time. Thank you to acdw for pointing me towards these helpful hooks to remove. #+begin_src elisp -(setopt help-window-select t) +(remove-hook 'jabber-alert-muc-hooks #'jabber-muc-echo) +(remove-hook 'jabber-alert-presence-hooks #'jabber-presence-echo) #+end_src -Also to that end, we use the same window as the help buffer to open source files. +Also stop jabber from clobbering the dired-jump binding and instead use something on C-c like a good child. #+begin_src elisp - (setopt help-window-keep-selected t) +(with-eval-after-load 'jabber + (keymap-global-set "C-x C-j" #'dired-jump)) #+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. +Finally, have a binding to jump to a buffer in which there's been some new activity. Better than always using switch-to-buffer to get there. #+begin_src elisp - (setopt help-enable-variable-value-editing t) +(keymap-global-set "C-c C-j" #'jabber-activity-switch-to) #+end_src -* 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. - +* Nov.el #+begin_src elisp - (setopt switch-to-buffer-obey-display-actions t) + (package-ensure 'nov) + (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)) #+end_src +* Writing prose +The majority of the work i do in emacs is writing documents, so it's nice to know that the words coming out of my fingers are the ones i expect them to be. + ** Org mode I thought it would be a good idea to have the ability to set headings in my notes as things i wanted to recall later, as i store most of the things i think about in an org file. I still do think that, but the package isn't in melpa right now and i'm lazy and i'm not in education, so i haven't got around to it yet. @@ -665,6 +691,17 @@ This doesn't respect my timestamp custom format configured above. (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 + ** Website generation #+begin_src elisp @@ -793,340 +830,280 @@ The index page generation functions were taken from Dennis Ogbe. Thank you! :recursive t))) #+end_src -** Make wide windows narrow with visual-fill-column -Reading prose with long lines is a chore. Luckily there are several packages in emacs to make the windows squeeze text into a more legible sliver. Previously i used olivetti, which is a really nice package, but it's quite heavyweight and broke on me with no explanation more frequently than i appreciated. I've switched to visual-fill-column and been quite satisfied. +** Markdown #+begin_src elisp - (package-ensure 'visual-fill-column) - (setopt visual-fill-column-enable-sensible-window-split t) - (setopt visual-fill-column-center-text t) - (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust) + (package-ensure 'markdown-mode) + (add-to-list 'auto-mode-alist + '("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode)) - (add-hook 'text-mode-hook #'visual-line-fill-column-mode) - (add-hook 'eww-after-render-hook #'visual-line-fill-column-mode) - (add-hook 'nov-post-html-render-hook #'visual-line-fill-column-mode) #+end_src -The fringes of a window can show useful icons relating to the text in the buffer. For example, when using org modern, blocks get a line in the left fringe to show their content more clearly, Because of this, i prefer for them to be right next to the buffer text. +** 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 - (setopt visual-fill-column-fringes-outside-margins nil) +(add-hook 'text-mode-hook #'abbrev-mode) #+end_src -Adaptive wrap will indent visually wrapped text to match the indent at the start of the line, for example in lists. This works... fine. However it's adapting the prefix, it doesn't indent nicely with proportional fonts, but you can't win them all. +** 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 - ;; (use-package adaptive-wrap :ensure t) - ;; (add-hook 'visual-fill-column-mode-hook #'adaptive-wrap-prefix-mode) + (package-ensure 'jinx) + (setopt global-jinx-mode 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. +Replace the default spellcheck binding with jinx #+begin_src elisp - (setopt browse-url-browser-function 'eww-browse-url - browse-url-secondary-browser-function 'browse-url-default-browser) +(keymap-global-set "M-$" #'jinx-correct) +(keymap-global-set "C-M-$" #'jinx-languages) #+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. +*** 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 - (setopt url-cookie-trusted-urls '() - url-cookie-untrusted-urls '(".*") - shr-cookie-policy nil) +(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 -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. +*** 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 - (setopt shr-use-colors nil) - (setopt shr-max-width nil) + (put 'jinx-overlay 'mouse-face '(jinx-misspelled highlight)) #+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 +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. -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. +* 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 -(setopt eww-auto-rename-buffer 'title) + (package-ensure 'consult) + (package-activate 'consult) #+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 +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 -(setopt global-eldoc-mode t) + (autoload #'consult-buffer "consult" nil t) + (global-set-key [remap switch-to-buffer] #'consult-buffer) #+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 +These are some other almost default functions but with extra interactivity. #+begin_src elisp -(setopt - cursor-type 'bar - blink-cursor-mode -1 - blink-cursor-interval 0.7) + (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)) + (global-set-key [remap info] #'consult-info) #+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. +* 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 -(setopt delete-by-moving-to-trash t) + (package-ensure 'marginalia) + (setopt marginalia-mode 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. +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 dired-recursive-deletes 'always) +(setopt marginalia-max-relative-age most-positive-fixnum) #+end_src -Recursive copying isn't even destructive, so i definitely don't want to be asked about that. +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 <tab> 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 -(setopt dired-recursive-copies 'always) +(define-key input-decode-map [?\C-i] [C-i]) #+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. +Now that tab and C-i are properly distinguished, i can bind C-i to completion at point. #+begin_src elisp -(setopt dired-clean-up-buffers-too nil) +(global-set-key (kbd "<C-i>") 'completion-at-point) #+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. +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 dired-dwim-target t) +(setopt completion-in-region-function 'consult-completion-in-region) #+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. +* 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 dired-listing-switches "-alvFh --group-directories-first") +(setopt help-window-select t) #+end_src -By default, don't show dired details +Also to that end, we use the same window as the help buffer to open source files. #+begin_src elisp -(add-hook 'dired-mode-hook #'dired-hide-details-mode) + (setopt help-window-keep-selected t) #+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. +** Better aesthetics +Help buffers put quotes around links in the buffer. I don't think this adds anything to the output. #+begin_src elisp - (package-ensure 'dired-du) - (setopt dired-du-size-format t) - (add-hook 'dired-mode-hook #'dired-du-mode) -#+end_src - -** 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. + (setopt help-clean-buttons t) + #+end_src -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. +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 -(require 'elastic-indent) -(add-hook 'prog-mode-hook #'elastic-indent-mode) -#+end_src + (setopt help-downcase-arguments t) + #+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. +** 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 - (require 'elastic-table) - (add-hook 'prog-mode-hook #'elastic-table-mode) + (setopt help-enable-variable-value-editing t) #+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 +* 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. -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. +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 tab-always-indent nil) + (setopt browse-url-browser-function 'eww-browse-url + browse-url-secondary-browser-function 'browse-url-default-browser) #+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. +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 -(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)))) + (setopt url-cookie-trusted-urls '() + url-cookie-untrusted-urls '(".*") + shr-cookie-policy nil) #+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. +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 - (package-ensure 'aggressive-indent) - (setopt global-aggressive-indent-mode t) + (setopt shr-use-colors nil) + (setopt shr-max-width nil) #+end_src -** Interface -I want to make sure that various bits of the interface are hidden. but this isn't an "all gui chrome is useless" rampage. I personally think the scrollbar is useful, i like the visual indication it gives of how far i am through a file. - -At the moment, explicitly disabling the menu bar and tool bar does nothing, because i already set there to be no lines displayed for the tool and menu bars in my early-init.el file. +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 :tangle no - (setopt menu-bar-mode nil - tool-bar-mode nil) -#+end_src +#+begin_src elisp + (setopt shr-max-image-proportion 0.9 + shr-discard-aria-hidden t) + #+end_src -Tooltips are little popups next to the mouse cursor. I think this information is helpful, but i like it to appear in a more consistent position, because i find it frustrating when popups cover parts of the ui that i wanted to see. By disabling tooltip-mode, the contents that would be in a popup is instead shown in the echo area. +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 tooltip-mode nil) +(setopt eww-auto-rename-buffer 'title) #+end_src -I see no reason not to immediately show which chords in a key sequence i have already pressed. Emacs does, however, and instead of letting me set the value of echo-keystrokes to zero to wait zero seconds to show that information, it repurposes zero as a method of disabling the functionality altogether, and provides no special functionality for setting it to nil that would explain why that's not an acceptable method of disabling a feature. Instead, i have to deal with setting it to nearly zero, and luckily i can't tell the difference. +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 echo-keystrokes 0.1) +(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 -A useful feature when programming is to show matching parentheses. Show-paren-mode is a global mode. By default it runs in all buffers except those inheriting from special mode. - #+begin_src elisp -(setopt show-paren-mode t) +(setopt global-eldoc-mode t) #+end_src -This variable means that if there is no non-whitespace character in between the point and the paren, it will be highlighted. It's useful to highlight parentheses if the point is at the start of the line and the paren is indented. +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 show-paren-when-point-in-periphery t) +(setopt + cursor-type 'bar + blink-cursor-mode -1 + blink-cursor-interval 0.7) #+end_src -By default, the point has to be after a paren for it to be highlighted. But often the point will be just inside, in which case it's also helpful for the pair to be highlighted. +* 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. -#+begin_src elisp -(setopt show-paren-when-point-inside-paren t) -#+end_src +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 tab-bar-mode t) -(setopt tab-bar-format '(tab-bar-format-menu-bar - tab-bar-format-align-right - tab-bar-format-global)) +(setopt delete-by-moving-to-trash t) #+end_src -#+begin_src elisp -(setopt global-font-lock-mode t) -(setopt font-lock-maximum-decoration nil) -#+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 inhibit-startup-screen t - mouse-drag-and-drop-region nil - mouse-yank-at-point t - ;; deleting should be an explicit action - delete-selection-mode nil) +(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 -(global-set-key (kbd "C-t") 'tab-new) +(setopt dired-recursive-copies 'always) #+end_src -Shift click to select region with the mouse. This annoyingly rings the bell for an error. It also interferes with my input method switcher, which doesn't notice the mouse click and thinks i've pressed shift with no other keys. +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 -(global-unset-key (kbd "S-<down-mouse-1>")) -(global-set-key (kbd "S-<down-mouse-1>") 'mouse-save-then-kill) +(setopt dired-clean-up-buffers-too nil) #+end_src -* Saving -Backups are pointless in long emacs sessions imo, but autosaves are useful. +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 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 1 - 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))) +(setopt dired-dwim-target t) #+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. +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 :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) +#+begin_src elisp +(setopt dired-listing-switches "-alvFh --group-directories-first") #+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. +By default, don't show dired details #+begin_src elisp -(setopt sentence-end-double-space nil) +(add-hook 'dired-mode-hook #'dired-hide-details-mode) #+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. +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 -(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) + (package-ensure 'dired-du) + (setopt dired-du-size-format t) + (add-hook 'dired-mode-hook #'dired-du-mode) #+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. +* Behaviour +** Switching buffers -#+begin_src elisp -(setopt require-final-newline t) -#+end_src +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 window-min-height 1 - window-combination-resize t - window-resize-pixelwise t - frame-resize-pixelwise t) -#+end_src elisp + (setopt switch-to-buffer-obey-display-actions t) +#+end_src -** history +** History #+begin_src elisp (setopt history-length 250 kill-ring-max 25) @@ -1207,8 +1184,80 @@ Hide commands in M-x which do not work in the current mode. bookmark-default-file "~/.config/emacs/bookmarks") #+end_src -* Miscellaneous +** 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 1 + 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 @@ -1216,6 +1265,8 @@ C-l goes in order, rather than first centering the cursor. This is particularly (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 @@ -1223,16 +1274,13 @@ Emacs uses choppy scrolling by default. If i scoll with my trackpad, it's nice pixel-scroll-precision-use-momentum t) #+end_src +** Remember my position in files + #+begin_src elisp (setopt save-place-mode 1) #+end_src -#+begin_src elisp -(setenv "PAGER" "cat") -(setenv "TERM" "dumb") -(setenv "NO_COLOR") -(setenv "GPG_AGENT_INFO" nil) -#+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. @@ -1241,161 +1289,238 @@ The former means that when given a list of choices, we can use single character (setopt use-short-answers t) #+end_src +** Disable disabled commands + #+begin_src elisp -(setq disabled-command-function nil) -(setopt custom-file (make-temp-file "custom")) -(setq inhibit-startup-echo-area-message "noa") ;; #userfreedom + (setq disabled-command-function nil) #+end_src +** Don't save changes in the customize interface + #+begin_src elisp -(setopt - uniquify-after-kill-buffer-p t - uniquify-buffer-name-style 'forward - uniquify-ignore-buffers-re "^\\*" - uniquify-separator "/") + (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 -(setopt - save-interprogram-paste-before-kill t - mouse-yank-at-point t - require-final-newline t - visible-bell t - load-prefer-newer t - ediff-window-setup-function 'ediff-setup-windows-plain) + (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 help-at-pt-display-when-idle t) + (setopt ring-bell-function 'ignore) #+end_src -This behaviour changes how we visit symlinks. +** Automatic help at point + +This puts some help in the minibuffer when we leave the point on some interactive text. #+begin_src elisp - (setopt find-file-visit-truename t) - (setopt vc-follow-symlinks 'ask) +(setopt help-at-pt-display-when-idle 'never) #+end_src -Use ibuffer instead of list-buffers +** Long lines + +Better support for long lines. #+begin_src elisp - (global-set-key [remap list-buffers] 'ibuffer) + (setopt global-so-long-mode t) #+end_src -Some settings for nicer completion with the default emacs completion buffer. I don't use this, because i use vertico. +** Kill processes without asking -#+begin_src elisp :tangle no - (setopt completion-auto-help 'lazy - completion-auto-select 'second-tab - completion-show-help nil - completions-sort nil - completions-header-format nil) +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 -Some tramp settings. +** Automatically revert buffers + +Automatically revert buffers when they change on disk. This doesn't apply to tramp. #+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) + (setopt global-auto-revert-mode t) #+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. +** Visit symlinks + +This behaviour changes how we visit symlinks. #+begin_src elisp - (setopt vc-handled-backends '(Git)) - ;; (setopt vc-handled-backends '(Git RCS CVS SVN SCCS SRC Bzr Hg)) + (setopt find-file-visit-truename t) + (setopt vc-follow-symlinks 'ask) #+end_src - It seems that tramp can also be made faster with these .ssh/config settings. +** Ibuffer -#+begin_src text :tangle no - Host * - ControlMaster auto - ControlPath ~/.ssh/master-%h:%p - ControlPersist 10m - ForwardAgent yes - ServerAliveInterval 60 +Use ibuffer instead of list-buffers + +#+begin_src elisp + (global-set-key [remap list-buffers] 'ibuffer) #+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 -(global-set-key (kbd "M-z") 'zap-up-to-char) +(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 - (package-ensure 'markdown-mode) - (add-to-list 'auto-mode-alist - '("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode)) + (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 - (package-ensure 'valign) - (add-hook 'markdown-mode-hook #'valign-mode) - (add-hook 'org-mode-hook #'valign-mode) +(setopt tab-always-indent nil) +#+end_src - (setopt valign-fancy-bar t) - (setopt valign-max-table-size 0) +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 -This is my own version of fixed-pitch, which has some changes to it. No hooks are added by default. Updating the whitelist automatically applies the hooks. And there is no functionality for changing the cursor type. +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 - (use-package fixed-pitch) - (setopt fixed-pitch-whitelist-hooks '(calendar-mode-hook - dired-mode-hook - ibuffer-mode-hook - magit-mode-hook - profiler-report-mode-hook - jabber-roster-mode-hook - mu4e-headers-mode-hook)) + (package-ensure 'aggressive-indent) + (setopt global-aggressive-indent-mode t) #+end_src -While we're at it, ensure that transients look nice with a variable pitch font. +* 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 transient-align-variable-pitch t) +(setopt sentence-end-double-space nil) #+end_src -Automatically revert buffers when they change on disk. This doesn't apply to tramp. +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 - (setopt global-auto-revert-mode t) +(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) #+end_src -This will stop us being prompted before killing a buffer with a running process: +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 kill-buffer-query-functions - (remq 'process-kill-buffer-query-function - kill-buffer-query-functions)) +(setopt require-final-newline t) #+end_src -Scroll along with text in compilation mode, and stop scrolling at the first error. +#+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 compilation-scroll-output 'first-error) + (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 -Ignore the bell. +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 ring-bell-function 'ignore) + (setopt vc-handled-backends '(Git)) + ;; (setopt vc-handled-backends '(Git RCS CVS SVN SCCS SRC Bzr Hg)) #+end_src -Better support for long lines. + 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 - (setopt global-so-long-mode t) + ;; (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 +* 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. @@ -1443,6 +1568,17 @@ Okay, i lied about being vanilla. I want isearch to use the minibuffer like a g (global-set-key (kbd "C-x k") 'kill-this-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. @@ -1456,59 +1592,6 @@ If you want to move the cursor to a subsequent instance of the search term, you 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. -* 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 - -** Fonts -My current favourite font is sn pro, which feels like comic sans for grown ups. It's friendly but consistent and well thought out. However, it's also a proportional font, which obviously is the right way to do things, but emacs is very old and comes from a time before the innovation of legibility. As a result, there are some things that require a monospaced font, so i set one here. I chose go mono for two reasons: the first is because i think it looks really nice; the second is because it has serifs and is very visually distinct from sn pro, so i can notice and shame those buffers which require a fixed width font to operate properly. - -#+begin_src elisp -(custom-set-faces - '(fixed-pitch ((t (:family "Go Mono" :height 110)))) - '(variable-pitch ((t (:family "SN Pro" :height 110))))) -#+end_src - -For some frustrating reason, emacs does not respect fontconfig font settings. What this means in practice is that emacs by default draws cjk characters with the korean variant. Luckily emacs has its own obscure and poorly documented way of doing things, so i can iterate over the relevant charsets and set the font specifically for those characters. - -#+begin_src elisp -(dolist (charset '(han cjk-misc)) - (set-fontset-font t charset (font-spec :family "Noto Sans CJK SC"))) -#+end_src - -Similar to the above, we have to manually set the font we want to be used for emoji. I like the cute style of the emoji in fsd emoji, but it doesn't have very good coverage, so we also set noto emoji as the backup. Note that noto emoji is not the same as noto color emoji, which uses coloured emoji. That's clearly against the vibe of this emacs! - -#+begin_src elisp -(set-fontset-font t 'emoji (font-spec :family "FSD Emoji") nil 'prepend) -(set-fontset-font t 'emoji (font-spec :family "Noto Emoji") nil 'append) -#+end_src - -While we're here, let's set up emoji input. - -#+begin_src elisp - (global-set-key (kbd "C-.") #'emoji-search) -#+end_src - -Describe a key based on a string like "C-SPC" - -#+begin_src elisp -(defun describe-key-shortcut (shortcut) - (interactive "MShortcut: ") - (describe-key (kbd shortcut))) -#+end_src - -Update the calendar. We want weeks to start on a monday, the first day of the week. Holidays should be highlighted, and the date format should put the year first. - -#+begin_src elisp -(setopt calendar-week-start-day 1 - calendar-mark-holidays-flag t - calendar-date-style 'iso) -#+end_src - ** Replace Anzu provides real-time updates to the replacement string when running query-replace. @@ -1561,12 +1644,12 @@ Put a quote in the scratch buffer "\n\n")) #+end_src -** Eshell +* 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 +** 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 @@ -1576,56 +1659,16 @@ This is equivalent to the message of the day present in some shells. I wanted i (setopt eshell-banner-message (noa/eshell-banner-message)) #+end_src -** i-ching -I don't know why but it seems cool - -#+begin_src elisp - ;; (package-ensure 'i-ching) -#+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 +** Environment variables #+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)))) +(setenv "PAGER" "cat") +(setenv "TERM" "dumb") +(setenv "NO_COLOR") +(setenv "GPG_AGENT_INFO" nil) #+end_src -** Pass(1) +* Pass(1) The auth-source library allows packages to request password information. It has a password store backend, which is nicer to work with than netrc files. #+begin_src elisp @@ -1814,3 +1857,16 @@ I have experimented with lots of different methods of reading mail, both in and (setopt message-forward-as-mime t) (setopt message-interactive 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 + + |