コンソールで動くエディタを作る(13日目) キーバインドについて

本稿は[自作エディタをつくる Advent Calendar 2016](http://qiita.com/advent-calendar/2016/make_editor)の13日目です、レポジトリは[こちら](https://github.com/tinyco/tiny_code_editor)

# 文字を挿入後にカーソルを1つ後ろにずらす

12日目の積み残しです。ずらしました。

# カーソル位置をチェックする

現状の動作だと、カーソルをマイナス3文字目とかに動かせてしまうので、簡単にセグフォが起こせてしまいます。
12日目の`保持しているカーソルの位置と、実際のカーソル位置がズレたほうが使いやすそう`問題は、後で考えることにして
いったんセグフォが起きない位置にしかカーソルを動かせないようにします。

ちょっと困ったのは`unsigned int`の挙動です。

`unsigned int x = 0; x--;`は、コンパイルが通るし、これだけではランタイムエラーにならないです。

`unsigned int x = 0; x--;if(x<0) {x=0;}`は、コンパイルで怒られます。

エラー内容は`comparison of unsigned expression < 0 is always false` 0未満にはならないよ、ですかね。

ちょっと著者が`unsigned`に求めていたものと違うのですが、言われるとそうだよねって感じです。

今回はカーソル位置はたまたま1始まりなので`x<1`にしましたが、設計時点で色々考えればよかった気がします。

教訓として、unsignedを使う時はエラーケースをどこにしまうか考えてからにしよう！って思いました。

余談として、charをunsignedにするとprintfのときは便利ですが、signedのほうがstrcpy等が使いやすいので、指定してあればどちらでも良いのかもしれません。

全体的に、ちょっと型に理解が浅かったですね。

話を元に戻して、カーソル位置について

- 行の先頭より左に行けなくする
- ファイルの先頭より上に行けなくする

は自明ですが、

- 行の末尾より右に行けなくする

は12日目の`保持しているカーソルの位置と、実際のカーソル位置がズレたほうが使いやすそう`問題があります

- ファイルの末尾より下に行けなくする

は、その通りでもいいし、空行を追加して下に行けるようにしてほしい派もいる気がします。

けっこうカーソル移動は奥が深いですね。好みの問題の気がするので、とりあえず実装が楽な方に倒します。

これでセグフォは起きなくなりました。

# 制御文字を入力する(十字キー)

9日目の`十字キーの謎`より、十字キー文字コードは分かっているため(mac+bash環境調べ)

```
up    = 0x1B,0x5B,0x41
down  = 0x1B,0x5B,0x42
right = 0x1B,0x5B,0x43
left  = 0x1B,0x5B,0x44
```

こんな感じで0x1B、0x5Bを特殊な状態として扱うことで、十字キーを入力できるようにしました。

```snipet.c
if(key[0] == 0x1B && flag == NOT_CTRL) {
  flag = ALLOW_1;
} else if (key[0] == 0x5B && flag == ALLOW_1) {
  flag = ALLOW_2;
} else if (flag == ALLOW_2) {
  if(key[0] == 0x41) cmd.command_key = UP;
  if(key[0] == 0x42) cmd.command_key = DOWN;
  if(key[0] == 0x43) cmd.command_key = RIGHT;
  if(key[0] == 0x44) cmd.command_key = LEFT;
  flag = NOT_CTRL;
} else {
  flag = NOT_CTRL;
  cmd.command_key = INSERT;
}
```

無事に動作したので、u/d/l/rキーの割り当ては無くします。
終了は、後述するCtrl+Qにしました。

# 制御文字を入力する(それ以外のキー)

ついでに、削除キー等の文字コードもprintfで調べます。
コメントは、どういう操作を割り当てたいかの希望です。

```わかったキー
Backspace = 0x7F //文字削除
Esc = 0x1B //終了
Alt + LEFT = 0x1B,0x62 //行先頭へ
Alt + RIGHT = 0x1B,0x66 //行末尾へ
Alt + S = 0xC3 // 保存
Alt + Q = 0xC5 // 終了
Ctrl + S = 0x13 // 保存
Ctrl + Q = 0x11 // 終了
Shift + LEFT = 0x1B,0x5B,0x31,0x3B,0x32,0x44 //範囲選択
Shift + RIGHT = 0x1B,0x5B,0x31,0x3B,0x32,0x44 //範囲選択
```

```わからないキー
Shift + UP / DOWN = 判別不能(Shiftを押さない時と同じ文字コードになる)
ALT + UP / DOWN = 判別不能(Shiftを押さない時と同じ文字コードになる)
Ctrl + 十字キー = 測定不能(Macのスクリーン移動に操作を取られる)
Cmd + 各種キー = 測定不能(Macの固有機能に操作を取られる)
```

後半の測定不能は、3日目の懸念事項だったものですが、Ctrl+十字キーも無理だとは・・・。
ちょっとショックです。

Escキーって、0x1Bなんですね。

後続に文字が来るかどうかは、現状の実装(getchar)だとわからないので、
十字キー(例：上キーは0x1B,0x5B,0x41)の先頭の0x1Bか、Escキーかはその時点で判断できません。
今の所困らないですが、Escキー単押しに機能を割り振るのは難しそうです。

# 先人(vi/emacs)の知恵を知る

そもそも、Alt+SはMacだと`ß`を打つためのキーバインドなので、奪って機能を割り振ってしまうと`ß`を打ちたい人が困ります。
じゃあCmdキーは・・・、となるとMac固有の機能と衝突するため割り振れません。
となると、`Ctrl+何か`、`Esc押した後に何か`、あたりが候補に挙がって・・・(๑•̀ㅁ•́๑)✧ ハッ！

まるで vi / emacs のキーバインドですね。(nanoもですね。)
あの変則的なキーバインドに不満を感じたのがエディタを作る動機の1つだったのですが、
どうやら似たキーバインドにたどり着きそうです。なるほど、こういう理由から、キーバインドが生まれたのですかね。

自分のエディタに対して、先人の知恵をなぞるか、新たな形を模索するか考えた方が良さそうです。
ctrl+十字キーさえ打てれば、ctrlで揃えるのもありかなと思ったのですが、ちょっと考えます。

# 今日のまとめ

- カーソル位置をチェックするようにしました
- vi/emacs/nanoの工夫を垣間見ました

長くなってきたので、`文字を削除する`は明日に回します。

> vi/emacsの変則的なキーバインドに不満を感じたのがエディタを作る動機の1つだったのですが、
> どうやら似たキーバインドにたどり着きそうです

釈迦の掌の上で回ってる気分ですね・・・。なるほどなぁ。

前回分のコメント(自動テストに関して)ありがとうございます。
まだ参照先を読みきれてないですが、単体テストはそろそろ入れないとつらいところ。