Skip to content

Conversation

@tooro88
Copy link

@tooro88 tooro88 commented Nov 3, 2025

「emacs 本体の関数の上書きを無くしたい #29」の実装の続き、input method 方式での実装です。「isearch拡張機能のadviceによる再実装 #30」の commit に続ける形の commit 群です。#30 で修正が入ったら、こちらも取り換えます。(#30 が済んでからこちらを提出してもよかったのですが、早めに全体像が見えていた方がよいかとも思い、先に提出します。これで私が予定していた修正は全てです。)

修正内容の説明

バグ修正2つ、後置部首/交ぜ書き変換の実装、前置部首/交ぜ書き変換の実装、バグ修正3つ、README修正、テスト追加、という順の9個の commit です。

いくつかのバグは、tcode-input-method の実行タイミングが変わることに起因します。その説明を #29 に書いておきました。

以下、commit log に書ききれなかったコメントです。特に従来実装に影響が無いかについて検討します。

Handle errors in input method to prevent isearch breakage (commit 3187239)

isearch 中にエラーが起きるとわけのわからないモードになって開発がしにくいので、最初にこの修正を入れました。

エラーを起こさなければ従来実装と同じ挙動です。

Fix non-terminating kbd macro when bushu conversion occurs last (commit 4935d2b)

input-method-functionnil を返すのはあまり望ましくないようです。代わりに、ダミーのイベント tcode-ignore を返し、次のように何もしないコマンド ignore にバインドしました。

