aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--org-drill.el22
-rw-r--r--tests/test-org-drill-read-key-sequence.el108
2 files changed, 125 insertions, 5 deletions
diff --git a/org-drill.el b/org-drill.el
index 4d7479c..07de25d 100644
--- a/org-drill.el
+++ b/org-drill.el
@@ -1482,13 +1482,20 @@ of QUALITY."
(reverse intervals)))
(defun org-drill--read-key-sequence (prompt)
- "Just like `read-key-sequence' but with input method turned off."
+ "Just like `read-key-sequence' but with input method turned off.
+
+Uses `deactivate-input-method' / `activate-input-method' rather than
+`set-input-method', because `(set-input-method nil)' clears
+`default-input-method' as a side effect when no input method is
+currently active (upstream issues #52 and #58)."
(let ((old-input-method current-input-method))
(unwind-protect
(progn
- (set-input-method nil)
+ (when current-input-method
+ (deactivate-input-method))
(read-key-sequence prompt))
- (set-input-method old-input-method))))
+ (when old-input-method
+ (activate-input-method old-input-method)))))
(defun org-drill-reschedule (session)
"Returns quality rating (0-5), or nil if the user quit."
@@ -1810,14 +1817,19 @@ Consider reformulating the item to make it easier to remember.\n"
(org-drill-response-complete))
(defun org-drill-response-get-buffer-create ()
- "Create a response buffer."
+ "Create a response buffer.
+
+Propagates the caller's input method into the new buffer, but only when
+one is actually active — otherwise `(set-input-method nil)' would clear
+`default-input-method' as a side effect (upstream issues #52, #58)."
(let ((local-current-input-method
current-input-method))
(with-current-buffer
(get-buffer-create "*Org-Drill*")
(erase-buffer)
(org-drill-response-mode)
- (set-input-method local-current-input-method)
+ (when local-current-input-method
+ (activate-input-method local-current-input-method))
(current-buffer))))
(defun org-drill-presentation-prompt-in-buffer (session &optional prompt)
diff --git a/tests/test-org-drill-read-key-sequence.el b/tests/test-org-drill-read-key-sequence.el
new file mode 100644
index 0000000..672b034
--- /dev/null
+++ b/tests/test-org-drill-read-key-sequence.el
@@ -0,0 +1,108 @@
+;;; test-org-drill-read-key-sequence.el --- Tests for input-method-safe key reading -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Regression tests for `org-drill--read-key-sequence'.
+;;
+;; Upstream issues #52 (breadncup, 2023-11-24) and #58 (breadncup,
+;; 2024-12-31) reported that running an org-drill session silently
+;; nulled out the user's `default-input-method'.
+;;
+;; Root cause: the wrapper called `(set-input-method nil)' to disable
+;; the input method during the key read. In Emacs, calling
+;; `set-input-method' with nil while no input method is active has the
+;; documented side effect of clearing `default-input-method' as well —
+;; clobbering the user's persistent setting. The fix is to use the
+;; primitive that's specifically scoped to current state:
+;; `deactivate-input-method' on the way out, and `activate-input-method'
+;; on the way back.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org-drill)
+
+;;;; Helpers
+
+(defmacro with-stubbed-key-sequence (return-value &rest body)
+ "Run BODY with `read-key-sequence' replaced by a stub that returns RETURN-VALUE.
+Avoids the interactive prompt during batch test runs."
+ (declare (indent 1))
+ `(cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) ,return-value)))
+ ,@body))
+
+;;;; Normal cases
+
+(ert-deftest test-org-drill-read-key-sequence-normal-no-input-method-set ()
+ "When neither default- nor current-input-method is set, both stay nil."
+ (let ((default-input-method nil)
+ (current-input-method nil))
+ (with-stubbed-key-sequence "x"
+ (org-drill--read-key-sequence "prompt: "))
+ (should (null default-input-method))
+ (should (null current-input-method))))
+
+(ert-deftest test-org-drill-read-key-sequence-normal-returns-stubbed-value ()
+ "The wrapper returns whatever `read-key-sequence' returns."
+ (let ((default-input-method nil)
+ (current-input-method nil))
+ (should (equal "answer"
+ (with-stubbed-key-sequence "answer"
+ (org-drill--read-key-sequence "prompt: "))))))
+
+;;;; Boundary / regression — issues #52 and #58
+
+(ert-deftest test-org-drill-read-key-sequence-preserves-default-when-no-current ()
+ "Issue #52/#58 regression: default-input-method must survive the call.
+
+The reporter (breadncup) had `default-input-method' set to a Korean
+input method but no input method active at session start. Each
+org-drill rating prompt would silently clear `default-input-method'."
+ (let ((default-input-method "TeX")
+ (current-input-method nil))
+ (with-stubbed-key-sequence "x"
+ (org-drill--read-key-sequence "prompt: "))
+ (should (equal "TeX" default-input-method))))
+
+(ert-deftest test-org-drill-read-key-sequence-preserves-default-across-multiple-calls ()
+ "Repeated calls during a session don't accumulate damage to default-input-method."
+ (let ((default-input-method "TeX")
+ (current-input-method nil))
+ (with-stubbed-key-sequence "x"
+ (dotimes (_ 5)
+ (org-drill--read-key-sequence "prompt: ")))
+ (should (equal "TeX" default-input-method))))
+
+;;;; Error cases
+
+(ert-deftest test-org-drill-read-key-sequence-error-restores-default-after-throw ()
+ "If `read-key-sequence' errors, unwind-protect must still leave default-input-method intact."
+ (let ((default-input-method "TeX")
+ (current-input-method nil))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) (error "simulated read failure"))))
+ (should-error (org-drill--read-key-sequence "prompt: ")))
+ (should (equal "TeX" default-input-method))))
+
+;;;; Same bug pattern in org-drill-response-get-buffer-create
+
+(ert-deftest test-org-drill-response-get-buffer-create-preserves-default-input-method ()
+ "Issue #52/#58 regression in the response-buffer path.
+
+`org-drill-response-get-buffer-create' captures `current-input-method'
+from the caller, then propagates it to the new *Org-Drill* buffer via
+`set-input-method'. When the caller has no input method active, the
+captured value is nil — and `(set-input-method nil)' clears the global
+`default-input-method'. Same bug pattern, different function."
+ (let ((default-input-method "TeX")
+ (current-input-method nil))
+ (unwind-protect
+ (org-drill-response-get-buffer-create)
+ (when (get-buffer "*Org-Drill*")
+ (kill-buffer "*Org-Drill*")))
+ (should (equal "TeX" default-input-method))))
+
+(provide 'test-org-drill-read-key-sequence)
+
+;;; test-org-drill-read-key-sequence.el ends here