端末の中で完結する SKK 日本語入力。
擬似端末で子プロセスを包み、標準入力を横取りして確定した文字列だけを子に渡す。 未確定の文字は端末へ直接重ね描きするので、子アプリの画面には一度も現れない。
入力メソッドが端末アプリの層にいるため、キーは必ず「端末多重化器 → ttyskk → 子」の
順に流れる。X の入力メソッド層 (fcitx5 など) を使ったときのような、Ctrl+Z の
取り合いが構造的に起きない。
ttyskk # $SHELL を包む
ttyskk -- claude # 特定のコマンドを包む
ttyskk vim memo.txtGitHub から直に入れられる。端末で使うぶんにはこれだけでよい — GUI の入力
メソッド (fcitx5/) と C ABI (capi/) は付いてこない。
cargo install --locked --git https://github.com/barewalker/ttyskk--locked を付けると、同梱の Cargo.lock がそのまま使われる。付けないと cargo が
依存をその時点の最新で解決し直すので、依存の側が新しい Rust を要求していると止まる
ことがある。Rust は 1.88 以降が要る。
更新は同じコマンドに --force を足す。手元にクローンしてあるなら次のとおり。
cargo build --release
cargo install --path .依存するのは POSIX の擬似端末と VT100 系のエスケープ列を解する端末だけ。 tmux / screen / SSH / mosh のどれとも組み合わせられるし、なくても動く。 Linux と macOS で動く。WSL2 なら動くが、Windows ネイティブでは動かない (termios と POSIX 擬似端末を使っているため)。
変換には SKK の辞書が要る。
sudo apt install skkdic # Debian / Ubuntu / WSL
sudo pacman -S skk-jisyo # Arch置き場所が違う場合は TTYSKK_JISYO で指定する。
設定は無くても動く。変えたくなったら、設定できる項目を全部並べた見本を書き出す。
mkdir -p ~/.config/ttyskk
ttyskk --config-example > ~/.config/ttyskk/config.toml同じ変換エンジンを fcitx5 の入力メソッドとして動かせる。辞書と学習を端末の
ttyskk と共有するので、どちらで覚えた語も両方で先頭に出る。設定 (config.toml)
も同じものを読む。
こちらはソースからの組み立てが要る (fcitx5 の addon は ABI が版に結びつくため、 使っている fcitx5 と同じ環境で組む必要がある)。
sudo apt install fcitx5-modules-dev extra-cmake-modules cmake build-essential
git clone https://github.com/barewalker/ttyskk && cd ttyskk/fcitx5
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr && cmake --build build
sudo cmake --install build
setsid fcitx5 -r -d >/dev/null 2>&1 </dev/nullfcitx5-configtool の入力メソッド一覧に ttyskk が出るので追加する。仕組みは
docs/fcitx5-addon.md。
skkeleton や fcitx5-skk など、別の SKK 実装で溜めた学習を合流できる。
ttyskk --import ~/.skkeleton # skkeleton (nvim)
ttyskk --import ~/.local/share/fcitx5/skk/user.dict
# CorvusSKK (Windows)。WSL からは /mnt/c 経由で読める
ttyskk --import /mnt/c/Users/<利用者>/AppData/Roaming/CorvusSKK/userdict.txt符号化は自動で見分ける — BOM があれば信じ、無ければ UTF-8、それも駄目なら EUC-JP。
CorvusSKK の利用者辞書は UTF-16LE + BOM で書かれるが、そのまま渡してよい。
送り仮名ブロック (おくr /送/[り/送/]/ の [...]) は落とす。
既にある候補は動かさない。 学習の順序は「最近使った順」なので、取り込んだものを 先頭に置くと、いま使っている語より古い語が前に出てしまう。相手にしかない候補だけを 後ろへ足す。何度実行してもよい (足すものが無ければ 0 件と出る)。
利用者辞書 (~/.local/share/ttyskk/user.dict) を git に載せれば、職場と自宅で
覚えた語が行き来する。ttyskk を止めなくてよい。
保存も読み直しも「ディスクの現状を土台に、この起動で覚えたことを重ねる」方式
なので、外から書き換わっても自分の学習を失わない。取り込む側は利用者辞書の更新時刻
を見張っていて、git pull で新しくなればその場で読み直す。
cd ~/.local/share/ttyskk
git init -b main && git add -A && git commit -m "init"
git remote add origin <辞書用のリポジトリ>
git push -u origin mainもう一方の環境では clone する。
git clone <辞書用のリポジトリ> ~/.local/share/ttyskkあとは定期的に pull と push を回す。手元では ~/.local/bin/ttyskk-sync と
systemd の user timer (10 分おき) で回している。非対話にすること — 鍵の
パスフレーズを聞かれると、誰も答えられないまま止まる。
export GIT_SSH_COMMAND="ssh -o BatchMode=yes -o ConnectTimeout=10"
export GIT_TERMINAL_PROMPT=0| キー | 動作 |
|---|---|
C-j |
かなモードへ入る |
l |
ASCII へ戻る |
L |
全角英数へ |
q |
ひらがな ⇄ カタカナ |
C-q |
ひらがな ⇄ 半角カタカナ |
モードはカーソルの見た目で分かる。何も打っていない状態でも見て判断できる。 出し方は 3 通りから選べる。
[behavior]
mode_marker = "cell" # カーソル位置のセルに色を敷く (既定)
mode_marker = "symbol" # カーソル位置のセルに半角の記号を出す
mode_marker = "beside" # カーソルの右隣のセルに色を敷く
mode_marker = "letter" # カーソルの直後に あ / ア / 半 / A を出す
mode_marker = "off" # 何も描かず、カーソルの形だけで表すcell と beside は文字を足さない。 セルの色だけを変えるので、行の長さも中身も
変わらない。違いは置く場所だけ。
| モード | 色 | symbol の記号 |
letter の印 |
off のときの形 |
|---|---|---|---|---|
| ASCII | 色を敷かない | 記号なし | 印なし | 下線 |
| ひらがな | 緑 | ~ |
あ | ブロック |
| カタカナ | 水色 | + |
ア | バー |
| 半角カタカナ | 青緑 | - |
半 | 点滅するバー |
| 全角英数 | 紫 | @ |
A | 点滅するブロック |
symbol は色に頼らない。 カーソル位置に半角一桁の記号を出すので、白黒の端末でも
モードが分かる。色も併せて付くので、色が使える環境では二重の手掛かりになる。cell と
違って下にある文字は隠れるが、かなを打っている間のカーソルはたいてい行末の空きセルに
あるので実害は小さい。
記号にしてあるのは、英字だと本文と紛れるため。既定は「ひらがなは曲線的な ~、
カタカナは角ばった +、半角カタカナは + から縦棒を取った - (半分)、全角英数は
英数の @」という覚え方。好みで変えられる。
[behavior.mode_symbols]
hiragana = "#"
katakana = "*"
hankaku_katakana = "="
zenkaku = "%"半角一桁の文字だけを受け付ける (全角を許すと幅が二桁になり、見た目が崩れる)。
ASCII で使えるのは ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ { | } ~。 反転表示の一桁で見分けやすいのは # * + @ % & ~ =` あたり。
モノクロの記号も使える。 ASCII より形の差が大きいので見分けやすい。
[behavior.mode_symbols]
hiragana = "♪" # 曲線的
katakana = "★" # 角ばった塗りつぶし
hankaku_katakana = "☆" # ★ の中抜き (半分)
zenkaku = "◆"通るのは ❤ ⚠ ★ ☆ ✓ ✔ ✗ ✖ ♪ ◆ ● ▲ ☺ ❄ ✎ ➤ ✚ ☯ ☀ ☂ ♦ ◎ ⌘ ⚑ ⚙ など。カラーの絵文字は
使えない — 😀 ⭐ ✅ ☕ ⚡ のたぐいは幅が二桁だから。❤️ のように異体字セレクタ
(U+FE0F) が付いたものも、二コードポイントになるので受け付けない (❤ なら通る)。
ただし ★ ☆ ◆ ● ▲ ◎ ♪ ☺ などは Unicode 上「幅が曖昧」な文字で、ttyskk は一桁と
見なすが、端末やフォントの設定によっては二桁で描かれる。その場合は印がずれるので、
切り替えて確かめてから使う。崩れるようなら ASCII の記号に戻すのが確実。
cell はカーソルのあるセルを塗るので、カーソルそのものが色付いたように見える。
このときはカーソルの形を下線に固定する — ブロックだと色を覆ってしまうため。つまり
形 = ttyskk が動いている合図、色 = モードという配分になる。素の端末ではこれが
いちばん自然に見える。
beside はカーソルの右隣を塗る。端末多重化器がカーソルの見た目を遅れて同期
する環境では、cell の色がブロックのカーソルに覆われて見えない (herdr が実際に
そう — カーソルの形も色も、多重化器が再同期するまで古いまま)。beside なら覆われ
ないので確実に見える。この方式ではカーソル自体には色を付けない — 付けると色付きの
ものが二つ並んで見えて紛らわしいため。色は箱が担い、カーソルは形だけで表す。
端末多重化器を挟むと、カーソルの色 (OSC 12) も形 (DECSCUSR) も外側の端末まで
届かない。 herdr で実測したところ、ペインからどちらを指定しても外側は白い四角の
まま変わらなかった (色だけは Ctrl+Z で prefix に入った瞬間に反映される — herdr が
そのタイミングでカーソルを同期しているため)。多重化器がカーソルを自分で描いている
以上、ttyskk 側から操る手段はない。
一方、文字として書いた色はそのまま届く。cell / beside / letter はこの性質を
使っている。
多重化器の下で cell が見えるかどうかは、多重化器が描くカーソルの形で決まる。
四角なら色を覆ってしまうので beside か letter にする。多重化器の側で下線やバーに
できるなら cell が使えて、下線と色が同じ場所に重なって見える — これがいちばん
収まりがよい。herdr なら次の二行で細いカーソルになる (全ペイン共通の固定値で、
モードには追随しない)。
# ~/.config/herdr/config.toml
reveal_hidden_cursor_for_cjk_ime = true
cjk_ime_cursor_shape = "underline"off は多重化器の下では形が届かないので何も分からなくなる。素の端末なら cell が
いちばん自然で、off でも形で五つのモードを区別できる。
ASCII モードでは何も描かないので、そのときの完全透過は保たれる。
子アプリがカーソルの形や色を変えた場合 (vim など) は、そのつど塗り直して
モードの合図を保つ。TTYSKK_NO_CURSOR を設定するとカーソルには一切触らない。
| キー | 動作 |
|---|---|
| 大文字 | 変換の開始 (▽)。Kanji → ▽かんじ |
| 途中の大文字 | 送り仮名の始まり。UgoKu → ▼動く |
space |
変換する / 次の候補へ |
x |
前の候補へ |
X |
▼ の候補を利用者辞書から取り除く |
C-j |
候補を確定する |
C-g |
取り消す |
q |
▽ の内容をカタカナにして確定 |
C-q |
▽ の内容を半角カタカナにして確定 |
Q |
空の見出し語で変換を始める (複合語向け) |
/ |
ASCII の見出し語で変換する |
> |
接頭辞 (▽ の途中) / 接尾辞 (▼ の途中) の変換 |
TAB |
▽ の見出し語を補完する / 次の補完へ |
S-TAB |
前の補完へ |
BS |
一文字戻る |
候補が 5 つ目に達すると横並びの一覧が出て、a s d f j k l で選べる。一覧に載らない
分は末尾に [残り 12] と件数で出る。ここに挙げたキーはすべて設定で変えられる。
一覧の出し方は 2 通りから選べる。
[candidates]
layout = "inline" # 入力中の行に続けて横並び (既定)
layout = "float" # カーソルの下の行に一行で浮かせる (最下行なら上の行)inline は行が伸びるぶん折り返すことがある。float は行が伸びず、縦にも広がらない
ので、nvim の補完 popup のような縦長の表示とも重なりにくい。浮かせた行の元の内容は
画面の控えから書き戻すので、vim や Claude Code の枠線の上に出しても消えない。
ddskk も既定はエコーエリアへの横並びで、CorvusSKK も「候補一覧を縦に表示する」が 設定項目 (= 既定は横) になっている。縦に並べるのは skkeleton (Vim の popup menu に 乗っているため) で、SKK の流儀というより実装上の都合。
Enter は候補の確定だけを行い、改行は送らない。端末では改行が「コマンドの実行」を
意味するため、変換の確定と取り違えると事故になる。
区切りの文字が来たら、space を押さなくても変換が始まる。 Honnyakuwo と
打てば を の直前までの「ほんやく」で変換に入り、を は候補の後ろに置かれる。
▽ほんやく →(を)→ ▼翻訳を →(space)→ ▼飜訳を
引き金の文字は見出し語に含めない — 含めると辞書を引けない。候補を送っても後ろに 付いたまま動き、確定すると「飜訳を」と出る。
既定の顔ぶれは ddskk の skk-auto-start-henkan-keyword-list と同じ。
[behavior]
auto_start_henkan = "を、。.,?」!;:);:)”】』》〉}]?.,!"
auto_start_henkan = [] # 空にすると自動変換をしない「2 文字めに《ん》が来る」「二重母音」という日本語に多い並びを 2 打で打てる。
[behavior]
romaji = "azik" # 既定は "default"母音キーの代わりにその下のキーを打つと「母音 + ん」になる (撥音拡張)。
| あ段 | い段 | う段 | え段 | お段 | |
|---|---|---|---|---|---|
| キー | z |
k |
j |
d |
l |
| 例 | kz かん |
kk きん |
kj くん |
kd けん |
kl こん |
母音キーの近くのキーが二重母音になる。
| あい | うう | えい | おう | |
|---|---|---|---|---|
| キー | q |
h |
w |
p |
| 例 | sq さい |
kh くう |
sw せい |
kp こう |
あ行には拡張を当てない (ai あい、aq あん)。ほかに次の決まりがある。
- 「っ」は
;、長音は:、単独の「ん」はq(nnも打てる) - シャ行は
x、チャ行はc—shchは二重母音拡張 (s+h= すう) に使うため - 小書きのかなは
lを前置 (laぁ、lyoょ、kulwaくゎ) —xをシャ行に譲ったため - 拗音の
yはgでも打てる (kgaきゃ、kgpきょう)。左右の交互打鍵になる - あ段の撥音は
nでも打てる (snさん、dnだん) - 同じ指が続く綴りは
fで (kfき、mfむ) - 頻出の並びは 2 打 (
ktこと、dsです、mnもの、srする)
変換表は標準ローマ字への展開として持っている。 kz → kann → 「かん」という
組み立てなので、かなの綴りを二重に持たない。子音の並びも標準表から集めるため、
表を足しても書き漏らしが起きない。
裏を返すと、子音の重ねで促音を打つ綴りは使えなくなる (tt は「たち」、kk は
「きん」)。AZIK では促音は ; を使う。
. と , から出す文字は四通りから選べる (ddskk の skk-kutouten-type と同じ)。
[behavior]
kutouten = "jp" # 。 、 (既定)
kutouten = "en" # . ,
kutouten = "jp-en" # 。 ,
kutouten = "en-jp" # . 、差し替わるのは . , から出るものだけで、z. の … や z, の ‥ はそのまま。
辞書に無い複合語をその場で組み立てる。SKK-JISYO.L にはこの形の項目が約 1,200 ある。
Aka> → ▼赤 (▽あか> として引く。> を押した時点ですぐ変換)
Pen space → ▼ペン → 赤ペン
Kandou space → ▼感動
> → ▽> (候補を確定し、> から始まる見出し語を立てる)
teki space → ▼的 → 感動的
▽ の途中の > は見出し語の末尾に付いてすぐ変換を始める。▼ の途中の > は
いまの候補を確定してから、> で始まる新しい見出し語を立てる (space を待つ)。
変換していないときの > はただの文字として子へ渡る。ddskk / skkeleton と同じ作り。
続けて確定した語は繋げて覚える。 「さい>」→再 のあと「りよう」→利用 と確定すると
さいりよう /再利用/ が利用者辞書に入る。接尾辞側も同じで、「かんどう」→感動 のあと
「>てき」→的 で かんどうてき /感動的/ になる。間に別の文字を打つと繋がらない
(隣り合っていないため)。ddskk の skk-learn-combined-word と同じ振る舞いで、
[behavior] learn_combined = false で止められる。
見出し語に数字が含まれると、その部分を # に置き換えた見出し語でも辞書を引く。
SKK-JISYO.L にはこの形の項目が 490 個あり、これが無いとどれも引けない。
Dai5kai space → ▼第5回 → 第5回 → 第五回
Q1234en space → ▼1234円 → 1234円 → 1,234円 → 千二百三十四円
数字は大文字にできないので、数字から始まる見出し語は Q で変換を先に始める。
候補の # に続く一桁が型を表す。
| 型 | 1234 が | 型 | 1234 が |
|---|---|---|---|
#0 |
1234 | #3 |
千二百三十四 |
#1 |
1234 | #5 |
壱阡弐百参拾四 |
#2 |
一二三四 | #8 |
1,234 |
#9 は将棋の棋譜 (2 桁を 34 → 3四)。#4 (数値再変換) は SKK-JISYO.L に
1 件しかなく辞書を再帰的に引く必要があるため扱わず、そのままの数字を出す。
学習は # のままの形で書き戻す。 数字を戻した形で覚えると だい#かい /第5回/
のようにその数字専用の項目になってしまう。打った通りの見出し語 (だい5かい) に
項目があれば、そちらが先に出る。
▼ の途中で X を押すと、いま選んでいる候補を利用者辞書から取り除く。誤って登録した語や、
学習で先頭に来てしまった誤変換を消すために使う。次の候補へ移り、候補が尽きたら ▽ に戻る。
共有辞書には手を触れない。 そちら由来の候補は次の変換でも出る — 消えるのは学習に よる先頭への繰り上がりだけ。手元に無い項目でも削除の記録は残すので、別のペインが 覚えたものが保存時に消える。
▽ の途中で TAB を押すと、前方一致する見出し語に伸ばす。もう一度押すと次の候補へ、
S-TAB で戻る。C-g は補完だけを取り消して元の見出し語に戻す (もう一度押すと ▽ ごと)。
Nihon TAB → ▽にほんご
TAB → ▽にほんじん
space → ▼日本人
補完の元は利用者辞書が先、共有辞書が後ろ。そこにあるのは実際に使った語なので、
17 万語から拾ったものより当たりやすい。同じ長さなら辞書順に並ぶ。送りありの見出し語
(うごk) は補完しても打ち直せないので出さない。
直接入力中の TAB は子へそのまま渡る。シェルの補完は殺さない。
候補が見つからないとき、または候補を出し切ったところでもう一度 space を押すと
登録に移る。見出しが [登録:てがき] の形で出るので、そのまま打ち込んで Enter。
| キー | 動作 |
|---|---|
Enter |
登録して確定する |
C-g |
登録をやめて ▽ に戻る |
BS |
一文字消す。空のところで押すと ▽ に戻る |
登録の中でも変換はそのまま使える。未知語が出てくればもう一段積まれ、[[登録:...]]
のように括弧が重なる。送りありの語は語幹だけを登録すればよい (UgoKu →
[登録:うご*く] に「動」と打つと「動く」が出て、辞書には うごk /動/ が入る)。
長い文字列を登録しておけば、そのままスニペットとして使える。
キーの割り当ては ~/.config/ttyskk/config.toml で変えられる。書いた項目だけが
既定を上書きするので、変えたいものだけ書けばよい。
設定できる項目を全部、既定値のまま並べて # で無効にした見本を実行ファイルに
埋め込んである (config.example.toml と同じもの)。書き出して、変えたい行の # を
外すのがいちばん早い。
mkdir -p ~/.config/ttyskk
ttyskk --config-example > ~/.config/ttyskk/config.toml[keys]
kana = "C-o" # かなモードへ入るキーを変える
cancel = ["C-g", "esc"] # 複数割り当てるなら並びで書く
select = ["1", "2", "3", "4"] # 候補一覧から選ぶキー (個数 = 一頁の候補数)
[candidates]
inline = 2 # 2 つ目の候補から一覧を出す
[behavior]
ascii_keys = ["esc", "C-c"] # 押すと ASCII モードへ戻るキー
mode_marker = "letter" # モードの印の出し方 (cell / letter / off)
learn_combined = false # 接頭辞・接尾辞に続く語を繋げて覚えないキーの書き方は C-j / Ctrl-j / ctrl+j、space enter tab esc bs、
q / のような一文字そのもの。場所は XDG_CONFIG_HOME を尊重し、TTYSKK_CONFIG
で直接指定もできる。
書き換えは動いている ttyskk にそのまま反映される。 起動し直さなくてよい。 書き方を間違えた設定は捨てられ、それまでの設定が使われ続ける — 変換の途中で キーが効かなくなる事態を避けるため。手元で確かめるには次を使う。
ttyskk --check-configEnter だけは割り当てを変えられない。変換中は確定として働き、そうでなければ
そのまま子へ流す。端末では改行が「コマンドの実行」を意味するので、変換の途中で
子へ送るわけにいかない一方、直接入力では必ず届かなければならない。
Esc と C-c を押すと ASCII モードへ戻る。 挿入モードを抜けたのにかなモードが
残っていると、次の dd や :w が日本語になってしまうため。どちらも子アプリには
そのまま渡るので、vim 側の設定は要らない。
ttyskk -- nvim memo.txti で挿入 → C-j でかな → 打つ → Esc で ASCII、という往復がそのまま回る。
既定が Esc と C-c の二つなのは nvim で実測した結果による。C-d は挿入モードを
抜けず、インデントを一段戻す動作なので入れていない。増やしたいときは設定で足せる。
[behavior]
ascii_keys = ["esc", "C-c", "C-o"]ascii_keys = [] にすると何もしない。辞書登録の途中では効かない (打ち込んだ内容が
消えると困るため)。
押したキーはそのまま子へも渡る。 便乗しているだけで、横取りはしない。子アプリ側で
そのキーに割り当てられた動作は起きる — 例えば Claude Code の Ctrl+C は入力欄を空に
するので、押せばモードが戻ると同時に打ちかけの内容も消える。
すでに ttyskk の中にいる場合、二つ目は自分を子で置き換えて (exec) そのまま退く。
包み直しても外側が先にキーを取るので内側は永久に ASCII のまま働かず、辞書をもう一部
抱えるだけになる (常駐が倍) ためで、TTYSKK_ACTIVE という目印で判定している。
herdr の既定シェルを ttyskk にした状態でそのペインに ttyskk -- claude と打つ、
といった場面で効く。承知のうえで入れ子にしたいときは目印を外す。
env -u TTYSKK_ACTIVE ttyskk -- claudeClaude Code のような TUI は起動時に CSI > 1 u (kitty 鍵盤プロトコル) や
CSI > 4 ; 2 m (modifyOtherKeys) を有効にする。この状態では Ctrl+J が 0x0a
ではなく CSI 106;5u という形で届くため、素朴に読むとモード切り替えが効かない。
ttyskk は [keys] で SKK 自身の操作に割り当てられている修飾キー付きの打鍵に限って
この形を解釈する。既定なら Ctrl+J (かな) Ctrl+G (取り消し) Ctrl+Q (半角
カタカナ) Shift+Tab (前の補完候補) の四つ。割り当てを変えれば解釈する対象も一緒に
変わるので、キーを付け替えても拡張鍵盤プロトコルの下だけ効かない、ということは
起きない。
割り当ての無いキーは元のバイト列のまま子へ渡すので、Ctrl+Z や Shift+Enter など
子アプリ側の操作はそのまま働く。behavior.ascii_keys (既定 Esc と Ctrl+C) も
解釈しない — あれは子アプリが自分の操作に使っているキーへの便乗で、押したキーは
そのまま子へ渡るため、形を変えると子の操作まで変わってしまう。裏を返すと、拡張鍵盤
プロトコルを使うアプリの下では ascii_keys による ASCII 復帰は効かない。
一方で、ttyskk が解釈するキーに子アプリが割り当てている操作は使えなくなる。
Claude Code で改行を入れたいときは Shift+Enter か \ + Enter を使う。
端末は貼り付けた内容を CSI 200~ と CSI 201~ で挟んで送る (括弧付き貼り付け)。
挟まれた中身は打鍵ではないので、ローマ字変換にもモード切り替えにも回さない。
囲みごとそのまま子へ渡す。
素朴に一文字ずつ処理すると、かなモードのまま hello を貼ったときに he が「へ」へ
変わり、続く l が ASCII モードへの切り替えとして食われて へlo になってしまう。
モードが何であっても、貼ったものは貼ったとおりに入る。
変換の途中で貼った場合は、見出し語や候補を先に確定してから貼り付けを流す (矢印キーを 押したときと同じ扱い)。辞書登録の途中で貼った場合だけは、子へ出さずに登録内容へ足す。
| 種類 | 既定の場所 |
|---|---|
| 共有辞書 | /usr/share/skk/SKK-JISYO.L、/run/host/usr/share/skk/SKK-JISYO.L |
| 利用者辞書 | $XDG_DATA_HOME/ttyskk/user.dict |
| 同梱辞書 | バイナリに埋め込み (dict/SKK-JISYO.ttyskk) |
TTYSKK_JISYO (: 区切り)、TTYSKK_USER_JISYO で変えられる。EUC-JP と UTF-8 の
どちらでも読める。
利用者辞書が無い初回に限り、~/.local/share/fcitx5/skk/user.dict があれば読み込む。
fcitx5-skk からの乗り換えで学習内容がそのまま引き継がれる。
丸数字は同梱している。 どの標準辞書にも入っていないため、辞書の設置を待たずに 使えるようバイナリへ埋め込んである。読みは二通りあり、どちらでも同じものが出る。
| 打ち方 | 結果 |
|---|---|
Maru1 space |
① |
C1 space |
① |
C21 space |
㉑ |
C50 space |
㊿ |
C は circle。Kanji → ▽かんじ と同じ要領で、大文字で始めると見出し語が c1 に
なる。数字が続くのでローマ字とは衝突しない。共有辞書に同じ見出し語があればそちらが
優先される。
候補が空、または半角の空白だけの項目は読み飛ばす。登録に失敗した跡としてしばしば 利用者辞書に残っており、共有辞書の正しい候補を覆い隠すため。全角空白は正当な候補 なので残す。
確定した候補は先頭へ移り、終了時に保存される。保存は「この起動で覚えたこと」だけを ディスクの内容に重ねる方式なので、複数の端末で同時に使っても学習を消し合わない。
mosh 越しに使う場合はリモート側で動くため、辞書と学習がそこに集まる。どの端末から 繋いでも同じ変換になる。
- 画面の内容には触らない — 書くのは重ね描きとカーソルの見た目だけで、格子の
中身は一切書き換えない。ASCII モードで書くのはカーソルの形と色 (モードの合図)
だけなので、
lessのような全画面アプリの表示は乱れない。TTYSKK_NO_CURSORを 設定すると本当に 1 バイトも書かなくなり、素の実行とバイト単位で一致する (起動時のカーソル位置の問い合わせだけは残るが、画面には現れない)。 - 常設の行を作らない — 最下行にモード表示を置かない。端末のスクロール領域に 触れないので、子アプリの画面配置を乱さない。
- 未確定文字は重ね描き — 子アプリには送らないので、変換を取り消しても子の 側には何も残らない。
- カーソル位置が分からないうちは描かない — 重ね描きの基準を得るため、子を
起こす前に
CSI 6nを送る (このときは子がまだ何も出力していないので、応答が 子の出力と混ざらない)。以降は子の出力を横から読んで画面の控え (src/screen.rs) を保つ。原点がずれた状態で描くと、消去が無関係のセルを空白で潰し、物理カーソル まで誤った場所へ動くのでシェルの出力がそこへ落ちる — 打った文字が消えたように 見える。壊すくらいなら描かないので、位置が分かるまで重ね描きを控える。 尋ね直すのは画面サイズの変更後 (折り返しが組み直されて絶対位置が変わる) と、 かなモードへ入った瞬間 (重ね描きを始める直前で、子が静かなことがほとんど)。 応答を待つのは 1.5 秒 — mosh のような遅い経路でも取りこぼさないため。 子が描き直し始めたあとに届いた応答は古いものとして捨てる。 - エスケープ列の途中には割り込まない — 子の出力が読み取り境界で切れている
間は重ね描きを控える (
src/input.rsのSeqTracker)。 - 書き戻したらカーソルを戻す — 重ね描きの消去は控えの内容を書き直すので カーソルと表示属性が動く。子は自分が居た場所に書くつもりなので、子の出力を 流す前に必ず戻す。戻さないと子の文字が一つずれた場所に落ちる。
不具合を追うときは TTYSKK_DEBUG にパスを渡すと、控えのカーソル位置・子の出力・
打鍵ごとの重ね描きを書き出す。指定しなければ何も書かない。
| ファイル | 役割 |
|---|---|
変換エンジン (src/lib.rs) と、端末に載せる部分 (src/main.rs) に分かれている。 |
|
| エンジンは画面まわりを一切持たないので、別の入力メソッドに載せることもできる。 |
| ファイル | 役割 | |
|---|---|---|
src/skk.rs |
SKK の状態機械 | エンジン |
src/romaji.rs |
ローマ字からかなへの変換表 | エンジン |
src/dict.rs |
辞書の読み込み・引き当て・学習 | エンジン |
src/config.rs |
設定ファイルの読み込みと見張り | エンジン |
src/num.rs |
数値変換 (# の抜き出しと戻し) |
エンジン |
src/main.rs |
擬似端末と入出力の仲介 | 端末 |
src/screen.rs |
画面の控え (文字と表示属性の格子) | 端末 |
src/render.rs |
重ね描きと、控えからの書き戻し | 端末 |
src/input.rs |
入力バイト列のキーへの切り出し | 端末 |
エンジンだけを使うときは端末側の依存 (擬似端末・端末制御) を落とせる。
[dependencies]
ttyskk = { version = "0.1", default-features = false }キーを一つずつ Skk::handle に渡し、Response を出力に回す。入力中の表示は
Skk::preedit、候補の一覧は Skk::candidates で、いずれも組み上げ前の形で取れる。
Response は確定した文字列と、解釈しなかったキーを分けて返す。
pub struct Response {
pub commit: String, // 確定した文字列
pub passthrough: Option<Key>, // 解釈しなかったキー
pub mode_changed: bool,
}この二つは同時に起きる — ▽ の途中で矢印を押すと、見出し語を確定したうえで矢印を渡す。
端末ではどちらも子プロセスの標準入力という同じ穴へ流すので Response::to_child() で
一本のバイト列に組めるが、GUI の入力メソッドでは前者が「文字列の確定」、後者が
「このキーは使わなかった」というまったく別の知らせになる。
このエンジンを GUI の入力メソッドに載せる算段は
docs/fcitx5-addon.md にまとめてある (まだ実装は無い)。
- 数値変換の
#4(数値再変換) —SKK-JISYO.Lに 1 件のみ。辞書を再帰的に引く必要がある - 送り仮名の厳密一致 (
[り /送/]形式の送り仮名ブロック) - 動的補完 (打つそばから候補を出す)
- 候補一覧の縦並び — ddskk / CorvusSKK では設定で選べる
- skkserver — 辞書ファイルを共有すれば足りるとみて入れていない
矢印キーは見出し語を確定してから子へ渡す。SKK は語単位でその場で確定させるので、 文節変換型の入力メソッドのような「変換中のカーソル移動」は必要ない (skkeleton の 既定キー表にも矢印の割り当ては無い)。
MIT または Apache-2.0 のどちらかを選べる (LICENSE-MIT / LICENSE-APACHE)。
先行実装の sentimental-skk (GPL-3.0) は
設計の参考にしたが、移植ではない。NOTES.md にあるのは読み解いた内容を説明する
ための短い引用で、コードは Rust で新しく書いている。
ttyskk is an SKK Japanese input method that lives entirely inside the terminal.
It wraps a child process in a pseudo-terminal, intercepts stdin, and passes only
confirmed text to the child. Unconfirmed text is painted directly onto the
terminal as an overlay, so it never enters the child application's screen at all.
Because the input method sits at the terminal-application layer, keys always flow
in the order "multiplexer → ttyskk → child". This removes, structurally, the key
contention you get when an X-level input method (fcitx5 and friends) fights your
terminal multiplexer over Ctrl+Z.
Documentation is in Japanese, since the users are.