- Clone this repo to ~/.emacs.d
- Open
readme.org
in emacs- Press
C-c C-v t
to tangle all the blocks- Restart Emacs.
This setup make certain assumptions regarding its environment:
- Machine in use is Linux or MacOS
- Shell in use is
fish
- Feature Flags
Feature flags to change the configuration based on different scenarios. Right now I run my Emacs on two machines, but instead for adding checks for which machine I am on right now, I want to create a default configuration, and modify it based on which feature-flags are enabled.
A profile is a cons cell of
(name . metadata)
(defvar my--feature-flags '())
- On mac without external monitor
(when (eq system-type 'darwin) (add-to-list 'my--feature-flags '(small-screen . t)))
- When Emacs’ packages are handled by guix
(when (file-exists-p "/gnu/store") (add-to-list 'my--feature-flags '(powered-by-guix . t)))
- When Emacs’ packages are handled by straight.el
(unless (assoc 'powered-by-guix my--feature-flags) (add-to-list 'my--feature-flags '(powered-by-straightel . t)))
- On mac without external monitor
- Bootstrap
;; -*- lexical-binding: t -*- ;;; Bootstrap ;; Contrary to what many Emacs users have in their configs, you don't need ;; more than this to make UTF-8 the default coding system: (set-language-environment "UTF-8") ;; set-language-enviornment sets default-input-method, which is unwanted (setq default-input-method nil package-enable-at-startup nil)
;; -*- lexical-binding: t -*-
(when (assoc 'powered-by-guix my--feature-flags)
;; Must use :ensure nil until this issue is fixed
;; https://codeberg.org/guix/guix/issues/736
(setq use-package-ensure-function (lambda () nil)))
;; Bootstrap straight.el
(when (assoc 'powered-by-straightel my--feature-flags)
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name
"straight/repos/straight.el/bootstrap.el"
(or (bound-and-true-p straight-base-dir)
user-emacs-directory)))
(bootstrap-version 7))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage)))
;; use straight with use-package by default
(when (assoc 'powered-by-straightel my--feature-flags)
(setf straight-use-package-by-default t))
- Ergonomically create keymaps
(defmacro my--defkeymap (name prefix &rest bindings) "Create a new NAME-keymap bound to PREFIX, with BINDINGS. Usage: (my--defkeymap \"my-git\" \"C-c g\" '(\"s\" . magit-status)) " (let* ((keymap-name (intern (concat name "-keymap"))) (keymap-alias (intern name)) (keymap-bindings (mapcar (lambda (binding) (let ((binding (eval binding))) `(define-key ,keymap-name (kbd ,(car binding)) #',(cdr binding)))) bindings))) `(progn (defvar ,keymap-name (make-sparse-keymap)) (defalias ',keymap-alias ,keymap-name) (global-set-key (kbd ,prefix) ',keymap-alias) ,@keymap-bindings)))
- Utilities for helping with scratch buffer
This exactly function was introduced at or before Emacs version 29.1.
(defun my--get-or-create-scratch () "Switch to *scratch* buffer. Create if it doesn't already exist" (interactive) (let ((s-buf (get-buffer "*scratch*"))) (switch-to-buffer (get-buffer-create "*scratch*")) (when (not s-buf) (emacs-lisp-mode))))
For firefox, I want to capture whatever I am reading and open the captured content in baby-window. But there is a fuck-up here that I’ve been unable to fix. On aborting the capture, the window layout gets messed up.
(defun my--baby-window (&optional baby-size) "Create a baby of active window of BABY-SIZE height. A baby-window is a small window below active-window, like DevConsole in a browser. Depending on the active-window, baby-window contains a different application. If the prefix-arg is nil, baby-window always open *scratch* buffer." (interactive) (let ((baby-window (split-window-below (or baby-size -20)))) (select-window baby-window) (my--get-or-create-scratch)))
- Start emacs as a server
(server-start)
- Unset annoying keybindings
(global-unset-key (kbd "C-x C-z")) (global-unset-key (kbd "C-z")) (global-unset-key (kbd "C-h h"))
- Set a custom-file so Emacs won’t put customized entries in my
init.el
which gets overwritten every time I tangle spookmax.d(setq custom-file (concat user-emacs-directory "custom.el"))
- Disable the ugly-ass toolbar, scroll-bars and menu-bar
(setq inhibit-startup-screen t ring-bell-function #'ignore use-dialog-box nil) (tool-bar-mode -1) (scroll-bar-mode -1) (menu-bar-mode -1) (tooltip-mode -1)
- Make emacs a little transparent
(set-frame-parameter (selected-frame) 'alpha '(98 . 95)) (add-to-list 'default-frame-alist '(alpha . (98 . 95)))
- Disable native-comp warnings
(setq native-comp-async-report-warnings-errors 'silent)
- UI fixes copied from Doom
https://github.com/hlissner/doom-emacs/blob/develop/core/core-ui.el
- Scrolling
;;; Scrolling (setq hscroll-margin 2 hscroll-step 1 ;; Emacs spends too much effort recentering the screen if you scroll the ;; cursor more than N lines past window edges (where N is the settings of ;; `scroll-conservatively'). This is especially slow in larger files ;; during large-scale scrolling commands. If kept over 100, the window is ;; never automatically recentered. scroll-conservatively 101 scroll-margin 0 scroll-preserve-screen-position t ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll' ;; for tall lines. auto-window-vscroll nil ;; mouse mouse-wheel-scroll-amount '(2 ((shift) . hscroll)) mouse-wheel-scroll-amount-horizontal 2)
- Cursors
;;; Cursor (blink-cursor-mode -1) ;; Don't blink the paren matching the one at point, it's too distracting. (setq blink-matching-paren nil) ;; Don't stretch the cursor to fit wide characters, it is disorienting, ;; especially for tabs. (setq x-stretch-cursor nil)
- Window/Frame
;; A simple frame title (setq frame-title-format '("%b") icon-title-format frame-title-format) ;; Don't resize the frames in steps; it looks weird, especially in tiling window ;; managers, where it can leave unseemly gaps. (setq frame-resize-pixelwise t) ;; But do not resize windows pixelwise, this can cause crashes in some cases ;; when resizing too many windows at once or rapidly. (setq window-resize-pixelwise nil) ;; Favor vertical splits over horizontal ones. Monitors are trending toward ;; wide, rather than tall. (setq split-width-threshold 160 split-height-threshold nil)
- Minibuffer
;; ;;; Minibuffer ;; Allow for minibuffer-ception. Sometimes we need another minibuffer command ;; while we're in the minibuffer. (setq enable-recursive-minibuffers t) ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any ;; feedback after typing is better UX than no feedback at all. (setq echo-keystrokes 0.02) ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This ;; doesn't look too great with direnv, however... (setq resize-mini-windows 'grow-only) ;; Typing yes/no is obnoxious when y/n will do (setf use-short-answers t) ;; Try to keep the cursor out of the read-only portions of the minibuffer. (setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) ;; Don't resize the frames in steps; it looks weird, especially in tiling window ;; managers, where it can leave unseemly gaps. (setq frame-resize-pixelwise t) ;; But do not resize windows pixelwise, this can cause crashes in some cases ;; when resizing too many windows at once or rapidly. (setq window-resize-pixelwise nil)
- Scrolling
- Allow selection to be deleted, generally expected behavior during
editing. I tried to not have this on by default, but I am finding
that to be increasingly annoying.
(delete-selection-mode +1)
- Indentation and whitespace
(setq my--indent-width 2) (setq-default tab-width my--indent-width) (setq-default indent-tabs-mode nil)
From: https://github.com/susam/emfy/blob/main/.emacs#L26
(setq-default indicate-empty-lines t) (setq-default indicate-buffer-boundaries 'left) ;; Consider a period followed by a single space to be end of sentence. (setq sentence-end-double-space nil) (setq create-lockfiles nil)
I got sick of manually calling whitespace cleanup all the trim. Cleanup whitespace.
(use-package whitespace-cleanup-mode :config (global-whitespace-cleanup-mode +1))
- Fill column for auto-formatting/filling paragraphs.
(setq-default fill-column 80)
- Introspection
Setup
which-key
for easy keys discovery(use-package which-key :config (which-key-mode t))
- Highlighting
(global-hl-line-mode +1) (use-package highlight-symbol :hook (prog-mode . highlight-symbol-mode) :config (setq highlight-symbol-idle-delay 0.3))
- Line numbers
(global-display-line-numbers-mode 1)
- Fix control characters in compilation-mode
Otherwise weird characters appear when fancy-ass CLI tools are executed.
(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
- Window management
- Custom window keybindings
(my--defkeymap "my-windows" "C-c s-w" '("-" . split-window-below) '("_" . my--baby-window) '("/" . split-window-right) '("d" . delete-window) '("m" . delete-other-windows) '("o" . other-window) '("h" . windmove-left) '("j" . windmove-down) '("k" . windmove-up) '("l" . windmove-right) '("w" . ace-window))
- Install ace-window for some nice utilities.
(defun my--aw-kill-buffer-in-window (win) "Kill the buffer shown in window WIN." (kill-buffer (window-buffer win))) (defun my--aw-kill-buffer-and-window (win) "Kill the buffer shown in window WIN and window itself." (kill-buffer (window-buffer win)) (delete-window win)) (use-package ace-window :config (setq aw-dispatch-always t) (global-set-key (kbd "C-c w") 'ace-window) (setq aw-dispatch-alist '((?d my--aw-kill-buffer-in-window "Kill buffer in window") (?s aw-swap-window "Swap Windows") (?S aw-move-window "Move Window") (?c aw-copy-window "Copy Window") (?w aw-flip-window) (?b aw-switch-buffer-in-window "Select Buffer") (?B aw-switch-buffer-other-window "Switch Buffer Other Window") (?k aw-delete-window "Delete Window") (?K my--aw-kill-buffer-and-window "Kill buffer in window") (?= aw-split-window-fair "Split Fair Window") (?- aw-split-window-vert "Split Vert Window") (?/ aw-split-window-horz "Split Horz Window") (?m delete-other-windows "Delete Other Windows") (?? aw-show-dispatch-help)) aw-keys '(?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)))
- Custom window keybindings
- Buffer management
(my--defkeymap "my-buffers" "C-c b" '("b" . switch-to-buffer) '("n" . next-buffer) '("p" . previous-buffer) '("n" . next-buffer) '("d" . kill-current-buffer) '("s" . my--get-or-create-scratch))
- Font size
(defvar my--font-size 11) (when (assoc 'small-screen my--feature-flags) (setq my--font-size 14)) (set-face-attribute 'default nil :height (* 10 my--font-size))
- [Ma]git
Magit uses
project-switch-commands
which are present only in more recent project.el project.(use-package project :config (setq popper-group-function #'popper-group-by-project)) (use-package magit)
- git-link so I can copy link to lines in files because evidently, I am doing that a lot
(use-package git-link :config (setf git-link-use-commit t))
- Buncha nice keybindings.
(my--defkeymap "my-git" "C-c g" '("s" . magit-status) '("b" . magit-blame) '("g" . magit-dispatch))
- Magit Forge
;; (use-package forge ;; :after magit)
- git-link so I can copy link to lines in files because evidently, I am doing that a lot
- Ability to select both parts from a diff (from SO)
(defun my--ediff-copy-both-to-C () (interactive) (ediff-copy-diff ediff-current-difference nil 'C nil (concat (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer) (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer)))) (defun add-d-to-ediff-mode-map () (define-key ediff-mode-map "d" 'my--ediff-copy-both-to-C)) (add-hook 'ediff-keymap-setup-hook 'add-d-to-ediff-mode-map)
- Keep backup/auto-save files out of my vc
(setq backup-dir "~/.emacs.d/bakups" backup-directory-alist `((".*" . ,backup-dir)) auto-save-file-name-transforms `((".*" ,backup-dir t)) create-lockfiles nil)
- Setup PATH from shell
(use-package exec-path-from-shell :config (exec-path-from-shell-initialize))
Libraries used elsewhere.
(use-package plz)
(use-package org
:config
(eval-after-load 'org-mode
(org-link-set-parameters
"yt"
:follow #'my-org--follow-yt-link
:export #'my-org--export-yt-link))
(add-hook
'org-mode-hook
(lambda () (display-line-numbers-mode -1))))
- Other settings
(setq org-startup-indented t org-startup-folded nil org-agenda-window-setup "only-window" org-directory "~/Documents/org" org-agenda-diary-file (concat org-directory "/diary.org.gpg") org-inbox-file (concat org-directory "/TODOs.org") org-agenda-files (list org-inbox-file (expand-file-name "work/on.org" org-directory)) ;;Todo keywords I need org-todo-keywords '((sequence "TODO(t!)" "DOING(n!)" "|" "DONE(d!)" "CANCELED(c@)")) org-todo-keyword-faces '(("DOING" . "DeepSkyBlue") ("CANCELED" . org-done)) org-default-notes-file (concat org-directory "/refile.org") org-refile-targets '((org-agenda-files . (:maxlevel . 6))) org-capture-templates '(("i" "Idea" entry (file+headline org-inbox-file "Inbox") "* %?\t\t:idea:\n") ("t" "Todo" entry (file+headline org-inbox-file "Inbox") "* TODO %?\n")) org-log-into-drawer "LOGBOOK" org-log-done "time" org-clock-report-include-clocking-task t org-clock-into-drawer t org-fontify-done-headline t org-enforce-todo-dependencies t org-agenda-overriding-columns-format "%80ITEM(Task) %6Effort(Est){:} %6CLOCKSUM_T(Today) %6CLOCKSUM(Total)" org-columns-default-format org-agenda-overriding-columns-format org-agenda-columns-add-appointments-to-effort-sum t org-use-property-inheritance t org-confirm-babel-evaluate nil org-id-link-to-org-use-id t org-fold-catch-invisible-edits 'show org-cycle-separator-lines 0 org-export-allow-bind-keywords t ;; let's try these out org-use-speed-commands t ;; Pressing `l' in org-agenda-mode show the log of all state changes. Something I need because ;; otherwise old-scheduled and repeated tasks that I do, or tasks I cancel gets lost from my agenda ;; view org-agenda-log-mode-items '(closed clock state)) ;; org-mode settings (with-eval-after-load 'org (org-indent-mode t) (require 'org-id))
- Keybindings
(global-set-key (kbd "C-c c") #'org-capture) (my--defkeymap "my-org" "C-c o" '("a" . org-agenda-list) '("A" . org-agenda) '("c" . org-capture) '("C" . org-clock-goto) '("o" . consult-org-agenda)) (with-eval-after-load 'org (define-key org-mode-map (kbd "C-c >") #'org-timestamp-inactive))
- org-super-agenda
More/better structure in agenda view.
(use-package org-super-agenda :config (org-super-agenda-mode t) (setq org-super-agenda-groups '((:name "In Progress" :todo "DOING" :order 1) (:name "🪫" :tag "system" :order 1) (:name "👶🏼" :category "Baby" :tag "baby" :order 2) (:name "Work" :tag "work" :order 2) (:name "🏯" :category "Home 🏯" :tag "apartment" :order 3) (:name "Projects" :tag "project" :order 3) (:name "Self ❤️" :tag "health" :category "Self" :order 3) (:name "Inbox" :tag "inbox" :order 4) (:name "Study 📚" :tag ("study" "reading") :order 4) (:name "Finance 💶" :tag "finance" :order 4) (:name "Habits" :tag "habit" :order 6))))
- org-babel
(with-eval-after-load 'org (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (plantuml . t) (shell . t) (sql . t) (sqlite . t) (lisp . t) (js . t))))
- Allow adding HTML class/id to exported src blocks
Org mode don’t allow adding custom HTML class or id to exported src blocks, but I’ve found myself in need of this functionality when customizing published projects.
(defun my--org-src-block-html-attrs-advice (oldfun src-block contents info) "Add class, id or data-* CSS attributes to html source block output. Allows class, id or data attributes to be added to a source block using #attr_html: #+ATTR_HTML: :class myclass :id myid #+begin_src python print(\"Hi\") #+end_src " (let* ((old-ret (funcall oldfun src-block contents info)) (class-tag (org-export-read-attribute :attr_html src-block :class)) (data-attr (let ((attr (org-export-read-attribute :attr_html src-block :data))) (when attr (split-string attr "=")))) (id-tag (org-export-read-attribute :attr_html src-block :id))) (if (or class-tag id-tag data-attr) (concat "<div " (if class-tag (format "class=\"%s\" " class-tag)) (if id-tag (format "id=\"%s\" " id-tag)) (if data-attr (format "data-%s=\"%s\" " (car data-attr) (cadr data-attr))) ">" old-ret "</div>") old-ret))) (advice-add 'org-html-src-block :around #'my--org-src-block-html-attrs-advice)
- Support exporting code blocks with syntax-highlighting
(use-package htmlize)
- Custom links
yt://
links- Open
yt://
links inmpv
if mpv is present - Open
yt://
links in browser if mpv isn’t installed or prefix-argument is provided withorg-open-at-point
(i.eC-c C-o
)
(defun my-org--follow-yt-link (path prefix) (let* ((url (format "https:%s" path)) (proc-name (format "*yt://%s*" url))) (if (and prefix (executable-find "mpv")) (browse-url url) (make-process :name proc-name :buffer proc-name :command `("mpv" ,url)) (message "Launched mpv in buffer: %s" proc-name)))) (defun my-org--export-yt-link (path desc backend) (when (eq backend 'html) (let* ((video-id (cadar (url-parse-query-string path))) (url (if (string-empty-p video-id) path (format "//youtube.com/embed/%s" video-id)))) (format "<iframe width=\"560\" height=\"315\" src=\"%s\" title=\"%s\" frameborder=\"0\" allowfullscreen></iframe>" url desc))))
- Open
- Reset checkboxes when a subtree is repeated
Taken from SO
(add-hook 'org-todo-repeat-hook #'org-reset-checkbox-state-subtree)
- Set tab-width for column mode
org-columns
is complaining that tab-width in org-mode buffers should be 8 not 2.(defun my--set-org-vars () (setf tab-width 8)) (add-hook 'org-agenda-mode-hook #'my--set-org-vars)
- Lib
Utility functions for working with org/denote that are used elsewhere in configuration.
(defun my--denote-files-with-tags (tags) "Return denote files which have all TAGS." (seq-filter (lambda (file) (null (seq-difference tags (denote-extract-keywords-from-path file)))) (denote-directory-files)))
Let’s get some modal editing with some spice. I have used Evil mode with Spacemacs, I was going to configure Evil, but let’s give meow a shot!
- Meow qwerty setup copied from https://github.com/meow-edit/meow/blob/master/KEYBINDING_QWERTY.org
(defun meow-setup () (setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty) (meow-motion-overwrite-define-key '("j" . meow-next) '("k" . meow-prev) '("<escape>" . ignore)) (meow-leader-define-key ;; SPC j/k will run the original command in MOTION state. '("j" . "H-j") '("k" . "H-k") ;; Use SPC (0-9) for digit arguments. '("1" . meow-digit-argument) '("2" . meow-digit-argument) '("3" . meow-digit-argument) '("4" . meow-digit-argument) '("5" . meow-digit-argument) '("6" . meow-digit-argument) '("7" . meow-digit-argument) '("8" . meow-digit-argument) '("9" . meow-digit-argument) '("0" . meow-digit-argument) ;; '("/" . meow-keypad-describe-key) '("?" . meow-cheatsheet)) (meow-normal-define-key '("0" . meow-expand-0) '("9" . meow-expand-9) '("8" . meow-expand-8) '("7" . meow-expand-7) '("6" . meow-expand-6) '("5" . meow-expand-5) '("4" . meow-expand-4) '("3" . meow-expand-3) '("2" . meow-expand-2) '("1" . meow-expand-1) '("-" . negative-argument) '(";" . meow-reverse) '("," . meow-inner-of-thing) '("." . meow-bounds-of-thing) '("[" . meow-beginning-of-thing) '("]" . meow-end-of-thing) '("a" . meow-append) '("A" . meow-open-below) '("b" . meow-back-word) '("B" . meow-back-symbol) '("c" . meow-change) '("d" . meow-delete) '("D" . meow-backward-delete) '("e" . meow-next-word) '("E" . meow-next-symbol) '("f" . meow-find) '("g" . meow-cancel-selection) '("G" . meow-grab) '("h" . meow-left) '("H" . meow-left-expand) '("i" . meow-insert) '("I" . meow-open-above) '("j" . meow-next) '("J" . meow-next-expand) '("k" . meow-prev) '("K" . meow-prev-expand) '("l" . meow-right) '("L" . meow-right-expand) '("m" . meow-join) '("n" . meow-search) '("o" . meow-block) '("O" . meow-to-block) '("p" . meow-yank) ;; '("q" . meow-quit) ;; '("Q" . meow-goto-line) '("r" . meow-replace) '("R" . meow-swap-grab) '("s" . meow-kill) '("t" . meow-till) '("u" . meow-undo) '("U" . meow-undo-in-selection) '("v" . meow-visit) '("w" . meow-mark-word) '("W" . meow-mark-symbol) '("x" . meow-line) ;; '("X" . meow-goto-line) '("y" . meow-save) '("Y" . meow-sync-grab) '("z" . meow-pop-selection) '("'" . repeat) '("<escape>" . ignore)))
(use-package meow
:config
(setf meow-use-clipboard t)
(meow-global-mode)
(meow-setup))
- Normal mode-keybindings. Mostly mimicking vim
(meow-normal-define-key '("z" . my-fold) '("/" . "C-s") '("?" . "C-r"))
- Leader keybindings
(meow-leader-define-key '("/" . consult-git-grep) '("p" . "C-x p") '("e" . flycheck-command-map) '("w" . ace-window) '("b" . my-buffers) '("G" . my-git) '("o" . my-org) '("n" . my-notes))
- Keychords
(use-package key-chord :config (setf key-chord-two-keys-delay 0.1) (key-chord-mode 1) (key-chord-define meow-insert-state-keymap "fd" #'meow-insert-exit))
- Orderlies adds matches completion candidates by space-separated patterns in
any order
(use-package orderless :config (setq completion-styles '(orderless)))
- Vertico for completion UI
(use-package vertico) (require 'vertico) (with-eval-after-load 'vertico (vertico-mode +1) (define-key vertico-map (kbd "C-c ?") #'minibuffer-completion-help)) (use-package vertico-directory :ensure nil :straight nil :after vertico ;; More convenient directory navigation commands :bind (:map vertico-map ("C-h" . vertico-directory-delete-word)) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) (use-package vertico-quick :ensure nil :straight nil :after vertico :bind (:map vertico-map ("C-q" . vertico-quick-insert)) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) ;; Persist history over Emacs restarts. Vertico sorts by history position. (use-package savehist :ensure nil :straight nil :init (savehist-mode +1)) ;; Emacs 28: Hide commands in M-x which do not work in the current mode. ;; Vertico commands are hidden in normal buffers. (setq read-extended-command-predicate #'command-completion-default-include-p)
- Marginalia adds pretty information to completions. It’s pretty, useful, and
recommended by
embark
(it provides extra information toembark
);; Enable richer annotations using the Marginalia package (use-package marginalia) (require 'marginalia) (with-eval-after-load 'marginalia (define-key minibuffer-local-map (kbd "M-A") #'marginalia-cycle) (marginalia-mode +1))
- Consult for enhanced commands
(use-package consult :config (consult-customize consult-theme :preview-key '(:debounce 0.5 any)) (global-set-key (kbd "C-s") #'isearch-forward-regexp) (global-set-key (kbd "C-S-s") #'consult-line) (global-set-key (kbd "C-r") #'isearch-backward-regexp) (global-set-key (kbd "C-S-r") #'isearch-backward-regexp) (global-set-key (kbd "C-x b") #'consult-buffer) (define-key my-buffers-keymap (kbd "b") #'consult-buffer) (define-key my-buffers-keymap (kbd "B") #'consult-buffer-other-window) ;; better yank which show kill-ring for selection (global-set-key (kbd "C-y") #'consult-yank-pop) (meow-leader-define-key '("/" . consult-ripgrep)) (meow-normal-define-key '("p" . consult-yank-pop) '("Q" . consult-goto-line) '("X" . consult-focus-lines))) (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref) (recentf-mode +1) (use-package consult-flycheck :config (define-key flycheck-command-map (kbd "l") #'consult-flycheck)) (use-package embark-consult :after (embark consult) :demand t :hook (embark-collect-mode . consult-preview-at-point-mode))
- embark allow contextual actions, like opening buffers in other window from
minibuffer and a lot more
(defun my--embark-act-no-quit () "(embark-act), but don't quit the minibuffer" (interactive) (let ((embark-quit-after-action nil)) (embark-act))) (use-package embark :bind (("C-," . embark-act) ("C->" . embark-act-all) ("C-." . embark-dwim) ("C-h b" . embark-bindings) ("C-<" . my--embark-act-no-quit)))
wgrep
for editing grep buffers(use-package wgrep)
undo-tree-mode
for more powerful undo(use-package undo-tree :config (global-undo-tree-mode t) (global-set-key (kbd "C-/") #'undo) (global-set-key (kbd "C-S-/") #'undo-tree-redo) (setq undo-tree-history-directory-alist `(("." . ,(expand-file-name ".cache" user-emacs-directory)))))
embrace
for wrapping pair manipulation(let ((embrace-dir (expand-file-name "~/code/emacs/embrace.el"))) (when (file-exists-p embrace-dir) (add-to-list 'load-path embrace-dir) (require 'embrace) (with-eval-after-load 'embrace (add-hook 'org-mode-hook 'embrace-org-mode-hook)) (meow-normal-define-key '("S" . embrace-commander))))
- yasnippet for templates
(require 'yasnippet) (with-eval-after-load 'yasnippet (add-hook 'prog-mode-hook #'yas-minor-mode)) (use-package yasnippet-snippets :after yasnippet)
- Show trailing whitespace in programming files
(add-hook 'prog-mode-hook #'(lambda () (setq-local show-trailing-whitespace t)))
- Wrapping text in parens, quotes etc
(show-paren-mode 1) (electric-pair-mode 1)
- Code folding
(my--defkeymap "my-fold" "C-c f" '("b" . hs-hide-block) '("O" . hs-show-block) '("l" . hs-hide-level) '("L" . hs-show-block) '("a" . hs-hide-all) '("A" . hs-show-all) '("z" . hs-toggle-hiding)) (add-hook 'prog-mode-hook 'hs-minor-mode)
- Flycheck for getting those in-buffer warnings errors.
(use-package flycheck :init (global-flycheck-mode t) ;; alias is needed for using the keymap in meow (defalias 'flycheck-command-map flycheck-command-map))
Flycheck eglot integration
(straight-use-package 'flycheck-eglot) (with-eval-after-load 'flycheck-eglot (global-flycheck-eglot-mode 1))
- Completions
I think I have a general idea of what it does, but still fuzzy on details. This stuff is usually taken for granted; I’ve been taking it for granted with Spacemacs for a while now I suppose.
(straight-use-package '(nerd-icons-corfu :host github :repo "LuigiPiucco/nerd-icons-corfu")) (use-package corfu :config (setf corfu-auto t) (global-corfu-mode t) (corfu-popupinfo-mode t) (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
- Reformatter allow creating buffer/region formatters from any command.
(use-package reformatter :config (reformatter-define prettier-format :program (expand-file-name "node_modules/.bin/prettier" (locate-dominating-file (buffer-file-name) "node_modules/.bin/prettier")) :args `("--stdin-filepath" ,(buffer-file-name))) :hook ((web-mode . prettier-format-on-save-mode) (typescript-ts-mode . prettier-format-on-save-mode)))
- Direnv is pretty essential for my dev workflow.
(use-package direnv :config (direnv-mode) (when (not (boundp 'warning-suppress-types)) (setq warning-suppress-types nil)) (add-to-list 'warning-suppress-types '(direnv)))
- Eglot to provide LSP support.
;; Need to be manuall installed so we get latest version ;; (use-package jsonrpc ;; :ensure '(jsonrpc :repo "https://git.savannah.gnu.org/git/emacs.git" ;; :files ("lisp/jsonrpc.el"))) ;; (use-package eldoc ;; :ensure '(eldoc :repo "https://git.savannah.gnu.org/git/emacs.git" ;; :files ("lisp/emacs-lisp/eldoc.el"))) (use-package eglot :straight nil :ensure nil) ;; Looks like jsonrpc logging make eglot super laggy for typescript. ;; https://old.reddit.com/r/emacs/comments/1447fy2/looking_for_help_in_improving_typescript_eglot/ ;; https://www.reddit.com/r/emacs/comments/16vixg6/how_to_make_lsp_and_eglot_way_faster_like_neovim/ (fset #'jsonrpc--log-event #'ignore) (setq eglot-events-buffer-size 0 eglot-sync-connect nil eglot-connect-timeout nil company-idle-delay 0 company-minimum-prefix-length 1) (add-hook 'focus-out-hook 'garbage-collect)
(setq treesit-language-source-alist
'((bash "https://github.com/tree-sitter/tree-sitter-bash")
(cmake "https://github.com/uyha/tree-sitter-cmake")
(css "https://github.com/tree-sitter/tree-sitter-css")
(elisp "https://github.com/Wilfred/tree-sitter-elisp")
(go "https://github.com/tree-sitter/tree-sitter-go")
(html "https://github.com/tree-sitter/tree-sitter-html")
(javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
(json "https://github.com/tree-sitter/tree-sitter-json")
(make "https://github.com/alemuller/tree-sitter-make")
(markdown "https://github.com/ikatyang/tree-sitter-markdown")
(python "https://github.com/tree-sitter/tree-sitter-python")
(toml "https://github.com/tree-sitter/tree-sitter-toml")
(tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
(yaml "https://github.com/ikatyang/tree-sitter-yaml")))
- Structured movement
(use-package combobulate :custom ;; You can customize Combobulate's key prefix here. ;; Note that you may have to restart Emacs for this to take effect! (setf combobulate-key-prefix "C-c j") ;; Optional, but recommended. ;; ;; You can manually enable Combobulate with `M-x ;; combobulate-mode'. :hook ((python-ts-mode . combobulate-mode) (js-ts-mode . combobulate-mode) (html-ts-mode . combobulate-mode) (css-ts-mode . combobulate-mode) (yaml-ts-mode . combobulate-mode) (typescript-ts-mode . combobulate-mode) (json-ts-mode . combobulate-mode) (tsx-ts-mode . combobulate-mode)))
Lispy for some nasty lisp structural editing.
(use-package lispy
:hook ((emacs-lisp-mode . lispy-mode)
(lisp-mode . lispy-mode)
(scheme-mode . lispy-mode))
:config
(setf lispy-colon-p nil))
Pretty cool auto-indentation.
(use-package aggressive-indent
:hook ((emacs-lisp-mode . aggressive-indent-mode)
(lisp-mode . aggressive-indent-mode)))
- Common Lisp
Sly for interactive development.
(use-package sly :hook ((lisp-mode . sly-mode)) :config (setq org-babel-lisp-eval-fn #'sly-eval inferior-lisp-program "sbcl") (add-hook 'sly-mrepl-hook (lambda () (set-face-foreground 'sly-mrepl-output-face "khaki3"))))
sly-asdf add asdf integration to sly.
(use-package sly-asdf :config (add-to-list 'sly-contribs 'sly-asdf 'append))
- Helper utilities
- Are we using nvm?
(defun my--nvm-p () (when-let* ((node (string-trim (shell-command-to-string "fish -c 'readlink (which node)'"))) (nvm-bin-dir (and (string-match-p "\/nvm\/" node) (file-name-directory node)))) nvm-bin-dir))
- Are we using nvm?
(setq css-indent-offset my--indent-width)
(use-package js-ts
:mode "\\.js'"
:straight nil
:ensure nil
:config
(setq js-indent-level my--indent-width)
:hook
(((js-ts-mode
typescript-ts-mode) . subword-mode)))
(use-package web-mode
:mode (("\\.html?\\'" . web-mode))
:config
(setq web-mode-markup-indent-offset my--indent-width)
(setq web-mode-code-indent-offset my--indent-width)
(setq web-mode-css-indent-offset my--indent-width)
(setq web-mode-content-types-alist '(("jsx" . "\\.js[x]?\\'"))))
(use-package emmet-mode
:hook ((html-mode . emmet-mode)
(css-mode . emmet-mode)
(js-ts-mode . emmet-mode)
(js-jsx-mode . emmet-mode)
(typescript-ts-mode . emmet-mode)
(tsx-ts-mode . emmet-mode)
(web-mode . emmet-mode))
:config
(setq emmet-insert-flash-time 0.001) ; effectively disabling it
(add-hook 'js-jsx-mode-hook #'(lambda ()
(setq-local emmet-expand-jsx-className? t)))
(add-hook 'web-mode-hook #'(lambda ()
(setq-local emmet-expand-jsx-className? t))))
(my--defkeymap
"my-errors" "C-c e"
'("n" . flycheck-next-error)
'("p" . flycheck-previous-error)
'("l" . flycheck-list-errors)
'("e" . flycheck-explain-error-at-point))
(defun my--setup-ts-js ()
"Setup Javascript and Typescript for current buffer."
;; Add node_modules/.bin of current project to exec-path.
(if-let (nvm-bin (my--nvm-p))
(add-to-list 'exec-path nvm-bin)
(let ((bin-dir
(expand-file-name
"node_modules/.bin/"
(locate-dominating-file default-directory "node_modules"))))
(when (file-exists-p bin-dir)
(add-to-list 'exec-path bin-dir))))
(subword-mode)
;; For 95% of cases this is what I want
(prettier-format-on-save-mode)
(setf flymake-eslint-project-root
(locate-dominating-file default-directory "package.json"))
(eglot-ensure))
(add-hook 'js-ts-mode-hook #'my--setup-ts-js)
(use-package typescript-ts-mode
:mode "\\.ts\\'"
:config
(setq-default typescript-indent-level my--indent-width)
(add-hook 'typescript-mode-hook #'my--setup-ts-js)
(add-hook 'typescriptt-ts-mode-hook #'my--setup-ts-js))
(use-package tsx-ts-mode
:mode "\\.tsx\\'"
:straight nil
:ensure nil
:hook ((tsx-ts-mode . subword-mode)
(tsx-ts-mode . my--setup-ts-js))
:config
(setq typescript-indent-level my--indent-width))
(use-package css-ts-mode
:ensure nil
:straight nil
:mode "\\.s?css\\'")
- JSON support
(use-package json-ts-mode :mode "\\.json\\'")
;; (use-package rustic
;; :init
;; (setq rustic-cargo-bin "cargo")
;; (push 'rustic-clippy flycheck-checkers))
(use-package haskell-mode
:mode "\\.hs\\'"
:config
(add-hook 'haskell-mode-hook #'subword-mode)
(define-key haskell-mode-map (kbd "C-c , c") #'haskell-process-load-or-reload)
(define-key haskell-mode-map (kbd "C-c , s") #'haskell-interactive-switch)
(define-key haskell-mode-map (kbd "C-c , l") #'haskell-interactive-mode-clear)
(define-key haskell-mode-map (kbd "C-c , T") #'haskell-doc-show-type)
(define-key haskell-mode-map (kbd "C-c , t") #'haskell-mode-show-type-at))
(use-package yaml-mode
:mode "\\.ya?ml\\'")
(use-package graphql-mode
:mode "\\.graphql\\'")
Nice to have features but not necessary.
- Ace Jump for quickly jumping around in a buffer
(my--defkeymap "my-jump" "C-c q" '("q" . ace-jump-mode) '("w" . ace-jump-word-mode)) (use-package ace-jump-mode)
- Treemacs for easy code exploration
(use-package treemacs :defer t :init (with-eval-after-load 'winum (define-key winum-keymap (kbd "M-0") #'treemacs-select-window)) :config (progn (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0) treemacs-deferred-git-apply-delay 0.5 treemacs-directory-name-transformer #'identity treemacs-display-in-side-window t treemacs-eldoc-display 'simple treemacs-file-event-delay 2000 treemacs-file-extension-regex treemacs-last-period-regex-value treemacs-file-follow-delay 0.2 treemacs-file-name-transformer #'identity treemacs-follow-after-init t treemacs-expand-after-init t treemacs-find-workspace-method 'find-for-file-or-pick-first treemacs-git-command-pipe "" treemacs-goto-tag-strategy 'refetch-index treemacs-header-scroll-indicators '(nil . "^^^^^^") treemacs-hide-dot-git-directory t treemacs-indentation 2 treemacs-indentation-string " " treemacs-is-never-other-window nil treemacs-max-git-entries 5000 treemacs-missing-project-action 'ask treemacs-move-forward-on-expand nil treemacs-no-png-images nil treemacs-no-delete-other-windows t treemacs-project-follow-cleanup nil treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) treemacs-position 'left treemacs-read-string-input 'from-child-frame treemacs-recenter-distance 0.1 treemacs-recenter-after-file-follow nil treemacs-recenter-after-tag-follow nil treemacs-recenter-after-project-jump 'always treemacs-recenter-after-project-expand 'on-distance treemacs-litter-directories '("/node_modules" "/.venv" "/.cask") treemacs-show-cursor nil treemacs-show-hidden-files t treemacs-silent-filewatch nil treemacs-silent-refresh nil treemacs-sorting 'alphabetic-asc treemacs-select-when-already-in-treemacs 'move-back treemacs-space-between-root-nodes t treemacs-tag-follow-cleanup t treemacs-tag-follow-delay 1.5 treemacs-text-scale nil treemacs-user-mode-line-format nil treemacs-user-header-line-format nil treemacs-wide-toggle-width 70 treemacs-width 35 treemacs-width-increment 1 treemacs-width-is-initially-locked t treemacs-workspace-switch-cleanup nil) ;; The default width and height of the icons is 22 pixels. If you are ;; using a Hi-DPI display, uncomment this to double the icon size. ;;(treemacs-resize-icons 44) (treemacs-follow-mode t) (treemacs-filewatch-mode t) (treemacs-fringe-indicator-mode 'always) ;; (when treemacs-python-executable ;; (treemacs-git-commit-diff-mode t)) (pcase (cons (not (null (executable-find "git"))) (not (null treemacs-python-executable))) (`(t . t) (treemacs-git-mode 'deferred)) (`(t . _) (treemacs-git-mode 'simple))) (treemacs-hide-gitignored-files-mode nil)) :bind (:map global-map ("M-0" . treemacs-select-window) ("C-x t 1" . treemacs-delete-other-windows) ("C-x t t" . treemacs) ("C-x t d" . treemacs-select-directory) ("C-x t B" . treemacs-bookmark) ("C-x t C-t" . treemacs-find-file) ("C-x t M-t" . treemacs-find-tag))) (use-package treemacs-magit :after (treemacs magit)) (use-package treemacs-all-the-icons :config (treemacs-load-theme "all-the-icons"))
- Highlight indentation
(use-package highlight-indent-guides :config (setf highlight-indent-guides-method 'bitmap) (add-hook 'prog-mode-hook 'highlight-indent-guides-mode))
- Move text around
(use-package move-text :config (move-text-default-bindings))
;; (use-package doom-themes
;; :config
;; (setq doom-rouge-brighter-modeline t
;; doom-rouge-brighter-comments t)
;; (load-theme 'doom-rouge t))
;; (use-package nimbus-theme
;; :config
;; (load-theme 'nimbus t))
(use-package modus-themes
:config
(load-theme 'modus-vivendi-tinted :no-confirm))
Modeline
;; (use-package doom-modeline
;; :config
;; (setq doom-modeline-height 24)
;; (doom-modeline-mode 1))
Let’s also try smooth-scrolling.
(pixel-scroll-precision-mode t)
Non crucial things which should be loaded last. If they fail, nothing crucial is blocked.
(my--defkeymap "my-apps" "C-c a")
- Spell checking
(with-eval-after-load "ispell" (setq ispell-program-name "hunspell") (setq ispell-dictionary "en_US,de_DE") (ispell-set-spellchecker-params) (ispell-hunspell-add-multi-dic "en_US,de_DE") (setq ispell-personal-dictionary "~/.emacs.d/.hunspell_per_dic"))
(use-package flyspell :hook (text-mode . flyspell-mode) (prog-mode . flyspell-prog-mode) :config (define-key flyspell-mode-map (kbd "C-,") nil) (define-key flyspell-mode-map (kbd "C-.") nil) (define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper)) (use-package flyspell-correct :after (flyspell) :commands (flyspell-correct-at-point flyspell-correct-wrapper))
- Notes using denotes
(setq denote-directory (expand-file-name "denotes" org-directory) denote-date-prompt-use-org-read-date t) (use-package denote :config (add-hook 'dired-mode-hook #'denote-dired-mode))
- Enhance denote a bit, don’t know why these aren’t a part of denote itself.
(defun my--denote-split-org-subtree (&optional prefix) "Create new Denote note as an Org file using current Org subtree." (interactive "P") (let ((text (org-get-entry)) (heading (org-get-heading :no-tags :no-todo :no-priority :no-comment)) (tags (org-get-tags)) (subdir (when prefix (denote-subdirectory-prompt)))) (delete-region (org-entry-beginning-position) (org-entry-end-position)) (denote heading tags 'org subdir) (insert text)))
- Setup for taking notes for reading/video-watching I do in Firefox.
Moved to denote
- CRM
(defvar crm-directory (expand-file-name "crm" denote-directory)) (defun my-crm--open-or-create () "Find or create CRM entry." (interactive) (let ((denote-directory crm-directory)) (call-interactively #'denote-open-or-create))) (defun my-crm--link-or-create () "Find or create CRM entry." (interactive) (let ((denote-directory crm-directory)) (call-interactively #'denote-link-or-create)))
- Keyboard shortcuts for fluent note-taking/reading
(my--defkeymap "my-notes" "C-c n" '("n" . denote-open-or-create) '("N" . denote-link-or-create) '("b" . denote-backlinks) '("d" . my--diary-today) '("p" . my-crm--open-or-create) '("P" . my-crm--link-or-create) '("m" . my--micro-post))
- Diary
(defun my--find-habit (title) "Find the habit with TITLE in current buffer." (cl-block 'my--find-habit (org-map-entries (lambda () (let ((el (org-element-at-point-no-context))) (when (and (seq-contains-p (org-get-tags el) "habit" #'equal) (equal (downcase (org-element-property :raw-value el)) (downcase title))) (cl-return-from 'my--find-habit el))))))) (defun my--mark-habit-as-done (habit file) "Mark HABIT as done." (with-current-buffer (find-file-noselect file) (org-mode) (let ((el (cl-case habit (diary (my--find-habit "write diary entry"))))) (goto-char (org-element-property :begin el)) (org-todo 'done)))) (defun my--diary-today () "Go to today's diary entry." (interactive) (let* ((diary-habit-file (denote-get-path-by-id "20241231T055317")) (denote-directory (expand-file-name "diary" denote-directory)) (title (format-time-string "%Y-%m-%d"))) (if-let ((file (seq-find (lambda (f) (string-match-p title f)) (directory-files denote-directory)))) (progn (find-file (expand-file-name file denote-directory)) (goto-char (point-max))) (my--mark-habit-as-done 'diary diary-habit-file) (denote title '("diary")))))
- Work notes
(defun my--workday-notes (prefix) "Go to work notes for today plus PREFIX days." (interactive "P") (let* ((days (if prefix (prefix-numeric-value prefix) 0)) (denote-directory (expand-file-name "work" denote-directory)) (date (time-add (current-time) (days-to-time days))) (title (format-time-string "%Y-%m-%d" date))) (if-let ((file (seq-find (lambda (f) (string-match-p title f)) (directory-files denote-directory))) (file (expand-file-name file denote-directory))) (progn (find-file file) ;; Remove any other denotes/work file from agenda ;; Assuming that this will always remove older workday files (setf org-agenda-files (seq-filter (lambda (file) (not (string-match-p "denotes/work" file))) org-agenda-files)) (org-agenda-file-to-front file) (goto-char (point-max))) (denote title '("work") "org" nil title)))) (my--defkeymap "workday" "C-c n w" '("w" . my--workday-notes) '("i" . on-issue-note-open-or-create) '("I" . on-issue-note-link-or-create))
- Enhance denote a bit, don’t know why these aren’t a part of denote itself.
- dirvish for more powerful dired
(use-package all-the-icons) (use-package dirvish) (with-eval-after-load 'dirvish (dirvish-override-dired-mode) ;; (setq dirvish-attributes ;; '(vc-state subtree-state all-the-icons collapse file-size)) (global-set-key (kbd "C-c f") #'dirvish-fd) (define-key dirvish-mode-map (kbd "/") #'dirvish-narrow) (define-key dirvish-mode-map (kbd "a") #'dirvish-quick-access) (define-key dirvish-mode-map (kbd "f") #'dirvish-file-info-menu) (define-key dirvish-mode-map (kbd "y") #'dirvish-yank-menu) (define-key dirvish-mode-map (kbd "N") #'dirvish-narrow) (define-key dirvish-mode-map (kbd "^") #'dirvish-history-last) (define-key dirvish-mode-map (kbd "h") #'dirvish-history-jump) ; remapped `describe-mode' (define-key dirvish-mode-map (kbd "s") #'dirvish-quicksort) ; remapped `dired-sort-toggle-or-edit' ;; (define-key dirvish-mode-map (kbd "v") #'dirvish-vc-menu) ; remapped `dired-view-file' (define-key dirvish-mode-map (kbd "TAB") #'dirvish-subtree-toggle) (define-key dirvish-mode-map (kbd "M-f") #'dirvish-history-go-forward) (define-key dirvish-mode-map (kbd "M-b") #'dirvish-history-go-backward) (define-key dirvish-mode-map (kbd "M-l") #'dirvish-ls-switches-menu) (define-key dirvish-mode-map (kbd "M-m") #'dirvish-mark-menu) (define-key dirvish-mode-map (kbd "M-t") #'dirvish-layout-toggle) (define-key dirvish-mode-map (kbd "M-s") #'dirvish-setup-menu) (define-key dirvish-mode-map (kbd "M-e") #'dirvish-emerge-menu) (define-key dirvish-mode-map (kbd "M-j") #'dirvish-fd-jump))
- Ledger
(use-package ledger-mode :mode "\\.ledger\\'" :config (setq ledger-default-date-format ledger-iso-date-format))
- spookfox
(setq spookfox-dir (expand-file-name "~/code/spookfox")) (when (file-exists-p spookfox-dir) (add-to-list 'load-path (expand-file-name "lisp" spookfox-dir)) (add-to-list 'load-path (expand-file-name "lisp/apps" spookfox-dir)) ;; Manually load spookfox dependencies (use-package websocket) (require 'spookfox) (require 'spookfox-tabs) (require 'spookfox-windows) (require 'spookfox-js-injection) (eval-after-load 'spookfox (spookfox-start-server)) (defun my--switch-tab-and-focus () "Switch to browser tab and bring browser in focus." (interactive) (spookfox-switch-tab) (when (eq 'darwin system-type) (ns-do-applescript "tell application \"Firefox\"\n\tactivate\n\tend tell"))) (define-key my-buffers-keymap (kbd "t") #'my--switch-tab-and-focus))
- saunf
Use the local repo; very risky, should change.
(let ((saunf-dir (expand-file-name "~/code/saunf/src"))) (when (file-exists-p saunf-dir) (add-to-list 'load-path saunf-dir) (require 'saunf)))
- org-noter
(use-package nov :mode ("\\.epub\\'" . nov-mode) :config (setq nov-shr-rendering-functions '((img . nov-render-img) (title . nov-render-title))) (setq nov-shr-rendering-functions (append nov-shr-rendering-functions shr-external-rendering-functions))) (when (assoc 'powered-by-guix my--feature-flags) (require 'org-pdftools) (require 'pdf-tools) (require 'org-noter))
- eshell
I sometimes open eshell buffers. Following kbd actually clears the eshell buffer, which is something I like to do in my terminals.
(require 'esh-mode) (with-eval-after-load 'esh-mode (define-key eshell-mode-map (kbd "C-c M-o") (lambda () (interactive) (eshell/clear-scrollback) (eshell-send-input ))))
- Irc
Small utility to quickly connect to irc.
(use-package erc :config (setopt erc-modules (seq-union '(sasl) erc-modules)) (setq erc-nick "bitspook" erc-auth-source-server-function (lambda (a b) (auth-source-pass-get 'secret "libera.chat/bitspook")) erc-sasl-user :nick erc-autojoin-channels-alist '(("libera.chat" "#commonlisp" "#emacs" "#emacs-berlin" "#clschool" "#whereiseveryone" "#guix" "#lisp" ;; just explorin' "#berlin" "##deutsch"))) (setf erc-track-exclude-types '("JOIN" "KICK" "NICK" "PART" "QUIT" "MODE" "333" "353"))) (use-package erc-track :ensure nil :straight nil ;; Prevent JOINs and PARTs from lighting up the mode-line. :config (setopt erc-track-faces-priority-list (remq 'erc-notice-face erc-track-faces-priority-list)) :custom (erc-track-priority-faces-only 'all))
- Terraform
(use-package terraform-mode :config (add-hook 'terraform-mode-hook #'terraform-format-on-save-mode))
- Eww
(setf eww-readable-urls '(".*")) (add-hook 'eww-mode-hook (lambda () (display-line-numbers-mode -1)))
Syntax-highlighting for code blocks in HTML.
(use-package shr-tag-pre-highlight :straight t :after shr :config (add-to-list 'shr-external-rendering-functions '(pre . shr-tag-pre-highlight)))
Make EWW buffer niceer with org-outline like features.
(use-package shrface :straight t :config (shrface-basic) (shrface-trial) (shrface-default-keybindings) ; setup default keybindings (setq shrface-href-versatile t) ;; configure eww (add-hook 'eww-after-render-hook #'shrface-mode))
- org-download to easily attach images in my notes
(use-package org-download :init (setq-default org-download-method 'attach org-download-image-dir (expand-file-name "data" org-directory)) :config ;; Drag-and-drop to `dired` (add-hook 'dired-mode-hook 'org-download-enable))
- Ement
(use-package ement) (define-key my-apps-keymap (kbd "e") (define-keymap :parent my-apps-keymap :name "Ement" (kbd "e") #'ement-room-list (kbd "n") #'ement-notifications))
- Libretranslate for translating Deutsch from Emacs
(straight-use-package '(libretrans :host codeberg :repo "martianh/libretrans.el")) (setq libretrans-instance "http://red:5005/translate" libretrans-source "de" libretrans-target "en")
(let ((private-config (expand-file-name "./private.el" user-emacs-directory)))
(when (file-exists-p private-config)
(load-file private-config)))
I am experimenting with putting my configuration in blog posts (called “workflows”). These posts
are the source of truth for the configuration, which is tangled to ~/.emacs.d/workflow-*.el
files. Let’s load all of ‘em.
(let ((root-workflow (expand-file-name "init-workflows.el" user-emacs-directory)))
(when (file-exists-p root-workflow)
(load-file root-workflow)))