diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 01:47:51 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 01:48:05 +0200 |
| commit | 9b1fd26d28a02c108c986a68b98ab885d1893742 (patch) | |
| tree | b546ff606143ad9af901024543fe1329c746606f | |
| parent | bb95be8487700bfc14389f224dd59ec7d5b9883b (diff) | |
| download | emacs-9b1fd26d28a02c108c986a68b98ab885d1893742.tar.gz | |
md: 2026-09-04T01:47:55+02:00
| -rw-r--r-- | config.el | 83 | ||||
| -rw-r--r-- | config.org | 115 | ||||
| -rw-r--r-- | lisp/my-tinty.el | 85 |
3 files changed, 236 insertions, 47 deletions
@@ -60,6 +60,11 @@ Set per-host in the gitignored `local.el'.") (funcall orig name _keyword args))) (advice-add 'use-package-normalize/:ensure :around #'my/elpaca-skip-local) +(require 'my-tinty) +(setq my/tinty-theme-file (expand-file-name "themes/tinty-theme.el" user-emacs-directory) + my/tinty-reload-delay 0.15) +(my/tinty-auto-reload-mode 1) + (defun my/elpaca-write-lock-file () "Write exact revisions of all queued packages to `elpaca-lock-file'." (interactive) @@ -195,6 +200,13 @@ Set per-host in the gitignored `local.el'.") :type 'boolean :group 'appearance) +(use-package bookmark + :ensure nil + :custom + (bookmark-default-file (locate-user-emacs-file "bookmarks")) + (bookmark-save-flag 1) + (bookmark-watch-bookmark-file 'silent)) + (when (eq system-type 'berkeley-unix) ;; Start the Emacs server so emacsclient (and pinentry-emacs) can connect. (require 'server) @@ -2987,19 +2999,20 @@ so the working-tree diff stays visible until the user explicitly stages." :commands (sync-ui)) (defun my/reload-config () - "Re-tangle config.org and reload the generated config.el in place. -Applies changes to customs, hooks, keybindings, and variable -settings without restarting Emacs. Packages that are newly added -still require a restart since elpaca queues run at init time." + "Save, tangle and reload config.org in this Emacs process only." (interactive) + (require 'ob-tangle) (let* ((org-file (expand-file-name "config.org" user-emacs-directory)) (el-file (expand-file-name "config.el" user-emacs-directory)) (start (current-time))) - (message "Reloading config…") - ;; 1. Tangle org → el (skips up-to-date blocks automatically) - (org-babel-tangle-file org-file el-file "emacs-lisp") - ;; 2. Load the freshly tangled file - (load-file el-file) + (let ((inhibit-message t) + (message-log-max nil)) + (when-let* ((buffer (find-buffer-visiting org-file))) + (with-current-buffer buffer + (when (buffer-modified-p) + (save-buffer)))) + (org-babel-tangle-file org-file el-file "emacs-lisp") + (load el-file nil 'nomessage)) (message "Config reloaded in %.2fs" (float-time (time-since start))))) @@ -3118,8 +3131,11 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (call-interactively #'helm-projectile-rg)) (defun my/helm-find-in (dir) + "Recursively find files below DIR with Helm." (interactive "DFind files in: ") - (helm-find-1 dir)) + (let ((default-directory (file-name-as-directory + (expand-file-name dir)))) + (helm-find nil))) (defun my/helm-rg-in (dir) "Search DIR recursively with ripgrep via Helm." @@ -3752,6 +3768,53 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (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) +(defun my/evil-move-visual-lines (direction) + "Move the selected lines by DIRECTION, preserving the Visual selection." + (let* ((anchor (marker-position evil-visual-mark)) + (cursor (marker-position evil-visual-point)) + (type (evil-visual-type)) + (beg (save-excursion + (goto-char (min anchor cursor)) + (line-beginning-position))) + (end (save-excursion + (goto-char (max anchor cursor)) + (forward-line 1) + (point))) + (first-line (line-number-at-pos beg)) + (last-line (line-number-at-pos (max beg (1- end)))) + (final-newline (eq (char-before (point-max)) ?\n))) + (when (if (> direction 0) (< end (point-max)) (> beg (point-min))) + (evil-with-single-undo + (evil-move beg end (if (> direction 0) (1+ last-line) (- first-line 2))) + (unless final-newline + (save-excursion + (goto-char (point-max)) + (when (eq (char-before) ?\n) + (delete-char -1)))) + (let ((start (save-excursion + (goto-char (point-min)) + (forward-line (+ first-line direction -1)) + (point)))) + (evil-visual-make-selection + (+ start (- anchor beg)) (+ start (- cursor beg)) type)))) + (setq deactivate-mark nil))) + +(evil-define-command my/evil-move-visual-lines-down () + "Move selected lines down one line and remain in Visual state." + :keep-visual t + (interactive "*") + (my/evil-move-visual-lines 1)) + +(evil-define-command my/evil-move-visual-lines-up () + "Move selected lines up one line and remain in Visual state." + :keep-visual t + (interactive "*") + (my/evil-move-visual-lines -1)) + +(evil-define-key 'visual 'global + (kbd "N") #'my/evil-move-visual-lines-down + (kbd "P") #'my/evil-move-visual-lines-up) + (with-eval-after-load 'compile (keymap-set compilation-mode-map "C-c o" #'nix-compile-goto-option)) @@ -84,6 +84,15 @@ Set per-host in the gitignored `local.el'.") (advice-add 'use-package-normalize/:ensure :around #'my/elpaca-skip-local) #+end_src +* tinty-theme-sync + +#+begin_src emacs-lisp +(require 'my-tinty) +(setq my/tinty-theme-file (expand-file-name "themes/tinty-theme.el" user-emacs-directory) + my/tinty-reload-delay 0.15) +(my/tinty-auto-reload-mode 1) +#+end_src + * elpaca The lock file is activated in =init.el= before the first package order is @@ -231,6 +240,17 @@ normal package updates cannot silently rewrite pinned revisions. :group 'appearance) #+end_src +** shared bookmarks + +#+begin_src emacs-lisp +(use-package bookmark + :ensure nil + :custom + (bookmark-default-file (locate-user-emacs-file "bookmarks")) + (bookmark-save-flag 1) + (bookmark-watch-bookmark-file 'silent)) +#+end_src + * freebsd Settings that only apply on FreeBSD (system-type = berkeley-unix). @@ -3660,19 +3680,20 @@ moved to local.el #+begin_src emacs-lisp (defun my/reload-config () - "Re-tangle config.org and reload the generated config.el in place. -Applies changes to customs, hooks, keybindings, and variable -settings without restarting Emacs. Packages that are newly added -still require a restart since elpaca queues run at init time." + "Save, tangle and reload config.org in this Emacs process only." (interactive) + (require 'ob-tangle) (let* ((org-file (expand-file-name "config.org" user-emacs-directory)) (el-file (expand-file-name "config.el" user-emacs-directory)) (start (current-time))) - (message "Reloading config…") - ;; 1. Tangle org → el (skips up-to-date blocks automatically) - (org-babel-tangle-file org-file el-file "emacs-lisp") - ;; 2. Load the freshly tangled file - (load-file el-file) + (let ((inhibit-message t) + (message-log-max nil)) + (when-let* ((buffer (find-buffer-visiting org-file))) + (with-current-buffer buffer + (when (buffer-modified-p) + (save-buffer)))) + (org-babel-tangle-file org-file el-file "emacs-lisp") + (load el-file nil 'nomessage)) (message "Config reloaded in %.2fs" (float-time (time-since start))))) #+end_src @@ -3817,8 +3838,11 @@ place. `C-c C-c' commits, `C-c C-k' aborts." #+begin_src emacs-lisp (defun my/helm-find-in (dir) + "Recursively find files below DIR with Helm." (interactive "DFind files in: ") - (helm-find-1 dir)) + (let ((default-directory (file-name-as-directory + (expand-file-name dir)))) + (helm-find nil))) (defun my/helm-rg-in (dir) "Search DIR recursively with ripgrep via Helm." @@ -4495,38 +4519,55 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (define-key evil-outer-text-objects-map "q" #'my-evil-textobj-anyblock-a-quote) #+end_src -** COMMENT agent-shell-mode +*** move-visual-lines #+begin_src emacs-lisp -(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)) -#+end_src - -*** COMMENT agent-shell-viewport-view-mode +(defun my/evil-move-visual-lines (direction) + "Move the selected lines by DIRECTION, preserving the Visual selection." + (let* ((anchor (marker-position evil-visual-mark)) + (cursor (marker-position evil-visual-point)) + (type (evil-visual-type)) + (beg (save-excursion + (goto-char (min anchor cursor)) + (line-beginning-position))) + (end (save-excursion + (goto-char (max anchor cursor)) + (forward-line 1) + (point))) + (first-line (line-number-at-pos beg)) + (last-line (line-number-at-pos (max beg (1- end)))) + (final-newline (eq (char-before (point-max)) ?\n))) + (when (if (> direction 0) (< end (point-max)) (> beg (point-min))) + (evil-with-single-undo + (evil-move beg end (if (> direction 0) (1+ last-line) (- first-line 2))) + (unless final-newline + (save-excursion + (goto-char (point-max)) + (when (eq (char-before) ?\n) + (delete-char -1)))) + (let ((start (save-excursion + (goto-char (point-min)) + (forward-line (+ first-line direction -1)) + (point)))) + (evil-visual-make-selection + (+ start (- anchor beg)) (+ start (- cursor beg)) type)))) + (setq deactivate-mark nil))) -#+begin_src emacs-lisp -(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)) -#+end_src +(evil-define-command my/evil-move-visual-lines-down () + "Move selected lines down one line and remain in Visual state." + :keep-visual t + (interactive "*") + (my/evil-move-visual-lines 1)) -*** COMMENT async-shell-command buffer +(evil-define-command my/evil-move-visual-lines-up () + "Move selected lines up one line and remain in Visual state." + :keep-visual t + (interactive "*") + (my/evil-move-visual-lines -1)) -#+begin_src emacs-lisp -(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)) +(evil-define-key 'visual 'global + (kbd "N") #'my/evil-move-visual-lines-down + (kbd "P") #'my/evil-move-visual-lines-up) #+end_src ** compilation-mode diff --git a/lisp/my-tinty.el b/lisp/my-tinty.el new file mode 100644 index 0000000..3780b26 --- /dev/null +++ b/lisp/my-tinty.el @@ -0,0 +1,85 @@ +;;; my-tinty.el --- Follow Tinty changes in this Emacs -*- lexical-binding: t; -*- + +(require 'filenotify) + +(defgroup my/tinty nil + "Automatic Tinty theme updates." + :group 'faces) + +(defcustom my/tinty-theme-file + (expand-file-name "themes/tinty-theme.el" user-emacs-directory) + "Shared theme source to watch in every Emacs process." + :type 'file + :group 'my/tinty) + +(defcustom my/tinty-reload-delay 0.15 + "Seconds to combine filesystem events before loading the theme." + :type 'number + :group 'my/tinty) + +(defvar my/tinty-auto-reload-mode nil) +(defvar my/tinty--watch nil) +(defvar my/tinty--timer nil) +(defvar my/tinty--loaded-content nil) + +(defun my/tinty--reload () + "Reload only changed theme content, without success messages." + (setq my/tinty--timer nil) + (when (and my/tinty-auto-reload-mode (file-readable-p my/tinty-theme-file)) + (condition-case err + (let* ((content (with-temp-buffer + (insert-file-contents-literally my/tinty-theme-file) + (secure-hash 'sha256 (current-buffer)))) + (inhibit-message t) + (message-log-max nil)) + (unless (equal content my/tinty--loaded-content) + (load my/tinty-theme-file nil 'nomessage 'nosuffix) + (enable-theme 'tinty) + (setq my/tinty--loaded-content content))) + (error + (let ((inhibit-message t)) + (message "Tinty theme: %s" (error-message-string err))))))) + +(defun my/tinty--watch-start () + "Watch the theme directory, retrying if it is temporarily unavailable." + (setq my/tinty--timer nil) + (when my/tinty-auto-reload-mode + (condition-case nil + (unless (and my/tinty--watch (file-notify-valid-p my/tinty--watch)) + (setq my/tinty--watch + (file-notify-add-watch + (file-name-directory (expand-file-name my/tinty-theme-file)) + '(change attribute-change) #'my/tinty--changed)) + (my/tinty--reload)) + (file-notify-error + (setq my/tinty--timer (run-at-time 2 nil #'my/tinty--watch-start)))))) + +(defun my/tinty--changed (event) + "React only to events for the theme file or a stopped directory watch." + (when (and my/tinty-auto-reload-mode (equal (car event) my/tinty--watch)) + (cond + ((eq (cadr event) 'stopped) + (setq my/tinty--watch nil) + (when (timerp my/tinty--timer) (cancel-timer my/tinty--timer)) + (setq my/tinty--timer (run-at-time 2 nil #'my/tinty--watch-start))) + ((member (expand-file-name my/tinty-theme-file) (cddr event)) + (when (timerp my/tinty--timer) (cancel-timer my/tinty--timer)) + (setq my/tinty--timer + (run-at-time my/tinty-reload-delay nil #'my/tinty--reload)))))) + +;;;###autoload +(define-minor-mode my/tinty-auto-reload-mode + "Keep this Emacs's theme synchronized with Tinty, independently of config reloads." + :global t + :group 'my/tinty + (if my/tinty-auto-reload-mode + (unless (and my/tinty--watch (file-notify-valid-p my/tinty--watch)) + (when (timerp my/tinty--timer) (cancel-timer my/tinty--timer)) + (my/tinty--watch-start)) + (when (timerp my/tinty--timer) (cancel-timer my/tinty--timer)) + (let ((watch my/tinty--watch)) + (setq my/tinty--timer nil my/tinty--watch nil my/tinty--loaded-content nil) + (when watch (file-notify-rm-watch watch))))) + +(provide 'my-tinty) +;;; my-tinty.el ends here |
