Subject: Re: bug#44266: Transient input methods

From: Juri Linkov <[email-masked]>
Date: Thu, 05 Nov 2020 22:14:24 +0200

Here is a patch that implements support for transient input methods
in Isearch mode.

While 'C-x 8' simply adds own keys to the default isearch-mode-map,
input methods have to rely on isearch-process-search-multibyte-characters
in lisp/international/isearch-x.el.  Since it uses 'read-string'
to read a character, currently the activated transient input method
is deactivated in the minibuffer of 'read-string' that is a wrong place,
so needed to introduce a new separate function deactivate-transient-input-method,
thus renamed the previous function to activate-transient-input-method,
and had to add more global variables.  But in result everything works fine:

diff --git a/lisp/isearch.el b/lisp/isearch.el
index 245bf452b1..4fba4370d9 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -565,6 +565,10 @@ isearch-menu-bar-map
                   :help "Highlight all matches for current search string"))
     (define-key map [isearch-search-replace-separator]
       '(menu-item "--"))
+    (define-key map [isearch-transient-input-method]
+      '(menu-item "Turn on transient input method"
+                  isearch-transient-input-method
+                  :help "Turn on transient input method for search"))
     (define-key map [isearch-toggle-specified-input-method]
       '(menu-item "Turn on specific input method"
                   isearch-toggle-specified-input-method
@@ -747,6 +751,7 @@ isearch-mode-map
     ;; For searching multilingual text.
     (define-key map "\C-\\" 'isearch-toggle-input-method)
     (define-key map "\C-^" 'isearch-toggle-specified-input-method)
+    (define-key map "\C-x\\" 'isearch-transient-input-method)
 
     ;; People expect to be able to paste with the mouse.
     (define-key map [mouse-2] #'isearch-mouse-2)
@@ -1078,6 +1083,8 @@ isearch-forward
 \\[isearch-toggle-specified-input-method],
 and specify an input method you want to use.
 
+To activate a transient input method, type \\[isearch-transient-input-method].
+
 The above keys, bound in `isearch-mode-map', are often controlled by
  options; do \\[apropos] on search-.* to find them.
 Other control and meta characters terminate the search
diff --git a/lisp/international/isearch-x.el b/lisp/international/isearch-x.el
index d77234ec77..b22d0e968d 100644
--- a/lisp/international/isearch-x.el
+++ b/lisp/international/isearch-x.el
@@ -51,6 +51,18 @@ isearch-toggle-input-method
   (setq input-method-function nil)
   (isearch-update))
 
+;;;###autoload
+(defun isearch-transient-input-method ()
+  "Activate transient input method in interactive search."
+  (interactive)
+  (when default-transient-input-method
+    (let ((overriding-terminal-local-map nil))
+      (activate-transient-input-method))
+    (setq isearch-input-method-function input-method-function
+	  isearch-input-method-local-p t)
+    (setq input-method-function nil)
+    (isearch-update)))
+
 (defvar isearch-minibuffer-local-map
   (let ((map (copy-keymap minibuffer-local-map)))
     (define-key map [with-keyboard-coding] 'isearch-with-keyboard-coding)
@@ -117,6 +129,7 @@ isearch-process-search-multibyte-characters
 			  (cons last-char unread-command-events))
 		    ;; Inherit current-input-method in a minibuffer.
 		    str (read-string prompt isearch-message 'junk-hist nil t))
+	      (deactivate-transient-input-method)
 	      (if (or (not str) (< (length str) (length isearch-message)))
 		  ;; All inputs were deleted while the input method
 		  ;; was working.
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index dc435d9b17..7eab01d804 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -55,7 +55,7 @@ mule-keymap
 
 ;; Keep "C-x C-m ..." for mule specific commands.
 (define-key ctl-x-map "\C-m" mule-keymap)
-(define-key ctl-x-map "\\" 'transient-input-method)
+(define-key ctl-x-map "\\" 'activate-transient-input-method)
 
 (defvar describe-language-environment-map
   (let ((map (make-sparse-keymap "Describe Language Environment")))
@@ -1345,10 +1345,10 @@ default-input-method
                  mule-input-method-string)
   :set-after '(current-language-environment))
 
-(defcustom transient-input-method nil
+(defcustom default-transient-input-method nil
   "Default transient input method.
 This is the input method activated by the command
-`transient-input-method' (\\[transient-input-method])."
+`activate-transient-input-method' (\\[activate-transient-input-method])."
   :link  '(custom-manual "(emacs)Input Methods")
   :group 'mule
   :type '(choice (const nil)
@@ -1356,6 +1356,18 @@ transient-input-method
   :set-after '(current-language-environment)
   :version "28.1")
 
+(defvar current-transient-input-method nil
+  "The current input method temporarily enabled by `activate-transient-input-method'.
+If nil, that means no transient input method is activated now.")
+(make-variable-buffer-local 'current-transient-input-method)
+(put 'current-transient-input-method 'permanent-local t)
+
+(defvar previous-transient-input-method nil
+  "The input method that was active before enabling the transient input method.
+If nil, that means no previous input method was active.")
+(make-variable-buffer-local 'previous-transient-input-method)
+(put 'previous-transient-input-method 'permanent-local t)
+
 (put 'input-method-function 'permanent-local t)
 
 (defvar input-method-history nil
@@ -1531,36 +1543,43 @@ set-input-method
 (defvar toggle-input-method-active nil
   "Non-nil inside `toggle-input-method'.")
 
-(defun transient-input-method (&optional arg interactive)
+(defun activate-transient-input-method (&optional arg interactive)
   "Enable a transient input method for the current buffer.
-If `transient-input-method' was not yet defined, prompt for it."
+If `default-transient-input-method' was not yet defined, prompt for it."
   (interactive "P\np")
-  (when (or arg (not transient-input-method))
+  (when (or arg (not default-transient-input-method))
     (let* ((default (or (car input-method-history) default-input-method))
            (input-method
             (read-input-method-name
              (if default "Transient input method (default %s): " "Transient input method: ")
              default t)))
-      (setq transient-input-method input-method)
+      (setq default-transient-input-method input-method)
       (when interactive
-        (customize-mark-as-set 'transient-input-method))))
-  (let* ((previous-input-method current-input-method)
-         (history input-method-history)
-         (clearfun (make-symbol "clear-transient-input-method"))
+        (customize-mark-as-set 'default-transient-input-method))))
+  (let* ((clearfun (make-symbol "clear-transient-input-method"))
          (exitfun
           (lambda ()
-            (deactivate-input-method)
-            (when previous-input-method
-              (activate-input-method previous-input-method))
-            (setq input-method-history history)
+            (deactivate-transient-input-method)
             (remove-hook 'input-method-after-insert-chunk-hook clearfun))))
     (fset clearfun (lambda () (funcall exitfun)))
     (add-hook 'input-method-after-insert-chunk-hook clearfun)
-    (when previous-input-method
-      (deactivate-input-method))
-    (activate-input-method transient-input-method)
+    (let ((input-method-history input-method-history))
+      (setq previous-transient-input-method current-input-method)
+      (when previous-transient-input-method
+        (deactivate-input-method))
+      (activate-input-method default-transient-input-method)
+      (setq current-transient-input-method default-transient-input-method))
     exitfun))
 
+(defun deactivate-transient-input-method ()
+  (when current-transient-input-method
+    (let ((input-method-history input-method-history))
+      (deactivate-input-method)
+      (when previous-transient-input-method
+        (activate-input-method previous-transient-input-method)
+        (setq previous-transient-input-method nil))
+      (setq current-transient-input-method nil))))
+
 (defun toggle-input-method (&optional arg interactive)
   "Enable or disable multilingual text input method for the current buffer.
 Only one input method can be enabled at any time in a given buffer.