(global-set-key [tcode-ignore] 'ignore)

global-map は交換可能なので、デフォルトの global-map でバインドするだけでは不十分なのでは? とも考えたのですが、global-map を取り換えるコードを emacs ソースから探したところ、Calc や edt (古いエディタのエミュレータ) など、日本語入力をしなさそうな状況のものばかりなので、問題ないと考えました。

tcode-use-input-methodnil のときは従来通り nil を返します。

Add isearch support for tcode-use-input-method (commit ef576d5)

(setq tcode-use-isearch :im) で input method 方式の isearch になるようにしました。

この commit では、後置部首/交ぜ書き変換まで実装しています。isearch 中の特殊な input-method-function の呼び出しの事情については、コード内のコメントで説明しています。

tc-bushu.el、tc-mazegaki.el に全く変更が無い点がセールスポイントです。

従来コードと比べると、ラッパーを挟んでいるだけです。this-commandisearch-with-input-method でない限り従来と同じコードパスを通ります。isearch-with-input-method が呼ばれるのは、isearch-process-search-multibyte-characters から。isearch-process-search-multibyte-characters が呼ばれるのは isearch-printing-char からですが、tc-is22.el でも、advice 方式でも、T-Code モードの場合はその呼び出しを通りません。

Implement prefix-bushu/mazegaki conversion in isearch (commit 5fcc755)

前置部首/交ぜ書き変換の実装です。tc-bushu.el、tc-mazegaki.el の変更は、変換開始関数にラッパーを挟んだのと、終了を知らせる関数呼び出しを追加したのみです。

前者(変換開始関数のラッパー)は、tcode--in-isearch-flag が立っていないときは何もしません。tcode--in-isearch-flag を立てるのは、前 commit のラッパーで isearch 中と判断したときですが、これは従来実装では通らないコードでした。後者(変換終了を知らせる関数)は、tcode--in-minibuffer-for-conversion-flag が立っていないときは何もしません。このフラグが立つのは、変換開始時に tcode--in-isearch-flag が立っていたときのみ通るコードです。結局、従来実装では新規コードの部分を通りません。

Fix undo amalgamation of insertion and deletion (commit 6c60284)

tc-ja-alnum input method と T-Code input method でどちらも last-command をセットしているのですが、不要と考え削除しました。last-command というのは、input method が呼ばれる前のコマンドで、例えばカーソル移動かも知れませんし、ファイルのセーブかも知れません。input method とは関係がありません。削除しないと、前の編集コマンドが文字挿入とみなされ、これから行なう挿入と一緒に undo されてしまいます。

tc-ja-alnum input method は小さいので、内部で last-command を用いていないのは明らかです(同時にセットしている last-command-event も用いていません)。T-Code input method も調べたところで内部では last-command を使ってなさそうです。last-command-event は、内部から呼ばれるサブコマンドで使用しているので、そのままにしてあります。

last-command は、次のコマンドが始まる前に this-command の値で上書きされてしまうので、内部で使っていないとすると、あと参照するタイミングは何かのフックぐらいしかなさそうです。ちょうど、tc-complete.el の completion 機能が post-command-hook を使ってその中で last-command を参照しているので、これとの連絡のために last-command を流用していたのだと推測しています。tc-complete.el はコードが壊れていて現状動作しないので、これへの影響を考える必要は現時点ではなさそうです。

この修正は、従来実装が通る部分の動作を変更しています。(unless tcode-use-input-method ...) として従来実装への影響を無くすこともできますが、上に述べた話でつじつまが合うことから、そこまでする必要もないかと考えました。

Fix handling of overriding-terminal-local-map (commit a0f2c27)

C-u 人 で「人」が4つ表示されるはずが、「m」が4つ表示されてしまいます。ほかにも set-transient-map された状況はすべて同様の問題があります。quail のコードを真似て修正しました。

これも従来実装で通る部分の修正ですが、input method 方式でこの修正に問題が無いのであれば、マイナーモード方式ではもっと安全のはずです。transient-map はもう解除されているはずですし、そもそも tcode-self-insert-command がコマンドとして実行中ということは、キーマップを参照するフェーズはもう済んでいるわけですから、このタイミングでキーマップの存在によって動作を変える必要性があるとは考えにくいです。

Fix SPC insertion being affected by previous prefix argument (commit 9b25833)

input method 方式で current-prefix-arg が使えないという #29 で説明したそのままの問題です。

if 文により、従来実装では従来通りの動作です。

Update README.md: add :im to the list of isearch options (commit 3c55356)

README.md に :im の説明を追加しました。

Add tests (commit 725c63c)

いくつか細かい挙動のテストを追加しました。

2実装の比較

片方だけ採用される可能性もあると思い、:advice 方式と :im 方式の利点・欠点を比較してみました。

  • :advice 方式、:im 方式 共通の利点
    • emacs 本体の関数を書き換えている部分が無くなった。
    • (その帰結として) emacs 側の新機能(isearch-lax-whitespace ぐらい?)が動作するようになった。
  • :advice 方式 の利点
    • コードが tc-is22.el のものとほとんど同じ(wrapped-search 部分を除いて)なので、従来と同様の動作が期待できる。
    • 新規コードが少ないのでレビューの手間が少ない。(tc-ishelper.el と、数行のロードファイル切り換え部分のみ)
  • :advice 方式 の欠点(従来のtc-is22.elから欠点ではあるが。)
    • tcode-input-method、部首/交ぜ書き変換のコードの一部が、isearch 用に2重に実装されている。
  • :im 方式 の利点
    • isearch中の部首/交ぜ書き変換のコードは、通常時と同一のもの用いる。このため、isearch 中の一部機能に改善がある(多重の前置部首変換、交ぜ書き変換確定後の RET が不要に。)
    • 一部 isearch 中使えなかった機能が使えるようになる。(カタカナモード切り換え、句読点切り換え)
    • 一応、input-method-function という、input method 実装のための正規の機能を使った実装になっている。
  • :im 方式 の欠点
    • 修正が大きい。
    • 使用実績が少なく、まだバグがどんどん出てくるフェーズのはず。
    • input-method-function に許されている以上のふるまいをしている感がある(後置変換によるバッファ改変、前置変換のための minibuffer 使用など)。そのために「hack」的な無理をしているとも言える(post-command-hook の多用)。

@tooro88
Copy link
Author

tooro88 commented Nov 8, 2025

#30 (advice 方式) で commit を分割したので、こちらも更新になります。こちらも少し commit を分割し、:advice'advice:im'im の修正をしました。

元の

  • (commit ef576d5) :im 有効化と後置変換実装
  • (commit 5fcc755) 前置変換実装

は、次の4つになりました。

  • (commit 287d5d1) 'im 有効化
  • (commit 9921d38) 後置変換実装
  • (commit 6c7ed4b) 前置変換実装
  • (commit 4883963) isearch中のモード変更のサポート

テストは、#30 の方の commit にまとめました(元々理由があって分けていたのではなく、PR作成タイミングの都合で分かれていただけなので)。

@tooro88 tooro88 force-pushed the isearch-im branch 2 times, most recently from c6f14cd to c429365 Compare November 19, 2025 14:50
@tooro88
Copy link
Author

tooro88 commented Nov 19, 2025

バグを見つけたので、commit を修正しました。

tcode-use-isearchim で、tcode-use-as-default-input-methodnil のときに動作がおかしくなる問題がありました。tcode-use-input-methodt にセットする場所がおかしく、値をセットする前に tc.el がロードされて、nil の値を参照してしまっていました。

`isearch-toggle-tcode` is only referenced in obsolete XEmacs-specific
code.  Remove both.
 - Clarify the variable's docstring to better describe its behavior.
 - Remove the redundant value t.
 - For values 0 and 1, fix incomplete handling of input method state
   changes: update the mode line and maintain consistent internal
   state by using `isearch-toggle-input-method`.
The term "wrapped search" in tc-is22.el is confusing.  It refers to a
search that ignores line wrapping, while Isearch already uses "wrap"
to describe restarting a search from the opposite end of the buffer.

Rename "wrapped search" in tc-is22.el to "line-fold search" to avoid
this ambiguity.
Split tc-is22.el into two files for future reuse:
 - Keep in tc-is22.el only the functions that override Emacs internal
   functions.  These will be reimplemented later.
 - Move the remaining functions to tc-iscommon.el, which will be
   reused in the forthcoming reimplementation.
Upcoming changes will provide multiple implementations of the Isearch
extensions.  The variable `tcode-use-isearch` will be used to select
among them.

Rename the default value `t` to `'overwrite`, which more accurately
describes the default implementation.
Rename the variable `tcode-isearch-type` to
`tcode-isearch-overwrite-module`, which more accurately describes its
role.
Add an `'advice` option to `tcode-use-isearch`.  When this option is
selected, the loader skips tc-is22.el and instead loads
tc-ishelper.el, which will reimplement the features of tc-is22.el
using advice in forthcoming commits.
The Isearch extensions provided by tc-is22.el override several
internal Emacs functions.  This approach is error-prone and can miss
new features introduced in recent Emacs versions.

tc-is22.el provides two features:
 1. line-fold search, which will be reimplemented in the next commit, and
 2. user input hooking, which is reimplemented here.

Replace the modification of `isearch-printing-char` with advice that
achieves the same behavior -- invoking the T-Code input method when
necessary -- so that the existing bushu/mazegaki conversion code in
tc-iscommon.el continues to work unchanged.
tc-is22.el implements line-fold search by modifying internal Isearch
functions to convert a search string into a regexp.  Since Emacs 25,
`isearch-regexp-function` has provided a cleaner way to perform such
conversions.

Use `isearch-regexp-function` to reimplement line-fold search, and
enable this implementation when `tcode-use-isearch` is `'advice`.
Together with the change in the previous commit, this completes the
reimplementation of the features previously provided by tc-is22.el.
New files:
 - tctest.el      : Test items.
 - tctest-play.el : Test driver.  Simulate user inputs using keyboard macro.
 - tctest-env.el, run.bash : Convenient scripts for selecting isearch options.
Add `:quiet` option to tctest-play and enable it when `quiet` keyword
is specified for tctest-env.
When `tcode-use-input-method` is non-`nil`, errors in
`tcode-input-method` can leave Isearch in an unrecoverable state.
Catch errors with condition-case and discard them after displaying
the error message.
When `tcode-use-input-method` is non-`nil`, a keyboard macro whose
final key invokes bushu conversion does not terminate automatically;
it terminates only after additional user input is supplied.

This occurs because `tcode-input-method` returns an empty event list
after performing bushu conversion. The top-level command loop in Emacs
contains an inner loop that repeatedly calls the input method as long
as it returns an empty event list.  This loop continues even after the
keyboard macro has finished, causing Emacs to wait for user input.

This issue can be triggered by any T-Code subcommand, not just bushu
conversion.  For the same reason, `post-command-hook` is not invoked
immediately after executing a subcommand.

Fix `tcode-input-method` so that it never returns `nil` as an event
list.  Instead, return a dummy event `tcode-ignore`, bound to the
no-op command `ignore`.

In addition, `tcode-ignore` now acts as an undo boundary that isolates
a bushu/mazegaki conversion from subsequent insertions.  Without this
commit, such a boundary would otherwise need to be implemented
separately to prevent unexpected undo amalgamation.
Add an `'im` option to `tcode-use-isearch`.  When this option is
selected, `tcode-use-input-method` is also set to `t` automatically,
thus enabling T-Code within Isearch.
When `tcode-use-input-method` is non-`nil`, the result of
postfix-bushu/mazegaki conversion in Isearch is discarded by the
caller of the input method.  Add a post-processing phase that updates
the Isearch state stack with the conversion result.
When `tcode-use-input-method` is non-`nil`, Isearch prepares a
minibuffer for invoking the input method.  However, that minibuffer is
not suitable for prefix-bushu/mazegaki conversion, because it exits
and discards its contents after each character input.  Instead of
using that minibuffer, prepare a dedicated one for the conversion,
which remains active until the entire conversion is completed.
When `tcode-use-input-method` is non-`nil`, Isearch invokes
`tcode-input-method` in the minibuffer.  This prevents the function
from referring to buffer-local variables in the editing buffer.

Since various T-Code modes use buffer-local variables to maintain
their state, the input method cannot determine which mode was
previously active.  Moreover, any mode changes made in the minibuffer
are immediately lost because the minibuffer is deactivated each time
`tcode-input-method` returns.

Copy the buffer-local variables that hold mode state to the minibuffer
when it is activated, and write back the modified values when it is
deactivated.  This allows the input method to behave correctly
according to the state of the editing buffer, and ensures that mode
changes persist across successive activations of the minibuffer.
An issue regarding `last-command` modification will be fixed by the
next commit.  Remove `:expected-result :failed` annotations from the
related tests.
Typing "a BACKSPACE b" and invoking undo once should delete only "a".
However, the undo records for BACKSPACE and "b" are amalgamated, so
both are undone at once.

This bug occurs with the `japanese-2byte-alnum` input method
(`tc-ja-alnum.el`), and with the T-Code input method when
`tcode-use-input-method` is enabled.  Both modify `last-command` to
`self-insert-command` for unknown reasons.  As a result, when "b" is
typed, BACKSPACE is treated as an insertion and amalgamated with the
insertion of "b".

Remove two `setq` forms in `japanese-2byte-alnum`: one that sets
`last-command` and another that sets `last-command-event`, which also
seems unnecessary.  For T-Coded, remove only the `setq` for
`last-command`.  The variable `last-command-event` is still used by
subcommands called within, such as
`tcode-mazegaki-self-insert-or-convert`, which eventually calls
`self-insert-command` and requires `last-command-event` to be set
properly.

The only possible reason I could find for setting `last-command` is
that `tc-comelete.el` refers to it.  However, this seems to be a bug:
the code uses `last-command` to determine the last command executed by
the user, where it should instead use `this-command`.  In any case,
`tc-comelete.el` is currently broken and would require a complete
rework to become usable again, so the impact of this change can be
safely ignored for now.
The following bugs, which occur when `tcode-use-input-method` is
non-`nil`, stem from the same cause and are fixed by this commit:

 - After `indent-rigidly`, typing an ordinary character normally exits
   the special mode and inserts that character, so editing can resume
   immediately. The same should happen when inputting a Japanese
   character, but instead an ASCII character appears as if the input
   method were disabled.
 - Inputting a Japanese character also produces an ASCII character
   when a prefix argument such as `C-u` or `M-3` is used.

Both `indent-rigidly` and commands that set prefix arguents (such as
`universal-argument`) call `set-transient-map`, which in turn
activates `overriding-terminal-local-map`.  When
`overriding-terminal-local-map` is active, `tcode-input-method` stops
processing input events entirely and treats them as ASCII characters.

Fix the handling of `overriding-terminal-local-map` in
`tcode-input-method` to match the behavior of `quail-input-method`: it
now stops processing the input event only if the event is bound in
`overriding-terminal-local-map`.

See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=68338 for the
discussion of the same issue in the Quail input method.
After mazegaki conversion has been used once, inserting a space
character (SPC) becomes affected by the prefix argument of the
previous command, rather than the current one.

When mazegaki conversion is used for the first time, `tc-mazegaki.el`
adds a binding for SPC to `tcode-mode-map`.  Thereafter, typing SPC is
handled by `tcode-input-method`, which executes the command
`tcode-mazegaki-space-or-convert` with the value of
`current-prefix-arg` as its prefix argument.

This issue occurs when `tcode-use-input-method` is non-`nil`.
Changing this variable affects when `tcode-input-method` is invoked.
When the value is `nil`, the function is called from
`tcode-self-insert-command`, at which point `current-prefix-arg`
correctly represents the prefix argument for
`tcode-self-insert-command`, so it is appropriate to pass it along to
the subcommand.  However, when `tcode-use-input-method` is non-`nil`,
`tcode-input-method` is invoked directly from the event loop, at a
time when no command is currently executing.  In that case,
`current-prefix-arg` still holds the prefix argument from the previous
command, so using it for the subcommand is incorrect.  Use
`prefix-argument` instead when `tcode-use-input-method` is non-`nil`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant