aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-14 15:47:55 -0500
committerCraig Jennings <c@cjennings.net>2026-06-14 15:47:55 -0500
commit63035cff73b0f93ceb3785cda1cd4a1f068ec58c (patch)
tree24e4a6b1fe861a2854d8373ed641591e77f4eaca
parent92c062b8b4cc70b09d5487d1cc29287ad52babdf (diff)
downloaddotemacs-63035cff73b0f93ceb3785cda1cd4a1f068ec58c.tar.gz
dotemacs-63035cff73b0f93ceb3785cda1cd4a1f068ec58c.zip
fix(slack): autoload w/@/# commands, guard close-all, register the prefix
Three lifecycle gaps that bit before slack loads. The w / @ / # keys bound slack-message-write-another-buffer, slack-message-embed-mention, and slack-message-embed-channel, none autoloaded or in :commands, so they void-function'd before slack started; added them to :commands. cj/slack-close-all-buffers read slack-current-buffer via buffer-local-value on every buffer, which signals void-variable on buffers without the local binding; it now guards with buffer-local-boundp like its sibling. And C-; S was bound with a raw global-set-key, invisible to the keybindings registry; it now registers through cj/register-prefix-map like the signal and erc prefixes.
-rw-r--r--modules/slack-config.el10
-rw-r--r--tests/test-slack-config-close-all.el32
-rw-r--r--todo.org10
3 files changed, 48 insertions, 4 deletions
diff --git a/modules/slack-config.el b/modules/slack-config.el
index 0902ef35c..adf38804c 100644
--- a/modules/slack-config.el
+++ b/modules/slack-config.el
@@ -45,6 +45,7 @@
(require 'system-lib) ;; provides cj/auth-source-secret-value
(require 'cl-lib)
+(require 'keybindings) ;; provides cj/register-prefix-map
(defvar slack-current-buffer)
(defvar slack-message-compose-buffer-mode-map)
@@ -120,7 +121,9 @@ or more panes; this pins the choice to any non-selected window."
:defer t
:commands (slack-start slack-select-rooms slack-select-unread-rooms
slack-im-select slack-thread-show-or-create
- slack-insert-emoji slack-register-team)
+ slack-insert-emoji slack-register-team
+ slack-message-write-another-buffer
+ slack-message-embed-mention slack-message-embed-channel)
:custom
;; Disabled: emojify-mode in lui buffers causes (wrong-type-argument listp)
;; errors on emoji characters during lui-scroll-post-command's recenter call.
@@ -243,7 +246,8 @@ swallows exceptions via `websocket-try-callback'."
(interactive)
(let ((count 0))
(dolist (buf (buffer-list))
- (when (buffer-local-value 'slack-current-buffer buf)
+ (when (and (buffer-local-boundp 'slack-current-buffer buf)
+ (buffer-local-value 'slack-current-buffer buf))
(let ((win (get-buffer-window buf t)))
(when (and win (not (window-dedicated-p win)))
(delete-window win)))
@@ -256,7 +260,7 @@ swallows exceptions via `websocket-try-callback'."
(defvar cj/slack-keymap (make-sparse-keymap)
"Keymap for Slack commands under C-; S.")
-(global-set-key (kbd "C-; S") cj/slack-keymap)
+(cj/register-prefix-map "S" cj/slack-keymap "slack")
(define-key cj/slack-keymap (kbd "s") #'cj/slack-start)
(define-key cj/slack-keymap (kbd "c") #'slack-select-unread-rooms)
diff --git a/tests/test-slack-config-close-all.el b/tests/test-slack-config-close-all.el
new file mode 100644
index 000000000..a7f5423b8
--- /dev/null
+++ b/tests/test-slack-config-close-all.el
@@ -0,0 +1,32 @@
+;;; test-slack-config-close-all.el --- cj/slack-close-all-buffers guard -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; cj/slack-close-all-buffers iterates every buffer. It must not signal
+;; void-variable when `slack-current-buffer' has no binding in a buffer (slack
+;; not loaded), and must kill only buffers where it is set non-nil. The original
+;; read it with `buffer-local-value' (which errors on buffers without the local
+;; binding) instead of guarding like its sibling cj/slack-mark-read-and-bury.
+
+;;; Code:
+
+(require 'ert)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'slack-config)
+
+(ert-deftest test-slack-close-all-buffers-skips-unbound-kills-slack ()
+ "Error/Normal: no signal on buffers without `slack-current-buffer'; only
+buffers that have it set non-nil are killed."
+ (let ((plain (generate-new-buffer " *plain*"))
+ (slackish (generate-new-buffer " *slackish*")))
+ (with-current-buffer slackish (setq-local slack-current-buffer t))
+ (unwind-protect
+ (progn
+ (cj/slack-close-all-buffers)
+ (should (buffer-live-p plain))
+ (should-not (buffer-live-p slackish)))
+ (when (buffer-live-p plain) (kill-buffer plain))
+ (when (buffer-live-p slackish) (kill-buffer slackish)))))
+
+(provide 'test-slack-config-close-all)
+;;; test-slack-config-close-all.el ends here
diff --git a/todo.org b/todo.org
index 12a62350e..7edf87a3f 100644
--- a/todo.org
+++ b/todo.org
@@ -986,11 +986,13 @@ From the 2026-06 config audit, =modules/erc-config.el=:
- =:238= — =user-whole-name= read at load but =user-constants= only required at compile time (same trap as auth-config/keyboard-macros).
Fixed 2026-06-14: removed =notifications= from =erc-modules= (kept the custom =cj/erc-notify-on-mention=, so one notification per mention); rewrote =cj/erc-connected-servers= to filter on =(erc-server-buffer-p)= + =(erc-server-process-alive)= instead of the tautological self-eq; moved =user-constants= to a runtime require. New test-erc-config-connected-servers.el (live-server-only + empty cases) 2 green; module byte-compiles. erc-config not reloaded into the daemon (live IRC session) — takes effect on restart. VERIFY for the one-notification + real-server-list behavior.
-** TODO [#B] slack-config lifecycle gaps :bug:quick:solo:
+** DONE [#B] slack-config lifecycle gaps :bug:quick:solo:
+CLOSED: [2026-06-14 Sun]
From the 2026-06 config audit, =modules/slack-config.el=:
- =:265= — w / @ / # bound to commands neither autoloaded nor in :commands — void-function before slack loads. Add to :commands.
- =:246= — =cj/slack-close-all-buffers= reads =slack-current-buffer= (declared but unbound) without the boundp guard its sibling has — void-variable on C-; S Q before slack loads.
- =:259= — raw =global-set-key= for C-; S bypasses =cj/register-prefix-map= (signal/erc use it); invisible to the keybindings registry and the planned unification enumeration.
+Fixed 2026-06-14: added =slack-message-write-another-buffer=, =slack-message-embed-mention=, =slack-message-embed-channel= to =:commands= (w/@/# now autoload); guarded =cj/slack-close-all-buffers= with =buffer-local-boundp= (no void-variable on C-; S Q before slack loads); switched =global-set-key= to =(cj/register-prefix-map "S" cj/slack-keymap "slack")= (+require keybindings). New test-slack-config-close-all.el green; module loads, C-; S registered in the registry. Not reloaded into the live daemon (active slack session) — restart to apply. VERIFY for the pre-load key safety.
** TODO [#B] erc-yank silently publishes >5-line pastes as public gists :bug:
=modules/erc-config.el:345= — C-y in any ERC buffer auto-creates a public gist for anything over 5 lines: clipboard content goes to a public URL with no confirmation, and no executable-find guard for =gist= (errors mid-send if absent). Privacy trap. Add a =yes-or-no-p= gate or drop the package for plain C-y. From the 2026-06 config audit.
@@ -4451,6 +4453,12 @@ From the 2026-06-11 messenger-unification brainstorm. Google Voice has no offici
** TODO Manual testing and validation
Exercised once the phases above land.
+*** VERIFY slack keys are safe before slack loads
+What we're verifying: the C-; S slack keys don't error before slack has started, and the prefix shows in which-key. Fixed in modules/slack-config.el; restart to apply (not reloaded into the live session).
+- Restart Emacs but do NOT run cj/slack-start
+- Press C-; S Q (close all), and C-; S w / @ / # (these previously void-function'd or void-variable'd before load)
+- Press C-; S and check which-key shows the "slack" prefix
+Expected: C-; S Q reports "Closed 0 Slack buffers" with no error; w/@/# either run or autoload slack cleanly (no void-function); the which-key popup lists the slack prefix.
*** VERIFY ERC fires one mention notification and lists real servers
What we're verifying: a mention pops a single desktop notification (not two), and cj/erc-connected-servers lists only live server connections. Fixed in modules/erc-config.el; takes effect after an Emacs restart (not reloaded into the live IRC session).
- Restart Emacs and reconnect ERC