Karabinerで英数をoption空打ちに割り当てたら不意にDLE文字が入力されて困った話

# 前提

私はUS配列を愛用しているのですが、それで困るのは「英数」キーと「かな」キーが無いことです。入力切替の度にcontrol-spaceは面倒なので、 ~~Karabinerで左option空打ちを「英数」、右command空打ちを「かな」としています。~~
~~（左がcommandではないのは、ショートカットの入力で不意に英数に切り替わってしまうことが多いからです。）~~

最近はKarabiner-Elementsにて **左shift空打ちを「英数」、右shift空打ちを「かな」** にリマップして使っています。


また、Cocoaでemacs風のカーソル操作が使えるのが快適なので、C-p/n/b/f/a/eやC-d/hなどの組み合わせも多用します。

# いつの間にか謎の見えない文字（制御文字）が入力されてしまう

Cocoaではoptionキーを押しながらカーソル移動をすると単語ごとにジャンプしてくれます。ここにshiftを組み合わせると単語ごとの選択も可能です。特にコーディング時にはメソッド名や変数名の「いいところ」までを一気に選択できるので便利です。

私は文字入力でのカーソル移動は基本的にすべてemacs風に行っているので、先に挙げた単語移動も多用するのですが、割と多い頻度で起こる、見た目問題ないコードなのにビルドエラーになってしまう現象に悩みました。コード中ならまだエラーが出るので良いのですが、文字列の中身にいたりするとこれに気づかないことが多いです。

![1.png](https://qiita-image-store.s3.amazonaws.com/0/3603/d23a0672-48fc-5046-fdc6-81478a380a28.png)
_':'の箇所で謎のエラーが生じる_

画像の':'の箇所には、実は幅0の文字が存在しています。Sublime Textにコピペするとこれが制御文字の DLE - DATA LINK ESCAPE だとわかりました。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/3603/c3235325-d14e-52a8-f0cd-a52905532163.png)
_見えない文字の正体はDLEだった_

見えない文字の正体がわかったところで、これが入力されてしまう原因と対処を考えました。


# Karabiner-Elements で制御文字の入力を防ぐ

そもそもなぜ DLE をはじめとした制御文字が不意に入力されるのかというと、どうやら日本語入力プログラムのバグ(?)により、`control` + `かな`, `control` + `英数` の組み合わせにてDLEが挿入されてしまうようです。そのほかにも、`control` + `option` + ANYでさまざまな不可視文字が挿入されることがわかりました。

optionやcontrolの組み合わせはテキスト操作において多用するため、いつの間にか制御文字が挿入されてしまうことがある、というわけですね。困りました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/HiromitsuTakagi?ref_src=twsrc%5Etfw">@hiromitsutakagi</a> 更に調べてみたところ、デフォルトの日本語IMでは「Control+かな」が上矢印として処理されるバグが原因のようです。</p>&mdash; Takayama Fumihiko (@tekezo) <a href="https://twitter.com/tekezo/status/582170153057591296?ref_src=twsrc%5Etfw">March 29, 2015</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



## 英数、かな のリマップ定義で修飾キーを受け入れないようにする

Karabiner-Elementsの場合はComplex Modificationsでリマップを定義しますが、`from`で**修飾キーを受け入れない**記述にするとDLE文字の入力を防げるようになります。もしも`"modifiers": {"optional": ["any"]}`などの記述があったら削除してみてください。

```json:shift空打ちをぞれぞれ英数、かなにリマップ
{
  "title": "日本語向け設定",
  "rules": [
    
	{
		"description": "⇧ ▸ 英数 / かな",
		"manipulators": [
			{
				"type": "basic",
				"from": {
					"key_code": "left_shift"
				},
				"to": [
					{ "key_code": "left_shift" }
				],
				"to_if_alone": [
					{ "key_code": "japanese_eisuu" }
				]
			},			
			{
				"type": "basic",
				"from": {
					"key_code": "right_shift"
				},
				"to": [
					{ "key_code": "right_shift" }
				],
				"to_if_alone": [
					{ "key_code": "japanese_kana" }
				]
			}
		]
	}

]}

```

これは `left_shift`空打ち ▸ `japanese_eisuu`, `right_shift`空打ち ▸ `japanese_kana` の定義例ですが、例えば左optionを英数にしたい場合は `left_option` のように書き換えてください。

Macの修飾キーのキーコード一覧：


- left_command
- left_control
- left_option
- left_shift
- right_command
- right_control
- right_option
- right_shift
- fn
- command *(左右の区別をしない)*
- control *(左右の区別をしない)*
- option *(左右の区別をしない)*
- shift *(左右の区別をしない)*

https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/from/modifiers/

---

<details><summary>旧Karabinerでの対処方法</summary>

# 旧Karabinerでの対処方法

DLEが入ってしまうのはどうもIMのバグらしく、これを防ぐための設定が Karabiner に備わっておりました。
> "Hack for the bug of Mac OS X" のチェックを有効にする。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/HiromitsuTakagi?ref_src=twsrc%5Etfw">@hiromitsutakagi</a> Karabinerの「For Japanese &gt; Hack for the bug of Mac OS X」以下の設定でControl+かなを無効にしてください。</p>&mdash; Takayama Fumihiko (@tekezo) <a href="https://twitter.com/tekezo/status/582170228538220544?ref_src=twsrc%5Etfw">March 29, 2015</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


takezo さん、教えていただきありがとうございました。

</details>


# `control` + `option` 系の組み合わせでさまざまな制御文字が入力されるのを防ぐ

確認した限りだと、以下の文字の組み合わせでさまざまな不可視な文字が入力されてしまうようです。主要なもの大体全部ですね。キーによって文字の種類が異なるようです。一方でoptionを使った範囲選択は生かしておきたいため、無効化できるものは一つずつ定義を追加していくのが無難に思います。

CotEditorなどで不可視文字を表示して確認ができます。

- `escape`
- `hyphen`
- `q`
- `w`
- `e`
- `r`
- `t`
- `y`
- `u`
- `o`
- `p`
- `open_bracket`
- `close_bracket`
- `a`
- `s`
- `d`
- `g`
- `h`
- `k`
- `l`
- `backslash`
- `z`
- `x`
- `c`
- `v`
- `n`


```json:option + control + … を無効化
{
	"title": "control + option 系の不可視文字挿入を無効化",
	"rules": [
		{
			"description": "不可視文字の入力を無効化",
			"manipulators": [
				{
					"type": "basic",
					"from": {
						"key_code": "escape",
						"modifiers": { "mandatory": ["option", "control"], "optional": ["shift", "fn"] }
					},
					"to": [{
						"key_code": "escape",
						"modifiers": ["option"]
					}]
				},
				
				{
					"type": "basic",
					"from": {
						"key_code": "hyphen",
						"modifiers": { "mandatory": ["option", "control"], "optional": ["shift", "fn"] }
					},
					"to": [{
						"key_code": "hyphen",
						"modifiers": ["option"]
					}]
				},
				
				{
					"type": "basic",
					"from": {
						"key_code": "q",
						"modifiers": { "mandatory": ["option", "control"], "optional": ["shift", "fn"] }
					},
					"to": [{
						"key_code": "q",
						"modifiers": ["option"]
					}]
				}
				
			]
		}
	]
}
```

完全版

https://gist.github.com/usagimaru/84798f6937ce36eb42ea4ad510c7ab8f