aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-28 04:11:45 -0400
committerCraig Jennings <c@cjennings.net>2026-06-28 04:11:45 -0400
commitda7ee0841924dfbd91c9a944e0bfeff6903bd8a1 (patch)
treeb3461a7d9aa3971b55bfeeab9aeddb06d1b088fe
parentce74e30562dfac3f599a94a45ac16c3c2319ab41 (diff)
downloademacs-wttrin-da7ee0841924dfbd91c9a944e0bfeff6903bd8a1.tar.gz
emacs-wttrin-da7ee0841924dfbd91c9a944e0bfeff6903bd8a1.zip
fix: don't steal focus when an async weather response renders
wttrin--display-weather ran switch-to-buffer from the async callback, so a response arriving after the user moved to another buffer yanked them back to *wttr.in*. It now renders with set-buffer; selecting the buffer is the interactive command's job (wttrin-query already does it at invocation), so a late response updates the buffer in place without changing the selected window.
-rw-r--r--tests/test-wttrin--display-weather-focus.el45
-rw-r--r--tests/test-wttrin--display-weather.el8
-rw-r--r--wttrin.el6
3 files changed, 54 insertions, 5 deletions
diff --git a/tests/test-wttrin--display-weather-focus.el b/tests/test-wttrin--display-weather-focus.el
new file mode 100644
index 0000000..cc08fdd
--- /dev/null
+++ b/tests/test-wttrin--display-weather-focus.el
@@ -0,0 +1,45 @@
+;;; test-wttrin--display-weather-focus.el --- Async render doesn't steal focus -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024-2026 Craig Jennings
+
+;;; Commentary:
+;; wttrin--display-weather runs from an async callback. If the user moved to
+;; another buffer while the fetch was in flight, rendering the result must not
+;; select *wttr.in* and steal the window.
+
+;;; Code:
+
+(require 'ert)
+(require 'wttrin)
+(require 'testutil-wttrin)
+
+(ert-deftest test-wttrin--display-weather-error-does-not-steal-focus ()
+ "Error: rendering an async response does not select *wttr.in*.
+If the user moved to another buffer while the fetch was in flight, displaying
+the result must leave the selected window on that other buffer."
+ (let ((other (get-buffer-create "*wttrin-other*")))
+ (unwind-protect
+ (progn
+ (set-window-buffer (selected-window) other)
+ (wttrin--display-weather "Paris" "Weather report: paris\n+10 C\n"
+ nil nil nil)
+ (should (eq (window-buffer (selected-window)) other)))
+ (when (get-buffer "*wttr.in*") (kill-buffer "*wttr.in*"))
+ (when (buffer-live-p other) (kill-buffer other)))))
+
+(ert-deftest test-wttrin--display-weather-normal-populates-buffer ()
+ "Normal: rendering still fills *wttr.in* with the weather content."
+ (let ((other (get-buffer-create "*wttrin-other*")))
+ (unwind-protect
+ (progn
+ (set-window-buffer (selected-window) other)
+ (wttrin--display-weather "Paris" "Weather report: paris\n+10 C\n"
+ nil nil nil)
+ (with-current-buffer "*wttr.in*"
+ (should (string-match-p "Weather report: Paris"
+ (buffer-string)))))
+ (when (get-buffer "*wttr.in*") (kill-buffer "*wttr.in*"))
+ (when (buffer-live-p other) (kill-buffer other)))))
+
+(provide 'test-wttrin--display-weather-focus)
+;;; test-wttrin--display-weather-focus.el ends here
diff --git a/tests/test-wttrin--display-weather.el b/tests/test-wttrin--display-weather.el
index 99ea067..3b9cdee 100644
--- a/tests/test-wttrin--display-weather.el
+++ b/tests/test-wttrin--display-weather.el
@@ -51,7 +51,10 @@ Weather report: Paris, France
;;; Normal Cases
(ert-deftest test-wttrin--display-weather-normal-valid-data-creates-buffer ()
- "Test that valid weather data creates and displays buffer correctly."
+ "Test that valid weather data creates and fills the buffer correctly.
+Display is the interactive command's job (wttrin-query selects the buffer at
+invocation); this async render only updates content, so it does not select a
+window."
(test-wttrin--display-weather-setup)
(unwind-protect
(testutil-wttrin-with-clean-weather-buffer
@@ -60,9 +63,6 @@ Weather report: Paris, France
;; Buffer should exist
(should (get-buffer "*wttr.in*"))
- ;; Buffer should be displayed
- (should (get-buffer-window "*wttr.in*"))
-
;; Buffer should have content
(with-current-buffer "*wttr.in*"
(should (> (buffer-size) 0))
diff --git a/wttrin.el b/wttrin.el
index e34fdb5..c0359d9 100644
--- a/wttrin.el
+++ b/wttrin.el
@@ -1176,7 +1176,11 @@ coordinates but can name the place)."
"Cannot retrieve weather data. Perhaps the location was misspelled?"))
(wttrin--add-to-location-history display)
(let ((buffer (get-buffer-create (format "*wttr.in*"))))
- (switch-to-buffer buffer)
+ ;; Render into the buffer without selecting it. This runs from an
+ ;; async callback; the command (wttrin-query) already showed the buffer
+ ;; at invocation time, so re-selecting here would steal focus if the
+ ;; user moved away while the fetch was in flight.
+ (set-buffer buffer)
;; wttrin-mode calls kill-all-local-variables, so it must run
;; before setting any buffer-local state (xterm-color, location)