diff options
| author | cashmere <cashmere@autistici.org> | 2026-08-23 19:19:17 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-08-23 19:19:17 +0200 |
| commit | 15def4cc1edbd5a80973fa9c5bba35d5eb32c340 (patch) | |
| tree | a9172e36e74a6691738b7d5012289cec1767241d /config.el | |
| parent | e8b0abed0711cefcd0c06b8e12e3a7a9224fb329 (diff) | |
| download | emacs-15def4cc1edbd5a80973fa9c5bba35d5eb32c340.tar.gz | |
feat: refresh Emacs UI and lock Elpaca packages
Diffstat (limited to 'config.el')
| -rw-r--r-- | config.el | 1190 |
1 files changed, 659 insertions, 531 deletions
@@ -59,6 +59,25 @@ Set per-host in the gitignored `local.el'.") (funcall orig name _keyword args))) (advice-add 'use-package-normalize/:ensure :around #'my/elpaca-skip-local) +(defun my/elpaca-write-lock-file () + "Write exact revisions of all queued packages to `elpaca-lock-file'." + (interactive) + (unless elpaca-lock-file + (user-error "`elpaca-lock-file' is not configured")) + (elpaca-wait) + (elpaca-write-lock-file elpaca-lock-file) + ;; Make the newly written revisions visible to this Emacs session too. + (elpaca-menu-lock-file 'update) + (message "Elpaca lock file updated: %s" + (abbreviate-file-name elpaca-lock-file))) + +(defun my/elpaca-visit-lock-file () + "Visit the configured `elpaca-lock-file'." + (interactive) + (unless elpaca-lock-file + (user-error "`elpaca-lock-file' is not configured")) + (find-file elpaca-lock-file)) + (use-package emacs :ensure nil :custom @@ -213,8 +232,7 @@ Selects the compilation window so the cursor lands in it." "Focus the async-shell window and make q bury it without killing the process." (select-window window) (with-current-buffer (window-buffer window) - (evil-local-set-key 'normal (kbd "q") #'quit-window) - (evil-local-set-key 'motion (kbd "q") #'quit-window) + (my/async-shell-command-bindings) (evil-normal-state))) (use-package window @@ -276,11 +294,6 @@ Selects the compilation window so the cursor lands in it." (mailcap-add "image/jpg" "kitty --hold kitty +kitten icat %s") (mailcap-add "image/gif" "kitty --hold kitty +kitten icat %s") - (define-key dired-mode-map (kbd "E") - (lambda () - (interactive) - (mailcap-view-file (dired-get-filename)))) - (advice-add 'dired-create-directory :around (lambda (orig-fun dirname) (let ((current-buffer (current-buffer))) @@ -295,25 +308,11 @@ Selects the compilation window so the cursor lands in it." (use-package async :ensure t) -(use-package general - :ensure (:wait t) - :demand t - :config - (general-evil-setup)) - -(eval-and-compile - (require 'general) - - (general-create-definer my-leader - :states '(normal visual insert) - :keymaps 'override - :prefix "SPC" - :non-normal-prefix "M-SPC") +(defvar-keymap my/leader-map + :doc "Global leader map used from Evil states.") - (general-create-definer my-local-leader - :states '(normal visual) - :keymaps 'override - :prefix ",")) +(defvar-keymap my/local-leader-map + :doc "Global local-leader map used from Evil states.") ;; Always-available IRC entry points so `M-x run-irc` works from any daemon ;; (hub, work, standalone). Calling `erc-tls` autoloads ERC on demand. @@ -507,24 +506,7 @@ Temporarily disables notifications during the fetch." (yes-or-no-p (format "ERC buffer %s is connected. Really kill (will part channel)? " (buffer-name))) - t))) - - (general-def 'normal erc-mode-map - "q" 'quit-window - "Q" 'my/erc-quit-all - "gb" 'my/erc-switch-channel - "gn" 'erc-track-switch-buffer - "gH" 'my/erc-fetch-history - "go" 'erc-channel-names - "gj" 'erc-join-channel - "gl" 'my/erc-list-channels - "gr" 'my/erc-reconnect - "RET" 'erc-send-current-line) - - (general-def '(normal insert) erc-mode-map - "M-n" 'erc-track-switch-buffer - "C-k" 'erc-previous-command - "C-j" 'erc-next-command)) + t)))) (defvar my/centered-cursor-enabled nil) @@ -591,18 +573,11 @@ Temporarily disables notifications during the fetch." (setq isearch-lazy-count t) ;; Enable lazy counting to show current match information. (setq lazy-count-prefix-format "(%s/%s) ") ;; Format for displaying current match count. (setq lazy-count-suffix-format nil) ;; Disable suffix formatting for match count. - (setq search-whitespace-regexp ".*?") ;; Allow searching across whitespace. - :bind (("C-s" . isearch-forward) ;; Bind C-s to forward isearch. - ("C-r" . isearch-backward))) ;; Bind C-r to backward isearch. + (setq search-whitespace-regexp ".*?")) ;; Allow searching across whitespace. (use-package vc :ensure nil ;; This is built-in, no need to fetch it. :defer t - :bind - (("C-x v d" . vc-dir) ;; Open VC directory for version control status. - ("C-x v =" . vc-diff) ;; Show differences for the current file. - ("C-x v D" . vc-root-diff) ;; Show differences for the entire repository. - ("C-x v v" . vc-next-action)) ;; Perform the next version control action. :config ;; Better colors for <leader> g b (blame file) (setq vc-annotate-color-map @@ -744,12 +719,7 @@ Temporarily disables notifications during the fetch." (use-package smerge-mode :ensure nil ;; This is built-in, no need to fetch it. - :defer t - :bind (:map smerge-mode-map - ("C-c ^ u" . smerge-keep-upper) ;; Keep the changes from the upper version. - ("C-c ^ l" . smerge-keep-lower) ;; Keep the changes from the lower version. - ("C-c ^ n" . smerge-next) ;; Move to the next conflict. - ("C-c ^ p" . smerge-previous))) ;; Move to the previous conflict. + :defer t) (use-package markdown-mode :ensure t @@ -869,22 +839,6 @@ Temporarily disables notifications during the fetch." '("delta" "--no-gitconfig" "--color-only"))) (setq agent-shell-activity-group-header-label-function #'agent-shell-activity-group-descriptive-label) - (define-key agent-shell-mode-map (kbd "RET") #'my/agent-shell-ret) - (define-key agent-shell-mode-map (kbd "<return>") #'my/agent-shell-ret) - (define-key agent-shell-viewport-view-mode-map (kbd "RET") #'my/agent-shell-ret) - (define-key agent-shell-viewport-view-mode-map (kbd "<return>") #'my/agent-shell-ret) - (with-eval-after-load 'evil - (evil-define-key '(normal motion) agent-shell-mode-map - (kbd "RET") #'my/agent-shell-ret - (kbd "<return>") #'my/agent-shell-ret) - (evil-define-key 'insert agent-shell-mode-map - (kbd "RET") #'agent-shell-submit - (kbd "<return>") #'agent-shell-submit) - (evil-define-key '(normal motion) agent-shell-viewport-view-mode-map - (kbd "RET") #'my/agent-shell-ret - (kbd "<return>") #'my/agent-shell-ret)) - - ;; Work around agent-shell's `window-system' guard so clipboard images ;; work in terminal Emacs (`emacs -nw'). The underlying save routine ;; already shells out to wl-paste/xclip/pngpaste/powershell, none of @@ -1358,11 +1312,7 @@ Skips capture tasks and projects." ;; Change ACTIVE projects back to TODO (projects shouldn't be clocked directly) ((and (member (org-get-todo-state) (list "ACTIVE")) (my/is-project-p)) - "TODO")))) - - :bind (("<f11>" . org-clock-goto) - ("C-c C-x C-i" . org-clock-in) - ("C-c C-x C-o" . org-clock-out))) + "TODO"))))) (with-eval-after-load 'org (setq org-global-properties @@ -1384,10 +1334,7 @@ Skips capture tasks and projects." (cond ((string= complexity "low") (org-set-property "EFFORT" "0:30")) ((string= complexity "medium") (org-set-property "EFFORT" "2:00")) - ((string= complexity "high") (org-set-property "EFFORT" "5:00")))))) - - ;; Keybinding for manual effort setting - (define-key org-mode-map (kbd "C-c e") 'my/org-set-effort-from-complexity)) + ((string= complexity "high") (org-set-property "EFFORT" "5:00"))))))) (defun my/create-project () "Create a new project file with denote and comprehensive project structure." @@ -1915,6 +1862,14 @@ Skips capture tasks and projects." :ensure (:wait t) :after org) +(use-package vui + :ensure (:host github :repo "d12frosted/vui.el" + :ref "v1.4.0" + :files ("*.el")) + :demand t + :custom + (vui-width-mode 'pixel)) + (use-package denote :ensure (:wait t) :hook (dired-mode . denote-dired-mode) @@ -2094,8 +2049,7 @@ Works on the base filename (without extension), e.g. matches \"-agenda\", \":age (kill-buffer))))) (use-package ox-json - :ensure t - ) + :ensure t) (use-package ox-gfm :ensure t) @@ -2119,8 +2073,6 @@ Works on the base filename (without extension), e.g. matches \"-agenda\", \":age (setq tmr-sound-file "/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga" tmr-notification-urgency 'normal tmr-description-list 'tmr-description-history) - (define-key global-map (kbd "C-c t") #'tmr-prefix-map) - ;; ── Timer persistence across Emacs restarts ────────────── (defvar my/tmr-save-file (expand-file-name "tmr-timers.eld" user-emacs-directory) @@ -2284,12 +2236,8 @@ Timers that expired while Emacs was closed fire immediately." "\\`\\*eldoc" "\\`\\*elpaca-log\\*\\'" "\\`\\*Native-compile-Log\\*\\'" - "\\`\\*Async-native-compile-log\\*\\'")) + "\\`\\*Async-native-compile-log\\*\\'"))) ;; (helm-display-function #'helm-display-buffer-in-own-frame) - :bind (:map helm-map - ("C-j" . helm-next-line) - ("C-k" . helm-previous-line))) -(global-set-key (kbd "M-x") 'helm-M-x) (use-package helm-xref :ensure t @@ -2362,15 +2310,7 @@ Timers that expired while Emacs was closed fire immediately." (defun my/project-ctags-setup () (interactive) (my/project-ctags-start (my/project-ctags-root) - my/project-ctags-arguments)) - - (with-eval-after-load 'evil - (evil-define-key '(normal motion) 'global - (kbd "g d") #'citre-jump - (kbd "g D") #'citre-query-jump - (kbd "g p") #'citre-peek - (kbd "g r") #'citre-jump-to-reference - (kbd "g b") #'citre-jump-back))) + my/project-ctags-arguments))) (use-package nov :ensure t @@ -2426,8 +2366,6 @@ Timers that expired while Emacs was closed fire immediately." (goto-char (point-min)) (search-forward (car (last (split-string option "\\." t)))))) -(keymap-set compilation-mode-map "C-c o" #'nix-compile-goto-option) - (use-package c-ts-mode :ensure nil :mode (("\\.c\\'" . c-ts-mode) @@ -2647,13 +2585,7 @@ so the working-tree diff stays visible until the user explicitly stages." (setq evil-vsplit-window-right t) (setq evil-paste-from-register nil) :config - (evil-mode 1) - (evil-set-initial-state 'help-mode 'emacs) - (evil-set-initial-state 'messages-buffer-mode 'normal) - (evil-set-initial-state 'dired-mode 'normal) - (evil-set-initial-state 'ibuffer-mode 'normal) - (evil-set-initial-state 'erc-mode 'normal) - (define-key evil-insert-state-map (kbd "C-w") 'evil-window-map)) + (evil-mode 1)) (modify-syntax-entry ?_ "w") @@ -2665,8 +2597,6 @@ so the working-tree diff stays visible until the user explicitly stages." ;; (eldoc-doc-buffer t)) (eldoc-doc-buffer t)) -(define-key evil-normal-state-map (kbd "K") #'my/eldoc-and-jump) - (use-package smartparens :ensure t :init @@ -2701,9 +2631,6 @@ so the working-tree diff stays visible until the user explicitly stages." :ensure t :after evil :config - (define-key evil-inner-text-objects-map "b" 'evil-textobj-anyblock-inner-block) - (define-key evil-outer-text-objects-map "b" 'evil-textobj-anyblock-a-block) - (setq evil-textobj-anyblock-blocks '(("(" . ")") ("{" . "}") @@ -2730,9 +2657,6 @@ so the working-tree diff stays visible until the user explicitly stages." ("“" . "”")))) (evil-textobj-anyblock--make-textobj beg end type count t))) -(define-key evil-inner-text-objects-map "q" 'my-evil-textobj-anyblock-inner-quote) -(define-key evil-outer-text-objects-map "q" 'my-evil-textobj-anyblock-a-quote) - (use-package evil-mc :ensure t :after evil @@ -2760,10 +2684,7 @@ so the working-tree diff stays visible until the user explicitly stages." (evil-mc-make-cursor-in-visual-selection-end) (evil-append 1)) ;; Fallback: normales Verhalten - (call-interactively 'evil-append))) - - (define-key evil-visual-state-map (kbd "I") 'my/evil-mc-visual-block-insert) - (define-key evil-visual-state-map (kbd "A") 'my/evil-mc-visual-block-append)) + (call-interactively 'evil-append)))) (use-package flash :ensure (:host github :repo "Prgebish/flash") @@ -2787,13 +2708,6 @@ so the working-tree diff stays visible until the user explicitly stages." (require 'flash-evil) (flash-evil-setup t) - ;; Restore ; and , after flash-char overwrites them - ;; ; is used as prefix for prev-navigation (;b, ;d) - ;; , is the local leader - (evil-define-key* '(normal visual motion) 'global - (kbd ";") nil - (kbd ",") nil) - ;; Search integration: labels during C-s, /, ? (require 'flash-isearch) (flash-isearch-mode 1)) @@ -2903,7 +2817,7 @@ so the working-tree diff stays visible until the user explicitly stages." :mode-line-active-height 0.92 :mode-line-inactive-family "SFProText Nerd Font" :mode-line-inactive-height 0.92 - :header-line-family "SFProText Nerd Font" + :header-line-family ,cashmere/font-family :header-line-weight regular :line-number-family ,cashmere/font-family :line-number-height 0.9 @@ -2987,10 +2901,7 @@ so the working-tree diff stays visible until the user explicitly stages." (use-package croc-ui :ensure nil :commands (croc-ui croc-ui-send-files croc-ui-send-directory - croc-ui-send-text croc-ui-receive) - :init - (with-eval-after-load 'general - (my-leader "oc" '(croc-ui :wk "croc")))) + croc-ui-send-text croc-ui-receive)) (use-package textui :ensure (:host github :repo "yibie/textui" :files ("*.el"))) @@ -3003,17 +2914,11 @@ so the working-tree diff stays visible until the user explicitly stages." (use-package yggdrasil-ui :ensure nil :after textui - :commands (yggdrasil-ui) - :init - (with-eval-after-load 'general - (my-leader "ov" '(yggdrasil-ui :wk "yggdrasil")))) + :commands (yggdrasil-ui)) (use-package sync-ui :ensure nil :commands (sync-ui)) - :init - (with-eval-after-load 'general - (my-leader "oy" '(sync-ui :wk "sync"))) (defun my/reload-config () "Re-tangle config.org and reload the generated config.el in place. @@ -3057,8 +2962,6 @@ still require a restart since elpaca queues run at init time." (use-package projectile :ensure (:wait t) :demand t - :bind-keymap - (("C-x p" . projectile-command-map)) :config (projectile-mode +1) (setq projectile-completion-system 'default @@ -3330,12 +3233,6 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (with-eval-after-load 'projectile (setq projectile-switch-project-action #'my/workspace-switch-to-project)) - (define-key persp-mode-map [remap delete-window] - #'my/workspace-close-window-or-workspace) - (with-eval-after-load 'evil - (define-key persp-mode-map [remap evil-window-delete] - #'my/workspace-close-window-or-workspace)) - (add-hook 'persp-filter-save-buffers-functions (lambda (buf) (with-current-buffer buf (derived-mode-p 'erc-mode)))) @@ -3364,47 +3261,6 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (ibuffer nil (format "*ibuffer: %s*" name) (list (cons 'persp t)))))) -(my-leader - "TAB" '(:ignore t :wk "workspace") - "TAB TAB" '(my/workspace-display :wk "display") - "TAB ." '(my/workspace-switch-to :wk "switch to…") - "TAB `" '(my/workspace-other :wk "last") - "TAB n" '(my/workspace-new :wk "new") - "TAB r" '(my/workspace-rename :wk "rename") - "TAB d" '(my/workspace-kill :wk "delete") - "TAB x" '(my/workspace-kill-session :wk "kill session") - "TAB s" '(my/workspace-save-session :wk "save session") - "TAB l" '(my/workspace-load-session :wk "load session") - "TAB [" '(my/workspace-switch-left :wk "prev") - "TAB ]" '(my/workspace-switch-right :wk "next") - "TAB 1" '((lambda () (interactive) (my/workspace-switch-to-index 0)) :wk "1") - "TAB 2" '((lambda () (interactive) (my/workspace-switch-to-index 1)) :wk "2") - "TAB 3" '((lambda () (interactive) (my/workspace-switch-to-index 2)) :wk "3") - "TAB 4" '((lambda () (interactive) (my/workspace-switch-to-index 3)) :wk "4") - "TAB 5" '((lambda () (interactive) (my/workspace-switch-to-index 4)) :wk "5") - "TAB 6" '((lambda () (interactive) (my/workspace-switch-to-index 5)) :wk "6") - "TAB 7" '((lambda () (interactive) (my/workspace-switch-to-index 6)) :wk "7") - "TAB 8" '((lambda () (interactive) (my/workspace-switch-to-index 7)) :wk "8") - "TAB 9" '((lambda () (interactive) (my/workspace-switch-to-index 8)) :wk "9") - "TAB 0" '(my/workspace-switch-to-final :wk "last")) - -(general-def '(normal motion) - "gt" 'my/workspace-switch-right - "gT" 'my/workspace-switch-left) - -(dotimes (i 9) - (general-define-key - :states '(normal visual) - :keymaps 'override - :prefix "," - (number-to-string (1+ i)) - `(lambda () (interactive) (my/workspace-switch-to-index ,i)))) -(general-define-key - :states '(normal visual) - :keymaps 'override - :prefix "," - "0" #'my/workspace-switch-to-final) - (use-package pass :ensure t :defer t @@ -3415,29 +3271,7 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (add-to-list 'display-buffer-alist '("\\*Pass.*\\*" - (display-buffer-full-frame))) - - (with-eval-after-load 'evil - (general-def 'normal pass-mode-map - "q" 'quit-window - "j" 'pass-next-entry - "k" 'pass-prev-entry - "RET" 'pass-view - "d" 'pass-kill - "y" 'pass-copy - "Y" 'pass-copy-field - "e" 'pass-edit - "a" 'pass-insert - "G" 'pass-insert-generated - "o" 'pass-otp-options - "r" 'pass-rename - "/" 'isearch-forward) - - (general-def 'normal pass-view-mode-map - "q" 'quit-window - "t" 'pass-view-toggle-password - "y" 'pass-copy - "Y" 'pass-copy-field))) + (display-buffer-full-frame)))) (use-package auth-source :ensure nil ;; This is built-in, no need to fetch it. @@ -3467,300 +3301,448 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (defun my/format-buffer () "Format the current buffer with its mode-specific formatter." (interactive) - (cond - ((derived-mode-p 'org-mode) - (org-fmt-buffer)) - (t - (user-error "No formatter configured for %s" major-mode)))) + (if (derived-mode-p 'org-mode) + (org-fmt-buffer) + (user-error "No formatter configured for %s" major-mode))) (defun my/find-file-or-switch-project () - "Find a file in the current project; outside one, pick a project first. -Uses the native Helm sources from `helm-projectile'. Picking a project -switches to its workspace and shows its files via -`projectile-switch-project-action', so this also works from the master -workspace (e.g. *scratch*)." + "Find a file in the current project, or select a project first." (interactive) (if (projectile-project-p) (helm-projectile-find-file) (helm-projectile-switch-project))) -(my-leader - "SPC" '(my/find-file-or-switch-project :wk "find file/switch project") - "sp" '(helm-projectile :wk "search project") - "ss" '(helm-occur :wk "search line") - "sg" '(my/helm-rg-in :wk "rg in dir") - "sf" '(my/helm-find-in :wk "find file in dir") - "/" '(helm-projectile-rg :wk "search project") - "." '(helm-find-files :wk "find file") - "," '(helm-mini :wk "switch buffer") - ":" (lambda () (interactive) (execute-extended-command nil)) - "u" '(universal-argument :wk "universal argument") +(defun my/xref-find-definitions-other-window () + "Find the definition at point in another window." + (interactive) + (let ((current-prefix-arg 4)) + (call-interactively #'xref-find-definitions))) + +(defun my/comment-line () + "Toggle the comment on the current line." + (interactive) + (comment-or-uncomment-region + (line-beginning-position) + (line-end-position))) + +(defun my/comment-region (beg end) + "Toggle comments in the active region from BEG to END." + (interactive "r") + (comment-or-uncomment-region beg end)) + +(defun my/workspace-switch-by-key () + "Switch to workspace 1–9 using the invoking digit." + (interactive) + (my/workspace-switch-to-index (- last-command-event ?1))) + +(defvar my/leader-search-map nil) +(defvar my/leader-denote-map nil) +(defvar my/leader-denote-merge-map nil) +(defvar my/leader-denote-timer-map nil) +(defvar my/leader-file-map nil) +(defvar my/leader-buffer-map nil) +(defvar my/leader-project-map nil) +(defvar my/leader-git-map nil) +(defvar my/leader-agent-map nil) +(defvar my/leader-open-map nil) +(defvar my/leader-help-map nil) +(defvar my/leader-window-map nil) +(defvar my/leader-code-map nil) +(defvar my/leader-quit-map nil) +(defvar my/leader-web-map nil) +(defvar my/leader-workspace-map nil) + +;; Recreate the maps on reload so removed bindings cannot linger. +(setq my/leader-map (make-sparse-keymap) + my/local-leader-map (make-sparse-keymap) + my/leader-search-map (make-sparse-keymap) + my/leader-denote-map (make-sparse-keymap) + my/leader-denote-merge-map (make-sparse-keymap) + my/leader-denote-timer-map (make-sparse-keymap) + my/leader-file-map (make-sparse-keymap) + my/leader-buffer-map (make-sparse-keymap) + my/leader-project-map (make-sparse-keymap) + my/leader-git-map (make-sparse-keymap) + my/leader-agent-map (make-sparse-keymap) + my/leader-open-map (make-sparse-keymap) + my/leader-help-map (make-sparse-keymap) + my/leader-window-map (make-sparse-keymap) + my/leader-code-map (make-sparse-keymap) + my/leader-quit-map (make-sparse-keymap) + my/leader-web-map (make-sparse-keymap) + my/leader-workspace-map (make-sparse-keymap)) + +(keymap-set my/leader-map "SPC" #'my/find-file-or-switch-project) +(keymap-set my/leader-map "/" #'helm-projectile-rg) +(keymap-set my/leader-map "." #'helm-find-files) +(keymap-set my/leader-map "," #'helm-mini) +(keymap-set my/leader-map ":" #'execute-extended-command) +(keymap-set my/leader-map "u" #'universal-argument) +(keymap-set my/leader-map "x" #'org-capture) +(keymap-set my/leader-map ";" #'embark-act) +(keymap-set my/leader-map "P" #'helm-show-kill-ring) + +(keymap-set my/leader-map "s" my/leader-search-map) +(keymap-set my/leader-search-map "p" #'helm-projectile) +(keymap-set my/leader-search-map "s" #'helm-occur) +(keymap-set my/leader-search-map "g" #'my/helm-rg-in) +(keymap-set my/leader-search-map "f" #'my/helm-find-in) + +(keymap-set my/leader-map "d" my/leader-denote-map) +(keymap-set my/leader-denote-map "a" #'my/denote-toggle-agenda-keyword) +(keymap-set my/leader-denote-map "j" #'denote-journal-new-or-existing-entry) +(keymap-set my/leader-denote-map "d" #'denote-menu) +(keymap-set my/leader-denote-map "g" #'my/denote-rg) +(keymap-set my/leader-denote-map "l" #'denote-link-or-create) +(keymap-set my/leader-denote-map "n" #'denote) +(keymap-set my/leader-denote-map "r" #'denote-rename-file) +(keymap-set my/leader-denote-map "s" #'denote-solo-switch) +(keymap-set my/leader-denote-map "m" my/leader-denote-merge-map) +(keymap-set my/leader-denote-merge-map "r" #'denote-merge-region) +(keymap-set my/leader-denote-merge-map "f" #'denote-merge-file) +(keymap-set my/leader-denote-map "t" my/leader-denote-timer-map) +(keymap-set my/leader-denote-timer-map "l" #'tmr-list-timers) +(keymap-set my/leader-denote-timer-map "t" #'tmr) - "d" '(:ignore t :wk "denote") - "da" '(my/denote-toggle-agenda-keyword :wk "toggle agenda tag") - "dj" '(denote-journal-new-or-existing-entry :wk "journal") - "dd" '(denote-menu t :wk "List all notes") - "dm" '(:ignore t :wk "Merge Notes") - "dmr" '(denote-merge-region :wk "Merge Region") - "dmf" '(denote-merge-file :wk "Merge File") - "dg" '(my/denote-rg :wk "grep denotes") - "dl" '(denote-link-or-create t :wk "Link Note") - "dn" '(denote t :wk "Create a new note") - "dr" '(denote-rename-file t :wk "Rename Note") - "ds" '(denote-solo-switch :wk "switch silo") - "dtl" '(tmr-list-timers :wk "list timer") - "dtt" '(tmr :wk "set timer") +(keymap-set my/leader-map "f" my/leader-file-map) +(keymap-set my/leader-file-map "d" #'dired-jump) +(keymap-set my/leader-file-map "D" #'dired-jump) +(keymap-set my/leader-file-map "r" #'helm-recentf) +(keymap-set my/leader-file-map "f" #'helm-find-files) +(keymap-set my/leader-file-map "s" #'save-buffer) - "f" '(:ignore t :wk "files") - "fd" '(dired-jump :wk "dired") - "fD" '(dired-jump :wk "dired jump") - "fr" '(helm-recentf :wk "recent files") - "ff" '(helm-find-files :wk "find file") - "fs" '(save-buffer :wk "save file") - "b" '(:ignore t :wk "buffer/bookmarks") - "bb" '(helm-filtered-bookmarks :wk "display current bookmarks") - "bi" '(my/ibuffer-workspace :wk "ibuffer (workspace)") - "bp" '(projectile-ibuffer :wk "ibuffer project") - "bd" '(bookmark-delete :wk "delete bookmark") - "bk" '(kill-current-buffer :wk "kill buffer") - "bs" '(bookmark-set :wk "save bookmark") - "br" '(rename-buffer :wk "rename buffer") +(keymap-set my/leader-map "b" my/leader-buffer-map) +(keymap-set my/leader-buffer-map "b" #'helm-filtered-bookmarks) +(keymap-set my/leader-buffer-map "i" #'my/ibuffer-workspace) +(keymap-set my/leader-buffer-map "p" #'projectile-ibuffer) +(keymap-set my/leader-buffer-map "d" #'bookmark-delete) +(keymap-set my/leader-buffer-map "k" #'kill-current-buffer) +(keymap-set my/leader-buffer-map "s" #'bookmark-set) +(keymap-set my/leader-buffer-map "r" #'rename-buffer) - "p" '(:ignore t :wk "project") - "pp" '(helm-projectile-switch-project :wk "switch project workspace") - "pf" '(helm-projectile-find-file :wk "find file") - "ps" '(helm-projectile-rg :wk "search") - "pb" '(helm-projectile-switch-to-buffer :wk "buffers") - "pk" '(projectile-kill-buffers :wk "kill buffers") - "pd" '(projectile-remove-known-project :wk "delete project") - "pr" '(my/project-replace :wk "project replace (wgrep)") - "pa" '(projectile-add-known-project :wk "add project") - "pi" '(projectile-invalidate-cache :wk "invalidate cache") - "pt" '(projectile-run-task :wk "tasks") - "pc" '(my/project-ctags-setup :wk "generate/update ctags") +(keymap-set my/leader-map "p" my/leader-project-map) +(keymap-set my/leader-project-map "p" #'helm-projectile-switch-project) +(keymap-set my/leader-project-map "f" #'helm-projectile-find-file) +(keymap-set my/leader-project-map "s" #'helm-projectile-rg) +(keymap-set my/leader-project-map "b" #'helm-projectile-switch-to-buffer) +(keymap-set my/leader-project-map "k" #'projectile-kill-buffers) +(keymap-set my/leader-project-map "d" #'projectile-remove-known-project) +(keymap-set my/leader-project-map "r" #'my/project-replace) +(keymap-set my/leader-project-map "a" #'projectile-add-known-project) +(keymap-set my/leader-project-map "i" #'projectile-invalidate-cache) +(keymap-set my/leader-project-map "t" #'projectile-run-task) +(keymap-set my/leader-project-map "c" #'my/project-ctags-setup) - "g" '(:ignore t :wk "git") - "gc" '(magit-clone :wk "clone") - "gg" '(magit-status :wk "status") - "gf" '(fossil-ui-status :wk "fossil status") - "gl" '(magit-log-current :wk "log") - "gi" '(magit-init :wk "init") - "gd" '(xref-find-definitions :wk "go to definition") - "gD" '((lambda () (interactive) - (let ((current-prefix-arg 4)) - (call-interactively #'xref-find-definitions))) - :wk "definition other window") - "gI" '(citre-query-jump :wk "find definition") - "gt" '(citre-peek :wk "peek definition") - "gr" '(xref-find-references :wk "find references") - "gs" '(magit-file-stage :wk "stage file") - "gu" '(my/magit-uncommit :wk "uncommit (keep & unstage)") - "gb" '(vc-annotate :wk "blame") - "gT" '(my/code-todos-harvest :wk "harvest code TODOs") - "aa" '(agent-shell :wk "start") - "at" '(agent-shell-toggle :wk "toggle") - "am" '(agent-shell-help-menu :wk "open session") - "ar" '(agent-shell-send-region :wk "send region") - "ac" '(agent-shell-send-clipboard-image :wk "send screenshot") - "o" '(:ignore t :wk "open") - "oa" '(my/app-launcher :wk "app launcher") - "os" '(my/snip-upload :wk "snip buffer/region") - "oS" '(my/snip-upload-file :wk "snip file") - "op" '(pass :wk "pass") - "ot" '(ghostel :wk "ghostel") - "oz" '(zfs :wk "zfs") +(keymap-set my/leader-map "g" my/leader-git-map) +(keymap-set my/leader-git-map "c" #'magit-clone) +(keymap-set my/leader-git-map "g" #'magit-status) +(keymap-set my/leader-git-map "f" #'fossil-ui-status) +(keymap-set my/leader-git-map "l" #'magit-log-current) +(keymap-set my/leader-git-map "i" #'magit-init) +(keymap-set my/leader-git-map "d" #'xref-find-definitions) +(keymap-set my/leader-git-map "D" #'my/xref-find-definitions-other-window) +(keymap-set my/leader-git-map "I" #'citre-query-jump) +(keymap-set my/leader-git-map "t" #'citre-peek) +(keymap-set my/leader-git-map "r" #'xref-find-references) +(keymap-set my/leader-git-map "s" #'magit-file-stage) +(keymap-set my/leader-git-map "u" #'my/magit-uncommit) +(keymap-set my/leader-git-map "b" #'vc-annotate) +(keymap-set my/leader-git-map "T" #'my/code-todos-harvest) - "h" '(:ignore t :wk "help") - "hm" '(describe-mode :wk "mode") - "hf" '(describe-function :wk "function") - "hv" '(describe-variable :wk "variable") - "hk" '(describe-key :wk "key") - ;; "ht" '(load-theme :wk "load theme") +(keymap-set my/leader-map "a" my/leader-agent-map) +(keymap-set my/leader-agent-map "a" #'agent-shell) +(keymap-set my/leader-agent-map "t" #'agent-shell-toggle) +(keymap-set my/leader-agent-map "m" #'agent-shell-help-menu) +(keymap-set my/leader-agent-map "r" #'agent-shell-send-region) +(keymap-set my/leader-agent-map "c" #'agent-shell-send-clipboard-image) - "w w" '(evil-window-next :wk "Close window") - "w c" '(evil-window-delete :wk "Close window") - "w o" '(delete-other-windows :wk "Maximize window") - "w n" '(evil-window-new :wk "New window") - "w s" '(evil-window-split :wk "Horizontal split window") - "w v" '(evil-window-vsplit :wk "Vertical split window") - "w h" '(evil-window-left :wk "Window left") - "w j" '(evil-window-down :wk "Window down") - "w k" '(evil-window-up :wk "Window up") - "w l" '(evil-window-right :wk "Window right") - "w w" '(evil-window-next :wk "Goto next window") - "w H" '(buf-move-left :wk "Buffer move left") - "w J" '(buf-move-down :wk "Buffer move down") - "w K" '(buf-move-up :wk "Buffer move up") - "w L" '(buf-move-right :wk "Buffer move right") +(keymap-set my/leader-map "o" my/leader-open-map) +(keymap-set my/leader-open-map "a" #'my/app-launcher) +(keymap-set my/leader-open-map "s" #'my/snip-upload) +(keymap-set my/leader-open-map "S" #'my/snip-upload-file) +(keymap-set my/leader-open-map "p" #'pass) +(keymap-set my/leader-open-map "t" #'ghostel) +(keymap-set my/leader-open-map "z" #'zfs) +(keymap-set my/leader-open-map "c" #'croc-ui) +(keymap-set my/leader-open-map "v" #'yggdrasil-ui) +(keymap-set my/leader-open-map "y" #'sync-ui) +(keymap-set my/leader-open-map "r" #'rsync-ui) - "c" '(:ignore t :wk "code") - "cc" '(my/compile-or-recompile :wk "compile") - "cC" '(ghostel-compile :wk "recompile") - "ca" '(citre-query-jump :wk "find definition") - "cr" '(query-replace :wk "replace") - "cf" '(my/format-buffer :wk "format buffer") - "cs" '(yas-insert-snippet :wk "snippets") - "cl" '(flycheck-list-errors :wk "list errors") +(keymap-set my/leader-map "h" my/leader-help-map) +(keymap-set my/leader-help-map "m" #'describe-mode) +(keymap-set my/leader-help-map "f" #'describe-function) +(keymap-set my/leader-help-map "v" #'describe-variable) +(keymap-set my/leader-help-map "k" #'describe-key) +(keymap-set my/leader-help-map "r" #'my/reload-config) - "q" '(:ignore t :wk "quit") - "qq" '(save-buffers-kill-terminal :wk "quit emacs") - "qr" '(restart-emacs :wk "restart") - "hr" '(my/reload-config :wk "reload config") +(keymap-set my/leader-map "w" my/leader-window-map) +(keymap-set my/leader-window-map "w" #'evil-window-next) +(keymap-set my/leader-window-map "c" #'evil-window-delete) +(keymap-set my/leader-window-map "o" #'delete-other-windows) +(keymap-set my/leader-window-map "n" #'evil-window-new) +(keymap-set my/leader-window-map "s" #'evil-window-split) +(keymap-set my/leader-window-map "v" #'evil-window-vsplit) +(keymap-set my/leader-window-map "h" #'evil-window-left) +(keymap-set my/leader-window-map "j" #'evil-window-down) +(keymap-set my/leader-window-map "k" #'evil-window-up) +(keymap-set my/leader-window-map "l" #'evil-window-right) +(keymap-set my/leader-window-map "H" #'buf-move-left) +(keymap-set my/leader-window-map "J" #'buf-move-down) +(keymap-set my/leader-window-map "K" #'buf-move-up) +(keymap-set my/leader-window-map "L" #'buf-move-right) - "x" '(org-capture :wk "capture") +(keymap-set my/leader-map "c" my/leader-code-map) +(keymap-set my/leader-code-map "c" #'my/compile-or-recompile) +(keymap-set my/leader-code-map "C" #'ghostel-compile) +(keymap-set my/leader-code-map "a" #'citre-query-jump) +(keymap-set my/leader-code-map "r" #'query-replace) +(keymap-set my/leader-code-map "f" #'my/format-buffer) +(keymap-set my/leader-code-map "s" #'yas-insert-snippet) +(keymap-set my/leader-code-map "l" #'flycheck-list-errors) - ";" '(embark-act :wk "embark") - "P" '(helm-show-kill-ring :wk "paste history") +(keymap-set my/leader-map "q" my/leader-quit-map) +(keymap-set my/leader-quit-map "q" #'save-buffers-kill-terminal) +(keymap-set my/leader-quit-map "r" #'restart-emacs) - ;; "t" '(:ignore t :wk "treesitter") - ;; "ts" '(flash-treesitter :wk "flash treesitter") +(keymap-set my/leader-map "e" my/leader-web-map) +(keymap-set my/leader-web-map "e" #'eww) +(keymap-set my/leader-web-map "n" #'my/eww-new-buffer) +(keymap-set my/leader-web-map "b" #'eww-list-bookmarks) +(keymap-set my/leader-web-map "h" #'eww-list-histories) +(keymap-set my/leader-web-map "f" #'elfeed) +(keymap-set my/leader-web-map "L" #'link-hint-open-link) +(keymap-set my/leader-web-map "C" #'link-hint-copy-link) +(keymap-set my/leader-web-map "x" #'xwidget-webkit-browse-url) +(keymap-set my/leader-map "TAB" my/leader-workspace-map) +(keymap-set my/leader-workspace-map "TAB" #'my/workspace-display) +(keymap-set my/leader-workspace-map "." #'my/workspace-switch-to) +(keymap-set my/leader-workspace-map "`" #'my/workspace-other) +(keymap-set my/leader-workspace-map "n" #'my/workspace-new) +(keymap-set my/leader-workspace-map "r" #'my/workspace-rename) +(keymap-set my/leader-workspace-map "d" #'my/workspace-kill) +(keymap-set my/leader-workspace-map "x" #'my/workspace-kill-session) +(keymap-set my/leader-workspace-map "s" #'my/workspace-save-session) +(keymap-set my/leader-workspace-map "l" #'my/workspace-load-session) +(keymap-set my/leader-workspace-map "[" #'my/workspace-switch-left) +(keymap-set my/leader-workspace-map "]" #'my/workspace-switch-right) +(dotimes (index 9) + (keymap-set my/leader-workspace-map (number-to-string (1+ index)) + #'my/workspace-switch-by-key)) +(keymap-set my/leader-workspace-map "0" #'my/workspace-switch-to-final) - "e" '(:ignore t :wk "eww/web") - "e e" '(eww :wk "eww browse / search") - "e n" '(my/eww-new-buffer :wk "new eww buffer") - "e b" '(eww-list-bookmarks :wk "bookmarks") - "e h" '(eww-list-histories :wk "history") - "e f" '(elfeed :wk "elfeed (rss)") - "e L" '(link-hint-open-link :wk "hint open link") - "e C" '(link-hint-copy-link :wk "hint copy link") - "e x" '(xwidget-webkit-browse-url :wk "webkit browser") - ) +(keymap-set my/local-leader-map "a" #'org-agenda) +(keymap-set my/local-leader-map "c" #'my/centered-cursor) +(keymap-set my/local-leader-map "f" #'dirvish) +(keymap-set my/local-leader-map "m" #'mu4e) +(keymap-set my/local-leader-map "i" #'clatter) +(keymap-set my/local-leader-map "r" #'async-shell-command) +(keymap-set my/local-leader-map "t" #'ghostel-project) +(keymap-set my/local-leader-map "T" #'ghostel-list-buffers) +(keymap-set my/local-leader-map "z" #'golden-ratio-mode) +(keymap-set my/local-leader-map "o" #'my/global-olivetti-mode) +(keymap-set my/local-leader-map "s" #'my-org-sidecar-left) +(dotimes (index 9) + (keymap-set my/local-leader-map (number-to-string (1+ index)) + #'my/workspace-switch-by-key)) +(keymap-set my/local-leader-map "0" #'my/workspace-switch-to-final) -(defun my/flash-enabled-p () - (and (not (derived-mode-p 'magit-mode 'dired-mode 'ibuffer-mode)) - (not (eq major-mode 'dirvish-mode)))) +(with-eval-after-load 'which-key + (which-key-add-keymap-based-replacements + my/leader-map + "s" "search" "d" "denote" "f" "files" "b" "buffers/bookmarks" + "p" "project" "g" "git" "a" "agent" "o" "open" "h" "help" + "w" "windows" "c" "code" "q" "quit" "e" "web" "TAB" "workspace") + (which-key-add-keymap-based-replacements + my/leader-denote-map "m" "merge" "t" "timer")) + +(keymap-global-set "C-s" #'isearch-forward) +(keymap-global-set "C-r" #'isearch-backward) +(keymap-global-set "C-x v d" #'vc-dir) +(keymap-global-set "C-x v =" #'vc-diff) +(keymap-global-set "C-x v D" #'vc-root-diff) +(keymap-global-set "C-x v v" #'vc-next-action) +(keymap-global-set "C-x p" projectile-command-map) +(keymap-global-set "C-c t" tmr-prefix-map) +(keymap-global-set "<f11>" #'org-clock-goto) +(keymap-global-set "M-x" #'helm-M-x) +(keymap-global-set "C-=" #'text-scale-increase) +(keymap-global-set "C--" #'text-scale-decrease) (defun my/s-key-dispatch () + "Run Flash with stable viewport margins." (interactive) - (if (derived-mode-p 'magit-mode) - (call-interactively 'magit-stage) - (when (my/flash-enabled-p) - (let ((scroll-margin 0) - (maximum-scroll-margin 0)) - (call-interactively 'flash-evil-jump))))) + (let ((scroll-margin 0) + (maximum-scroll-margin 0)) + (call-interactively #'flash-evil-jump))) + +;; The wrapper must retain Flash's inclusive operator semantics. Without +;; these properties `d s' becomes exclusive and stops before the target. +(evil-set-command-properties #'my/s-key-dispatch + :type 'inclusive + :keep-visual t + :repeat 'motion) -(general-def '(normal visual operator) 'override - :predicate '(not (derived-mode-p 'mu4e-main-mode 'mu4e-headers-mode - 'mu4e-view-mode 'mu4e-compose-mode - 'croc-ui-mode 'sync-ui-mode - 'fossil-ui-mode)) - "s" 'my/s-key-dispatch) +;; Flash's enhanced character motions claim `;' and `,'. Keep `;' free for +;; mode-local prefixes and reserve `,' for the local leader below. +(evil-define-key 'motion 'global + (kbd ";") nil + (kbd ",") nil) +(evil-define-key '(normal visual) 'global (kbd "SPC") my/leader-map) +(evil-define-key 'insert 'global (kbd "M-SPC") my/leader-map) +(evil-define-key '(normal visual) 'global (kbd ",") my/local-leader-map) +(evil-define-key '(normal visual operator) 'global (kbd "s") #'my/s-key-dispatch) +(evil-define-key 'insert 'global (kbd "C-w") evil-window-map) +(evil-define-key 'normal 'global + (kbd "K") #'my/eldoc-and-jump + (kbd "] d") #'flycheck-next-error + (kbd "[ d") #'flycheck-previous-error + (kbd "] c") #'diff-hl-next-hunk + (kbd "[ c") #'diff-hl-previous-hunk + (kbd "] b") #'switch-to-next-buffer + (kbd "[ b") #'switch-to-prev-buffer + (kbd "] t") #'tab-next + (kbd "[ t") #'tab-previous + (kbd "g c c") #'my/comment-line + (kbd "g d") #'citre-jump + (kbd "g D") #'citre-query-jump + (kbd "g p") #'citre-peek + (kbd "g r") #'citre-jump-to-reference + (kbd "g b") #'citre-jump-back) -(general-def 'normal 'override - "K" 'my/eldoc-and-jump - "]d" 'flycheck-next-error - "[d" 'flycheck-previous-error - "]c" 'diff-hl-next-hunk - "[c" 'diff-hl-previous-hunk - "]b" 'switch-to-next-buffer - "[b" 'switch-to-prev-buffer - "]t" 'tab-next - "[t" 'tab-previous - ;; "P" 'helm-show-kill-ring - ;; "?" 'casual-avy-tmenu - "gcc" (lambda () - (interactive) - (unless (use-region-p) - (comment-or-uncomment-region - (line-beginning-position) - (line-end-position))))) +(evil-define-key 'visual 'global (kbd "g c") #'my/comment-region) +(evil-define-key 'visual 'global + (kbd "I") #'my/evil-mc-visual-block-insert + (kbd "A") #'my/evil-mc-visual-block-append) -(general-def 'visual 'override - "gc" (lambda () - (interactive) - (when (use-region-p) - (comment-or-uncomment-region - (region-beginning) - (region-end))))) +(define-key evil-inner-text-objects-map "b" #'evil-textobj-anyblock-inner-block) +(define-key evil-outer-text-objects-map "b" #'evil-textobj-anyblock-a-block) +(define-key evil-inner-text-objects-map "q" #'my-evil-textobj-anyblock-inner-quote) +(define-key evil-outer-text-objects-map "q" #'my-evil-textobj-anyblock-a-quote) -(my-leader - :keymaps 'org-mode-map - "m" '(:ignore :wk "org") +(with-eval-after-load 'agent-shell + (keymap-set agent-shell-mode-map "RET" #'my/agent-shell-ret) + (keymap-set agent-shell-mode-map "<return>" #'my/agent-shell-ret) + (evil-define-key '(normal motion) agent-shell-mode-map + (kbd "RET") #'my/agent-shell-ret + (kbd "<return>") #'my/agent-shell-ret) + (evil-define-key 'insert agent-shell-mode-map + (kbd "RET") #'agent-shell-submit + (kbd "<return>") #'agent-shell-submit)) - "mt" '(org-todo :wk "TODO") - "ma" '(org-add-note :wk "add note") - "mC" '(org-capture :wk "capture") - "mf" '(org-fmt-buffer :wk "format buffer") +(with-eval-after-load 'agent-shell + (keymap-set agent-shell-viewport-view-mode-map "RET" #'my/agent-shell-ret) + (keymap-set agent-shell-viewport-view-mode-map "<return>" #'my/agent-shell-ret) + (evil-define-key '(normal motion) agent-shell-viewport-view-mode-map + (kbd "RET") #'my/agent-shell-ret + (kbd "<return>") #'my/agent-shell-ret)) - "mc" '(:wk "set" :ignore) - "mk" '(kitty-graphics-org-heading-sizes :wk "kgfx headlines") - "mcd" '(org-deadline :wk "deadline") - "mcs" '(org-schedule :wk "schedule") - "mce" '(org-set-effort :wk "effort") - "mcr" '(org-clock-report :wk "clock report") - "m," '(org-priority :wk "priority") - "mI" '(org-clock-in :wk "clock in") - "mO" '(org-clock-out :wk "clock out") +(defun my/async-shell-command-bindings () + "Install bindings local to the displayed async shell buffer." + (evil-local-set-key 'normal (kbd "q") #'quit-window) + (evil-local-set-key 'motion (kbd "q") #'quit-window)) + +(with-eval-after-load 'compile + (keymap-set compilation-mode-map "C-c o" #'nix-compile-goto-option)) - "l" '(:ignore :wk "link") - "lc" '(org-cliplink :wk "cliplink") - "li" '(org-download-clipboard :wk "image") - "ll" '(org-insert-link :wk "link various things") +(defun my/dired-view-file-externally () + "Open the current Dired file through mailcap." + (interactive) + (mailcap-view-file (dired-get-filename))) - "n" '(org-toggle-narrow-to-subtree :wk "narrow")) +(with-eval-after-load 'dired + (evil-set-initial-state 'dired-mode 'normal) + (keymap-set dired-mode-map "E" #'my/dired-view-file-externally) + (keymap-set dired-mode-map "C-c r" #'rsync-ui-dired) + (evil-define-key 'normal dired-mode-map + (kbd "h") #'dired-up-directory + (kbd "l") #'dired-find-file)) -(evil-define-key 'normal org-mode-map (kbd "RET") 'org-open-at-point) +(defvar my/dirvish-yank-map (make-sparse-keymap)) +(defvar my/dirvish-symlink-map (make-sparse-keymap)) (defun dirvish-next-file (arg) - "Move down ARG lines, landing on the filename column. -Uses raw line motion so hidden detail lines (permissions, owner, -date) and the dired header are never skipped." + "Move down ARG raw lines and land on the filename column." (interactive "^p") (forward-line arg) (dired-move-to-filename)) (defun dirvish-prev-file (arg) - "Move up ARG lines, landing on the filename column." + "Move up ARG raw lines and land on the filename column." (interactive "^p") (forward-line (- arg)) (dired-move-to-filename)) -(general-def 'normal dired-mode-map - "h" 'dired-up-directory - "l" 'dired-find-file) - -(general-def 'normal dirvish-mode-map - "?" 'dirvish-dispatch - "q" 'dirvish-quit - "b" 'dirvish-quick-access - "f" 'dirvish-file-info-menu - "p" 'dirvish-yank - "S" 'dirvish-quicksort - "F" 'dirvish-layout-toggle - "z" 'zoxide-travel - "j" 'dirvish-next-file - "k" 'dirvish-prev-file - "gh" 'dirvish-subtree-up - "gl" 'dirvish-subtree-toggle - "h" 'dired-up-directory - "l" 'dired-find-file - "TAB" 'dirvish-subtree-toggle - "[h" 'dirvish-history-go-backward - "]h" 'dirvish-history-go-forward) - -(general-def '(normal visual) dirvish-mode-map - :prefix "y" - "l" 'dirvish-copy-file-true-path - "n" 'dirvish-copy-file-name - "p" 'dirvish-copy-file-path - "y" 'dired-do-copy) - -(general-def 'normal dirvish-mode-map - :prefix "s" - "s" 'dirvish-symlink - "S" 'dirvish-relative-symlink - "h" 'dirvish-hardlink) - (defun my/dirvish-copy-to-clipboard (&rest _) (my/send-to-clipboard (car kill-ring))) +(setq my/dirvish-yank-map (make-sparse-keymap) + my/dirvish-symlink-map (make-sparse-keymap)) +(keymap-set my/dirvish-yank-map "l" #'dirvish-copy-file-true-path) +(keymap-set my/dirvish-yank-map "n" #'dirvish-copy-file-name) +(keymap-set my/dirvish-yank-map "p" #'dirvish-copy-file-path) +(keymap-set my/dirvish-yank-map "y" #'dired-do-copy) +(keymap-set my/dirvish-symlink-map "s" #'dirvish-symlink) +(keymap-set my/dirvish-symlink-map "S" #'dirvish-relative-symlink) +(keymap-set my/dirvish-symlink-map "h" #'dirvish-hardlink) + +(with-eval-after-load 'dirvish + (evil-define-key 'normal dirvish-mode-map + (kbd "?") #'dirvish-dispatch + (kbd "q") #'dirvish-quit + (kbd "b") #'dirvish-quick-access + (kbd "f") #'dirvish-file-info-menu + (kbd "p") #'dirvish-yank + (kbd "S") #'dirvish-quicksort + (kbd "F") #'dirvish-layout-toggle + (kbd "z") #'zoxide-travel + (kbd "j") #'dirvish-next-file + (kbd "k") #'dirvish-prev-file + (kbd "g h") #'dirvish-subtree-up + (kbd "g l") #'dirvish-subtree-toggle + (kbd "h") #'dired-up-directory + (kbd "l") #'dired-find-file + (kbd "TAB") #'dirvish-subtree-toggle + (kbd "[ h") #'dirvish-history-go-backward + (kbd "] h") #'dirvish-history-go-forward + (kbd "s") my/dirvish-symlink-map) + (evil-define-key '(normal visual) dirvish-mode-map + (kbd "y") my/dirvish-yank-map)) + (dolist (fn '(dirvish-copy-file-path dirvish-copy-file-name dirvish-copy-file-true-path)) (advice-add fn :after #'my/dirvish-copy-to-clipboard)) +(with-eval-after-load 'elfeed + (evil-define-key '(normal visual) elfeed-search-mode-map + (kbd "f") #'link-hint-open-link + (kbd "F") #'link-hint-copy-link)) + +(with-eval-after-load 'elfeed + (evil-define-key '(normal visual) elfeed-show-mode-map + (kbd "f") #'link-hint-open-link + (kbd "F") #'link-hint-copy-link)) + +(with-eval-after-load 'erc + (evil-set-initial-state 'erc-mode 'normal) + (evil-define-key 'normal erc-mode-map + (kbd "q") #'quit-window + (kbd "Q") #'my/erc-quit-all + (kbd "g b") #'my/erc-switch-channel + (kbd "g n") #'erc-track-switch-buffer + (kbd "g H") #'my/erc-fetch-history + (kbd "g o") #'erc-channel-names + (kbd "g j") #'erc-join-channel + (kbd "g l") #'my/erc-list-channels + (kbd "g r") #'my/erc-reconnect + (kbd "RET") #'erc-send-current-line) + (evil-define-key '(normal insert) erc-mode-map + (kbd "M-n") #'erc-track-switch-buffer + (kbd "C-k") #'erc-previous-command + (kbd "C-j") #'erc-next-command)) + (defun my/eshell-clear () (interactive) (eshell/clear-scrollback)) @@ -3769,33 +3751,138 @@ date) and the dired header are never skipped." (interactive) (eshell-atuin-history)) +(defun my/setup-eshell-bindings () + "Apply native and Evil bindings to `eshell-mode-map'." + (keymap-set eshell-mode-map "C-l" #'my/eshell-clear) + (keymap-set eshell-mode-map "C-r" #'my/atuin-history) + (evil-define-key '(normal insert) eshell-mode-map + (kbd "C-r") #'my/atuin-history + (kbd "C-l") #'my/eshell-clear)) + +(defun my/eshell-bindings-after-evil-collection (mode _maps &rest _) + "Reapply Eshell bindings after Evil Collection configures MODE." + (when (eq mode 'eshell) + (my/setup-eshell-bindings))) + (with-eval-after-load 'eshell - (add-hook 'eshell-mode-hook - (lambda () - (local-set-key (kbd "C-l") 'my/eshell-clear) - (local-set-key (kbd "C-r") 'my/atuin-history)))) + (my/setup-eshell-bindings)) +(add-hook 'evil-collection-setup-hook + #'my/eshell-bindings-after-evil-collection) -(with-eval-after-load 'evil - (with-eval-after-load 'eshell - (evil-define-key '(normal insert) eshell-mode-map - (kbd "C-r") 'my/atuin-history - (kbd "C-l") 'my/eshell-clear))) +(defvar my/eww-local-leader-map (make-sparse-keymap)) +(setq my/eww-local-leader-map (make-sparse-keymap)) +(set-keymap-parent my/eww-local-leader-map my/local-leader-map) +(keymap-set my/eww-local-leader-map "B" #'eww-add-bookmark) +(keymap-set my/eww-local-leader-map "r" #'eww-readable) +(keymap-set my/eww-local-leader-map "i" #'my/eww-toggle-images) +(keymap-set my/eww-local-leader-map "y" #'my/eww-copy-as-org) +(keymap-set my/eww-local-leader-map "d" #'eww-download) -(my-local-leader - "a" '(org-agenda :wk "org agenda") - "c" '(my/centered-cursor :wk "center cursor") - "f" '(dirvish :wk "file manager") - "m" '(mu4e :wk "mu4e") - "i" '(clatter :wk "irc") - "r" '(async-shell-command :wk "run async") - "t" '(ghostel-project :wk "terminal (project)") - "T" '(ghostel-list-buffers :wk "terminal (switch)") - "z" '(golden-ratio-mode :wk "zoom/golden ratio") - "o" '(my/global-olivetti-mode :wk "center buffer") - "s" '(my-org-sidecar-left :wk "org sidecar")) +(with-eval-after-load 'eww + (evil-define-key '(normal visual) eww-mode-map + (kbd ",") my/eww-local-leader-map + (kbd "f") #'link-hint-open-link + (kbd "F") #'link-hint-copy-link)) -(global-set-key (kbd "C-=") 'text-scale-increase) -(global-set-key (kbd "C--") 'text-scale-decrease) +(with-eval-after-load 'ghostel + (evil-define-key 'normal ghostel-mode-map + (kbd "g t") #'ghostel-next + (kbd "g T") #'ghostel-previous + (kbd "g n") #'ghostel + (kbd "g b") #'ghostel-list-buffers)) + +(with-eval-after-load 'helm + (keymap-set helm-map "C-j" #'helm-next-line) + (keymap-set helm-map "C-k" #'helm-previous-line)) + +(evil-set-initial-state 'help-mode 'emacs) + +(evil-set-initial-state 'ibuffer-mode 'normal) + +(evil-set-initial-state 'messages-buffer-mode 'normal) + +(defvar my/org-leader-map (make-sparse-keymap)) +(defvar my/org-command-map (make-sparse-keymap)) +(defvar my/org-set-map (make-sparse-keymap)) +(defvar my/org-link-map (make-sparse-keymap)) + +(setq my/org-leader-map (make-sparse-keymap) + my/org-command-map (make-sparse-keymap) + my/org-set-map (make-sparse-keymap) + my/org-link-map (make-sparse-keymap)) +(set-keymap-parent my/org-leader-map my/leader-map) +(keymap-set my/org-leader-map "m" my/org-command-map) +(keymap-set my/org-command-map "t" #'org-todo) +(keymap-set my/org-command-map "a" #'org-add-note) +(keymap-set my/org-command-map "C" #'org-capture) +(keymap-set my/org-command-map "f" #'org-fmt-buffer) +(keymap-set my/org-command-map "k" #'kitty-graphics-org-heading-sizes) +(keymap-set my/org-command-map "c" my/org-set-map) +(keymap-set my/org-set-map "d" #'org-deadline) +(keymap-set my/org-set-map "s" #'org-schedule) +(keymap-set my/org-set-map "e" #'org-set-effort) +(keymap-set my/org-set-map "r" #'org-clock-report) +(keymap-set my/org-command-map "," #'org-priority) +(keymap-set my/org-command-map "I" #'org-clock-in) +(keymap-set my/org-command-map "O" #'org-clock-out) +(keymap-set my/org-leader-map "l" my/org-link-map) +(keymap-set my/org-link-map "c" #'org-cliplink) +(keymap-set my/org-link-map "i" #'org-download-clipboard) +(keymap-set my/org-link-map "l" #'org-insert-link) +(keymap-set my/org-leader-map "n" #'org-toggle-narrow-to-subtree) + +(with-eval-after-load 'org + (keymap-set org-mode-map "C-c e" #'my/org-set-effort-from-complexity) + (keymap-set org-mode-map "C-c C-x C-i" #'org-clock-in) + (keymap-set org-mode-map "C-c C-x C-o" #'org-clock-out) + (evil-define-key '(normal visual) org-mode-map + (kbd "SPC") my/org-leader-map) + (evil-define-key 'insert org-mode-map + (kbd "M-SPC") my/org-leader-map + (kbd "TAB") #'indent-for-tab-command) + (evil-define-key 'normal org-mode-map + (kbd "RET") #'org-open-at-point)) + +(with-eval-after-load 'which-key + (which-key-add-keymap-based-replacements + my/org-leader-map "m" "org" "l" "link") + (which-key-add-keymap-based-replacements + my/org-command-map "c" "set")) + +(with-eval-after-load 'pass + (evil-define-key 'normal pass-mode-map + (kbd "q") #'quit-window + (kbd "j") #'pass-next-entry + (kbd "k") #'pass-prev-entry + (kbd "RET") #'pass-view + (kbd "d") #'pass-kill + (kbd "y") #'pass-copy + (kbd "Y") #'pass-copy-field + (kbd "e") #'pass-edit + (kbd "a") #'pass-insert + (kbd "G") #'pass-insert-generated + (kbd "o") #'pass-otp-options + (kbd "r") #'pass-rename + (kbd "/") #'isearch-forward)) + +(with-eval-after-load 'pass + (evil-define-key 'normal pass-view-mode-map + (kbd "q") #'quit-window + (kbd "t") #'pass-view-toggle-password + (kbd "y") #'pass-copy + (kbd "Y") #'pass-copy-field)) + +(with-eval-after-load 'persp-mode + (define-key persp-mode-map [remap delete-window] + #'my/workspace-close-window-or-workspace) + (define-key persp-mode-map [remap evil-window-delete] + #'my/workspace-close-window-or-workspace)) + +(with-eval-after-load 'smerge-mode + (keymap-set smerge-mode-map "C-c ^ u" #'smerge-keep-upper) + (keymap-set smerge-mode-map "C-c ^ l" #'smerge-keep-lower) + (keymap-set smerge-mode-map "C-c ^ n" #'smerge-next) + (keymap-set smerge-mode-map "C-c ^ p" #'smerge-previous)) (use-package elfeed :ensure (:host github :repo "emacs-elfeed/elfeed" :branch "main") @@ -3924,16 +4011,7 @@ reset is unnecessary, so do the handler resolution ourselves and skip shr-max-image-proportion 0.6 shr-width nil shr-cookie-policy nil) - (make-directory eww-bookmarks-directory t) - - (with-eval-after-load 'general - (my-local-leader - :keymaps 'eww-mode-map - "B" '(eww-add-bookmark :wk "add bookmark") - "r" '(eww-readable :wk "reader mode") - "i" '(my/eww-toggle-images :wk "toggle images") - "y" '(my/eww-copy-as-org :wk "copy page as org") - "d" '(eww-download :wk "download")))) + (make-directory eww-bookmarks-directory t)) (defun my/eww-new-buffer (url) "Open URL in a fresh eww buffer (keep current)." @@ -3980,12 +4058,6 @@ reset is unnecessary, so do the handler resolution ourselves and skip :ensure t :commands (link-hint-open-link link-hint-copy-link)) -(with-eval-after-load 'general - (general-def :states '(normal visual) - :keymaps '(eww-mode-map elfeed-show-mode-map elfeed-search-mode-map) - "f" #'link-hint-open-link - "F" #'link-hint-copy-link)) - (use-package mu4e :ensure nil :defer t @@ -4182,11 +4254,6 @@ opening another file in same project does not re-notify." (ghostel-compile-global-mode 1) (setq-default window-adjust-process-window-size-function #'window-adjust-process-window-size-largest) - (evil-define-key 'normal ghostel-mode-map - (kbd "g t") #'ghostel-next - (kbd "g T") #'ghostel-previous - (kbd "g n") #'ghostel - (kbd "g b") #'ghostel-list-buffers) (defun my/ghostel-transparent-buffer-face (fg _bg) (unless (equal fg ghostel--face-cookie-fg-bg) (when ghostel--face-cookie @@ -4216,36 +4283,45 @@ opening another file in same project does not re-notify." (inheritenv-add-advice 'ghostel-compile) (inheritenv-add-advice 'ghostel-recompile)) -(when-let* ((garden-dir (expand-file-name - (or (alist-get 'garden my/local-packages) "~/garden"))) - ((file-directory-p garden-dir))) - (use-package vui - :ensure (:host github :repo "d12frosted/vui.el" :files ("*.el"))) - - (add-to-list 'load-path (expand-file-name "lisp/" garden-dir)) - (setq garden-directory (file-name-as-directory garden-dir)) - (require 'garden-core) +(use-package garden-core + :ensure nil + :demand t + :custom + (garden-directory (file-name-as-directory (expand-file-name "~/garden"))) + :config (garden-auto-index-mode 1) - (autoload 'garden "garden-dashboard" nil t) - (autoload 'garden-sidecar "garden-dashboard" nil t) - (autoload 'garden-search "garden-dashboard" nil t) - (autoload 'garden-fleet "garden-fleet" nil t) - (autoload 'garden-connect "garden-connect" nil t) - (autoload 'garden-connect-pick "garden-connect" nil t) - (autoload 'garden-publish "garden-publish" nil t) - (autoload 'garden-publish-all "garden-publish" nil t) - (autoload 'garden-publish-wiki "garden-publish" nil t) - (autoload 'garden-publish-blog "garden-publish" nil t) - (autoload 'garden-publish-deploy "garden-publish" nil t) - (autoload 'denote-capf-setup "denote-capf" nil t) - (with-eval-after-load 'denote-capf - (setq denote-capf-directories '("~/org/"))) - (add-hook 'org-mode-hook #'denote-capf-setup) +) + +(use-package garden-dashboard + :ensure nil + :commands (garden garden-sidecar garden-search garden-search-text + garden-visit-random garden-reclassify-tag)) + +(use-package garden-fleet + :ensure nil + :commands (garden-fleet garden-fleet-import-all)) + +(use-package garden-connect + :ensure nil + :commands (garden-connect garden-connect-pick garden-connect-all)) + +(use-package garden-publish + :ensure nil + :commands (garden-publish garden-publish-one garden-publish-all + garden-publish-wiki garden-publish-blog garden-publish-pages + garden-publish-deploy)) - (defun my/denote-capf-tab-in-insert () - (when (featurep 'evil) - (evil-local-set-key 'insert (kbd "TAB") #'indent-for-tab-command))) - (add-hook 'org-mode-hook #'my/denote-capf-tab-in-insert)) +(use-package garden-links + :ensure nil + :commands (garden-insert-denote-links garden-update-denote-links)) + +(use-package denote-capf + :ensure nil + :commands (denote-capf-setup denote-capf-teardown denote-capf-refresh) + :custom + (denote-capf-directories '("~/org/")) + :hook + (org-mode . denote-capf-setup)) (use-package sops :ensure (:type git :host github :repo "djgoku/sops") @@ -4357,12 +4433,7 @@ opening another file in same project does not re-notify." (use-package rsync-ui :ensure nil - :commands (rsync-ui rsync-ui-dired) - :bind (:map dired-mode-map - ("C-c r" . rsync-ui-dired)) - :init - (with-eval-after-load 'general - (my-leader "or" '(rsync-ui :wk "rsync")))) + :commands (rsync-ui rsync-ui-dired)) (defun rlr/org-export-html-to-browser () "Export the current Org buffer to a temporary HTML file in the system temp directory, open it, and then delete it when Emacs is killed." @@ -4477,6 +4548,63 @@ opening another file in same project does not re-notify." (require 'clatter-org) (clatter-org-setup))) +(defvar my/bluesky-feeds + '(("❄ Nix and NixOS · @alesya.social" + . "at://did:plc:s44h5nbko454vhpa3oyza5lf/app.bsky.feed.generator/aaah6ge3eqpck") + ("λ Emacs · @spathi.bsky.social" + . "at://did:plc:f6p7kxtbvmsujg67gayjux7m/app.bsky.feed.generator/aaabl5c2jmqaw")) + "Named custom feeds offered by `my/bluesky-feed'.") + +(defun my/bluesky-feed () + "Select and open one of `my/bluesky-feeds' with Helm." + (interactive) + (require 'helm) + (bluesky-feed + (helm-comp-read "Feed: " my/bluesky-feeds + :alistp t + :buffer "*helm bluesky feeds*" + :fuzzy t + :must-match t + :name "🦋 Bluesky feeds"))) + +(use-package bluesky + :ensure (:host github :repo "ahyatt/emacs-bluesky") + :commands (bluesky + bluesky-author + bluesky-search + bluesky-tag + bluesky-feed + bluesky-notifications + bluesky-likes + bluesky-replies) + :config + (require 'password-store) + + ;; Feed, thread, search, author, and notification buffers all use + ;; `bluesky-mode'; composers use `bluesky-post-mode'. + (with-eval-after-load 'evil + (evil-set-initial-state 'bluesky-mode 'emacs) + (evil-set-initial-state 'bluesky-post-mode 'emacs)) + + ;; Thread views are opened with `pop-to-buffer', so quitting the window + ;; naturally reveals the timeline that was underneath. + (keymap-set bluesky-mode-map "q" #'quit-window) + + (defun my/bluesky-password-store-auth + (orig callback &optional username password host discard-result) + "Authenticate Bluesky with the password-store entry `bsky.app'." + (funcall orig + callback + (or username (password-store-get-field "bsky.app" "username")) + (or password (password-store-get "bsky.app")) + host + discard-result)) + + (unless (advice-member-p #'my/bluesky-password-store-auth + #'bluesky--authenticate) + (advice-add 'bluesky--authenticate + :around #'my/bluesky-password-store-auth))) + (use-package org-other-agenda :ensure (:host github :repo "yibie/org-other-agenda" |
