特殊なキーと修飾キーでキーバインド(for Terminal)

端末(ターミナル), 仮想端末にて、(F1キー等)特殊なキーと各修飾キーと連携(Ctrl+Alt+F1等)した場合のキーバインド設定方法を紹介

#経緯
開発環境作成にて、tmuxに上記のようなキーを使用してショートカットの作成をしようとしたところ、参考サイトがなかなか見つけれず、思った以上に苦労したので備忘録も兼ねて記載。あわせて、zsh, vimの設定方法も調べたのでついでに記載

#環境
|             | 名前            | バージョン |
|:------------|:--------------- |:-------|
| OS          | OS X El Capitan |10.11.5 |
| 端末         | iTerm2          |3.0.4   |
| シェル        | zsh             |5.2     |
| 仮想端末      | tmux            |2.2     |
| 端末エミュレータ | xterm-256color  |-       |
#### 補足
端末エミュレータに関しては以下コマンド実行により確認

```zsh
$ echo $TERM
```

#設定手順概要
1. バインド対象キーが(利用端末にて)有効であるかを確認  
	- 有効であれば次手順へ
	- 無効な場合はバインド対象キーのEscシーケンスを確認
	- 端末設定にてバインド対象キーを追加し、Escシーケンスを設定する  
- 対象ソフトウェアの設定方法に合わせ、バインドキーを設定


#設定手順詳細
##対象キーが有効であるかの確認
1. 端末(iTerm2)の設定画面を表示
- 設定画面から「Profiles > Keys」を選択し、「Key Mappings」を表示
- 「Key Combination」の列を確認し、対象キーがあるかを確認

##バインド対象キーの追加し、利用可能にする
上記より、バインド対象キーがない場合の手順

1. (上記手順で表示した)「Key Mappings」の下にある「+」をクリック
2. 「Keyboard Shortcut」にてバインド対象のキーをクリック
3. 「Action:」にて、「Send Escape Sequence」を選択
4. 該当項目に「Escシーケンス」のEscを除去した文字列を入力  
※例えば 「Ctrl + F1」の場合は「[1;5P」と入力	 


###Escシーケンス記載手順
以下ルールに従い記載

1. 下記の「特殊キーのEscシーケンス一覧」より修飾キーなしの場合のEscシーケンスを確認
- 確認したEscシーケンスに修飾キーの番号を付与した文字列で記載  
※\Eはエスケープ(Esc)を表している

####特殊キーのEscシーケンス一覧
|キー|Escシーケンス|キー|Escシーケンス |
|:--- |:--------|:---------|:--------|
|F1   |\EOP     | PageUp   |\E[6~    |
|F2   |\EOQ     | PageDown |\E[5~    |
|F3   |\EOR     | Home     |\EOH     |
|F4   |\EOS     | End      |\EOF     |
|F5   |\E[15~   | Up       |\EOA     |
|F6   |\E[17~   | Down     |\EOB     |
|F7   |\E[18~   | Right    |\EOC     |
|F8   |\E[19~   | Left     |\EOD     |
|F9   |\E[20~   |          |         |
|F10  |\E[21~   |          |         |
|F11  |\E[23~   |          |         |
|F12  |\E[24~   |          |         |

---

####修飾キーの番号付与方法に関して
下記を参考に対象の修飾キーの番号を付与したEscシーケンスを記載

#####修飾キーの番号
_Shift_:2
_Alt_:3
_Shift+Alt_:4
_Ctrl_:5
_Shift+Ctrl_:6
_Alt+Ctrl_:7
_Shift+Alt+Ctrl_:8

#####修飾キーの番号付与ルール

- `OX`のときは`[1;(番号)X`
- `[(数値)~`のときは`[(数値);(番号)~`

例)Shiftの場合

|Shift + キー |Escシーケンス|Shift + キー|Escシーケンス|
|:----------- |:--------|:----------------|:--------|
|Shift + F1   |\E[1;2P  |Shift + PageUp   |\E[6;2~  |
|Shift + F2   |\E[1;2Q  |Shift + PageDown |\E[5;2~  |
|Shift + F3   |\E[1;2R  |Shift + Home     |\E[1;2H  |
|Shift + F4   |\E[1;2S  |Shift + End      |\E[1;2F  |
|Shift + F5   |\E[15;2~ |Shift + Up       |\E[1;2A  |
|Shift + F6   |\E[17;2~ |Shift + Down     |\E[1;2B  |
|Shift + F7   |\E[18;2~ |Shift + Right    |\E[1;2C  |
|Shift + F8   |\E[19;2~ |Shift + Left     |\E[1;2D  |
|Shift + F9   |\E[20;2~ |                 |         |
|Shift + F10  |\E[21;2~ |                 |         |
|Shift + F11  |\E[23;2~ |                 |         |
|Shift + F12  |\E[24;2~ |                 |         |


###参考サイト
[terminfo for xterm](http://invisible-island.net/xterm/terminfo.html)
[termcapとterminfo](http://doda.b.osdn.me/2013/01/03/termcap_andterminfo/)

###注意事項
- 上記手順にてiTerm2にバインドキーを設定した場合、「Action:」項目の表示はEsc文字は「^[」になります(e.g `^[[1;5P`等)
- iTerm2にて、Altキーを使用する場合は「Left option(⌥) key acts as」「Right option(⌥) key acts as」を「+Esc」に設定する必要があります  
※iTerm2の設定画面から「Profiles > Keys」を選択した際の画面下部のラジオボタンで変更

---

##tmuxに設定する

設定ファイル(.tmux.conf)に下記を記載。これでxtermのキーシーケンス使用可となる

```bash:.tmux.conf
set -g xterm-keys on
```

設定ルールに従い、キーバインドの定義を記載
例)

```bash:.tmux.conf
# ウィンドウ操作の再定義
unbind n; bind -n F2     next-window
unbind p; bind -n S-F2   previous-window
unbind w; bind -n M-F2   choose-window
unbind c; bind -n C-S-F2 new-window
```
「S-」がShift,「C-」がCtrl,「M-」がAltを表す
###参考
`man tmux`の「KEY BINDINGS」項目

---

##vimに設定する

###手順概要
1. vimにて各特殊キーの「<>表記」とEscシーケンスの定義を確認(`set termcap`)
- 確認したEscシーケンスの定義が環境と異なっている場合は再定義を実施(e.g.`:set <xF1>=^[[1;*P`)
- 設定ファイルにキーバインド設定を記載(e.g. `map <C-M-S-F2> :echo 'CMS F2 mapped'<CR>`)

###手順詳細
####Escシーケンスの定義確認
vimのコマンドラインから`set termcap`を実行し、「--- Terminal keys ---」の項目を確認

##### 「--- Terminal keys ---」で表示する内容
- [t_xx(termcapエントリ)]　[\<\>表記]　[Escシーケンス]で表示
- Escシーケンスの`*`は\<C-M-S-...\>等の修飾キーを使用した時、修飾キーの番号が入ることを示す
- デフォルトでは\<\>表記の\<x○○\>(\<xF1\>等)は\<○○\>(\<F1\>等)をマッピングする  
※\<○○\>を定義をした場合はマッピングしない

※私の環境では以下のように表示

```terminfo
--- Terminal keys ---
t_#2 <S-Home>    ^[[1;2H   t_k2 <F2>        ^[[12;*~  t_kr <Right>     ^[O*C
t_#4 <S-Left>    ^[[1;2D   t_k3 <F3>        ^[[13;*~  t_ku <Up>        ^[O*A
t_%1 <Help>      ^[[28;*~  t_k4 <F4>        ^[[14;*~       <Mouse>     ^[[M
t_%i <S-Right>   ^[[1;2C   t_k5 <F5>        ^[[15;*~       <xF1>       ^[O*P
t_&8 <Undo>      ^[[26;*~  t_k6 <F6>        ^[[17;*~       <xF2>       ^[O*Q
t_*7 <S-End>     ^[[1;2F   t_k7 <F7>        ^[[18;*~       <xF3>       ^[O*R
t_@7 <End>       ^[[1;*F   t_k8 <F8>        ^[[19;*~       <xF4>       ^[O*S
t_F1 <F11>       ^[[23;*~  t_k9 <F9>        ^[[20;*~       <xEnd>      ^[O*F
t_F2 <F12>       ^[[24;*~  t_k; <F10>       ^[[21;*~       <zEnd>      ^[[8;*~
t_K1 <kHome>     ^[[1;*~   t_kB <S-Tab>     ^[[Z           <xHome>     ^[O*H
t_K4 <kEnd>      ^[[4;*~   t_kD <Del>       ^[[3~          <zHome>     ^[[7;*~
t_K6 <kPlus>     ^[O*k     t_kI <Insert>    ^[[2;*~        <xUp>       ^[[1;*A
t_K7 <kMinus>    ^[O*m     t_kN <PageDown>  ^[[6;*~        <xDown>     ^[[1;*B
t_K8 <kDivide>   ^[O*o     t_kP <PageUp>    ^[[5;*~        <xLeft>     ^[[1;*D
t_K9 <kMultiply> ^[O*j     t_kb <BS>        ^?             <xRight>    ^[[1;*C
t_KA <kEnter>    ^[O*M     t_kd <Down>      ^[O*B          <kDel>      ^[[3;*~
t_KB <kPoint>    ^[O*n     t_kh <Home>      ^[[1;*H
t_k1 <F1>        ^[[11;*~  t_kl <Left>      ^[O*D
```
`^[`はEscを表しており、「^ + [」でないことに注意

####再定義とバインドの設定
確認したEscシーケンス定義が環境と異なっている場合は`:set [<>表記]=[Escシーケンス]`で再定義する
※同じである場合は再定義不要

##### 例)F1の場合
```pyson:.vimrc
:set <xF1>=^[[1;*P 
:set <F1>=^[OP
map <S-F1> [Shift+F1押下時のアクション記載]
map <C-F1> [Ctrl+F1押下時のアクション記載]
map <M-F1> [Alt+F1押下時のアクション記載]
map <C-S-F1> [Ctrl+Shift+F1押下時のアクション記載]
```
- \<xF1\>は`^[O*P`となっているため、`^[[1;*P`で(修飾キーを使用した場合の文字列で)再定義
- この定義により、\<F1\>が`^[[1;P`となってしまうので、あわせて再定義  
※`^[`はvimのINSERTモードにて、「 _Ctrl+v_ 押下」->「 _Esc_ 押下 」で記載
- mapの設定ルールに従い、キーバインドを定義


###参考
サイト：[term-Vim日本語ドキュメント](http://vim-jp.org/vimdoc-ja/term.html#xterm-shifted-keys)
※もしくはvimにて、以下を実行
```:help xterm-shifted-keys
```

---

##zshに設定する

設定ルールに従い記載。Escシーケンスはvimの以下機能を利用

- 非数字文字を文字通りに入力  
INSERTモードにて、 _Ctrl+v_ を押下し、対象のキーを押下

例)

```bash:.zshrc
# hook関数precmd実行
__call_precmds() {
  type precmd > /dev/null 2>&1 && precmd
  for __pre_func in $precmd_functions; do $__pre_func; done
}
#shift+upで親ディレクトリへ
#shift+downで戻る
__cd_up()   { builtin pushd ..; echo; __call_precmds; zle reset-prompt }
__cd_undo() { builtin popd;     echo; __call_precmds; zle reset-prompt }
zle -N __cd_up;   bindkey '^[[1;2A' __cd_up
zle -N __cd_undo; bindkey '^[[1;2B' __cd_undo
```

###注意
※修飾キーの連携とは無関係だが、zshの設定ではまった部分があったためメモ

参考サイト:[terminfo for xterm](http://invisible-island.net/xterm/terminfo.html)にある以下記載より
「Up」「Down」「Right」「Left」「Home」「End」
のEscシーケンスは上記の [特殊キーのEscシーケンス一覧](#特殊キーのEscシーケンス一覧) 
と異なるので設定に使用する場合は注意が必要(修飾キー利用時は同じ)


```sh:
# To see the difference between normal/application modes, consider this example:
#       + In normal (non-application) mode, the terminal transmits a down-arrow
#         as \E[C, which happens to echo as a down-arrow.
#       + In application mode the terminal transmits \EOC, which echoes as C.
#         That is because the \EO is the SS3 control, which says to use the
#         character from the G3 character set for the next cell.
#
```

####異なる特殊キーのEscシーケンス一覧
|キー|Escシーケンス |
|:---------|:--------|
| Home     |\E[H    |
| End      |\E[F    |
| Up       |\E[A    |
| Down     |\E[B    |
| Right    |\E[C    |
| Left     |\E[D    |