SublimeText2のキーバインドを、ちょっと突っ込んで解説

## ファイルフォーマット
ファイル形式は`JSON`で、拡張子は`.sublime-keymap`
プラットフォーム毎にファイルが作られます。

* Default (Windows).sublime-keymap
* Default (Linux).sublime-keymap
* Default (OSX).sublime-keymap

また、キーバインドの設定ファイルは、DefaultとUserの２つのファイルがありますがUserほうが優先順位が高いので、自分設定を追加する場合はUserのほうに書いていくほうがいいと思います。`Userのほうは一番初めに開くと空っぽのファイルなので[]の入力をお忘れなく。`
キーバインドの設定ファイルは、メニューの下記のところに。

* Preferences -> Key Bindings - Default
* Preferences -> Key Bindings - User

## キーバインドの構造
* `keys`
設定するキー
省略不可

* `command`
設定されたキーで、実行するコマンドのコマンド名。
省略不可

* `args`
コマンドへ渡す引数。
省略可

* `context`
何時このキーバインドを有効にするかを設定する。設定がなければ常に有効になります。
ここの設定で、特定のsyntaxでのみキーバインドを有効にする。みたいなことができるようになります。
省略可


## 基本形
一番シンプルな設定方法は、こんな感じ。
`{ "keys": ["キー"], "command": "コマンド名" }`

```js
// キーストロークが1回の時
{ "keys": [ctrl+n], "command": "new_window" },

// キーストロークが2回の時
{ "keys": [ctrl+n, ctrl+w], "command": "new_window"}
```

・Plugin作りたい人へのTips
このコマンド名はpythonのクラス名になっており、先頭と_の直後を大文字に変更したクラス名を設定します。
例えば上記の例だと NewWindowCommand or NewWindow どちらかのクラス名になっています。


## 引数がある場合
値をコマンドに渡したい時は、argsに続けて入力します。
`{ "keys": ["キー"], "command": "コマンド名", "args": {"変数名": "値"} }`

このargsに入る値はコマンドによって異なるので、
insertにのargsに{"by": "characters", "forward": false}を設定したらエラーになります。

```js
// 引数ひとつの時
{ "keys": ["enter"], "command": "insert", "args": {"characters": "\n"} },

// 引数複数の時
{ "keys": ["left"],  "command": "move", "args": {"by": "characters", "forward": false} },

// 引数配列の時
{ "keys": ["alt+o"], "command": "switch_file", "args": {"extensions": ["cpp", "c", "h"]} }
```

`command` に 'insert_snippet' を使うと、`$0`の位置にキャレットを移動させたり、'${0:$SELECTION}'で、選択文字列を取得したり、スニペット風の引数文字列を使うことができます。

```js
// `{` を入力すると '}' が自動入力され、キャレットが `{}` の間に移動
{ "keys": ["{"], "command": "insert_snippet", "args": {"contents": "{$0}"} }, 

// 文字列を選択状態で `{` を入力すると '}' が自動入力されて、選択文字列を `{}` で囲む
{ "keys": ["{"], "command": "insert_snippet", "args": {"contents": "{${0:$SELECTION}}"} }
```

・Plugin作りたい人へのTips
この時pythonのクラスは、こんな感じだと予想されます。

```python:plugin.py
class InsertCommand(sublime_plugin.TextCommand):
    def run(self, edit, characters):

class MoveCommand(sublime_plugin.TextCommand):
    def run(self, edit, characters, forward):

class SwitchFileCommand(sublime_plugin.WindowCommand):
    def run(self, extensions):
```

引数のInsertCommand, MoveCommandが持っている`edit`はテキスト編集したい時にベースクラスからもってくるものです。
ベースクラスは４つあって、その中でもテキストを編集するsublime_plugin.TextCommandを使用する時にだけ必要な引数です。
switch_fileはソースファイルに対応したヘッダファイルを開くコマンドなので、sublime_plugin.WindowCommandになっている。


## コンテキストがある場合
### コンテキストの構造
* `key`
状態を表す
例えば、selection_empty が入っていたら 文字選択をしていない場合 true を返す。
省略不可

* `operator`
演算子
例えば、equal, not_equal などがある。
省略可

* `operand`
operatorがequal, not_equalならtrue, false、1, 2などを入力。
regex_match, not_regex_match なら正規表現を入力。
省略可

* `match_all`
これをtrueにした判定のがすべてtrueにならなければマッチしたことにはならない。
デフォルトではfalse
省略可

### コンテキストの設定
コンテキストの使い方はこんな感じ。もちろんcommandによってはargsが入ります。

```js
{ "keys": ["キー"], "command": "コマンド名", "context": 
  [
    { "key": "キー", "operator": "演算子", "operand": "結果", "match_all": "true or false" }
  ]
}
```

### keyの一覧
* `num_selections`
選択している数

* `selection_empty`
何も選択していなければtrue

* `preceding_text`
キャレットより前の文字がどうなっているかを調べる

* `following_text`
キャレットより後の文字がどうなっているかを調べる

* `has_prev_field`
前に使用可能なスニペットフィールドがあればtrue

* `has_next_field`
後ろに使用可能なスニペットフィールドがあればtrue

* `auto_complete_visible`
オートコンプリートリストが表示されていればtrue

* `panel_visible`
何かpanelが表示されていればtrue

* `overlay_visible`
オーバーレイが表示されていればtrue

* `setting.XXX`
settingファイルで設定されているXXXの値が返る
値の確認はメニューの `Preferences -> Setting - Default`, `Preferences -> Setting - User` で確認

* `selector`
現在の scope が返ります
Packages -> 'scope名のフォルダ' -> 'scope名.tmLanguage'ファイルの下のほうにあるscopeNameでどんなscope名になってるか確認
こんな風に書いてあるところです

```xml
<key>scopeName</key>
<string>source.c++</string>
```


### operatorの一覧
* `equal, not_equal`
key と operand が同じだったら実行させたい = `equal`
違ったら実行させたい = `not_equal`

* `regex_match, not_regex_match`
key で選ばれる文字列が operand の正規表現とマッチするなら実行させたい = `regex_match`
違ったら実行させたい = `not_regex_match`
こっちはPythonでいう re.search. 文字列の途中マッチOK

* `regex_contains, not_regex_contains`
key で選ばれる文字列が operand の正規表現とマッチするなら実行させたい = `regex_contains`
違ったら実行させたい = `not_regex_contains`
こっちはPythonでいう re.match. 文字列の途中マッチNG

### keyの使用例
## num_selections
選択している数

```js:Default(Windows).sublime-keymap
  { "keys": ["escape"], "command": "single_selection", "context":
    [
      { "key": "num_selections", "operator": "not_equal", "operand": 1 }
    ]
  }
```

２つ選択しているので、escapeキーを押すと、single_selectionが実行される
![selection_empty](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000000.jpg)

## selection_empty
何も選択していなければtrue

```js:Default(Windows).sublime-keymap
  { "keys": ["ctrl+w"], "command": "cut", "context":
    [
      { "key": "selection_empty", "operator": "equal", "operand": true }
    ]
  }
```

何も選択されていないのでtrueになり、ctrl+wを押すと、cutが実行される
![selection_empty](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000001.jpg)

## preceding_text
キャレットより前の文字がどうなっているかを調べる

```js:Default(Windows).sublime-keymap
  { "keys": ["shift+tab"], "command": "unindent", "context":
    [
      { "key": "preceding_text", "operator": "regex_match", "operand": "^[\t ]*" }
    ]
  },
```

キャレットより前の文字が^[\t ]*にマッチしているので、shift+tabを押すとunindentが実行される
![preceding_text](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000002.jpg)

## following_text
キャレットより後の文字がどうなっているかを調べる

```js:Default(Windows).sublime-keymap
  { "keys": ["ctrl+j"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
    [
      { "key": "following_text", "operator": "regex_match", "operand": ";$" }
    ]
  }
```

キャレットより後ろの文字が ;$ にマッチしているので、ctrl+j を押すとキャレットがひとつ後ろに移動する
![following_text](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000003.jpg)

## has_prev_field
前に使用可能なスニペットフィールドがあればtrue

```js:Default(Windows).sublime-keymap
  { "keys": ["shift+tab"], "command": "prev_field", "context":
    [
      { "key": "has_prev_field", "operator": "equal", "operand": true }
    ]
  },
```

```xml:030-for-int-loop-(fori).sublime-snippet
<snippet>
  <description>For Loop</description>
    <content><![CDATA[
for( int ${1:i} = 0; $1 < ${2:count}; ${3:++}$1 ){
  ${0:/* code */}
}
]]></content>
    <tabTrigger>for</tabTrigger>
    <scope>source.c, source.objc, source.c++, source.objc++</scope>
</snippet>
```

{2:count}のスニペットフィールドを選択していて、$1のスニペットフィールドがあるのでshift+tabを押すとprev_fieldが実行される
![has_prev_field](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000004.jpg)

## has_next_field
has_prev_fieldの反対
後ろに使用可能なスニペットフィールドがあればtrue
上記の例からhas_prev_field を has_next_fieldに変えた場合、画像は${2:count}のスニペットフィールドを選択していて、${3:++}のスニペットフィールドがあるのでshift+tabを押すとprev_fieldが実行される


## auto_complete_visible
オートコンプリートリストが表示されていればtrue

```js:Default(Windows).sublime-keymap
  { "keys": ["escape"], "command": "hide_auto_complete", "context":
    [
      { "key": "auto_complete_visible", "operator": "equal", "operand": true }
    ]
  },
```

表示されているのでtrueになり、escapeキーを押すと、hide_auto_compliteが実行される
![auto_complete_visible](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000005.jpg)

## panel_visible
何かpanelが表示されていればtrue

```js:Default(Windows).sublime-keymap
  { "keys": ["escape"], "command": "hide_panel", "args": {"cancel": true},
    "context":
    [
      { "key": "panel_visible", "operator": "equal", "operand": true }
    ]
  },
```

パネルが表示されているので、hide_panelが実行される
![panel_visible](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000006.jpg)

## overlay_visible
オーバーレイが表示されていればtrue

```js:Default(Windows).sublime-keymap
  { "keys": ["escape"], "command": "hide_overlay", "context":
    [
      { "key": "overlay_visible", "operator": "equal", "operand": true }
    ]
  }
```

オーバーレイが表示されているので、hide_overlay が実行される
![overlay_visible](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/WS000007.jpg)

## setting.XXX
settingファイルで設定されているXXXの値が返る
値の確認は `Preferences -> Setting - Default`, `Preferences -> Setting - User` で確認。

```js:Preferences.sublime-settings
    "shift_tab_unindent": false
```

```js:Default(Windows).sublime-keymap
  { "keys": ["shift+tab"], "command": "unindent", "context":
    [
      { "key": "setting.shift_tab_unindent", "operator": "equal", "operand": true }
    ]
  }
```

shift_tab_unindent は falseに設定されているので、 shift_tab_unindent の時にshift+tabを押しても unindent は実行されません。

## selector
現在の scope名が返ります

```js:Default(Windows).sublime-keymap
{
  "keys": ["ctrl+s"], "command": "generate_snippet_from_raw_snippet", "context":
  [
    { "key": "selector", "operator": "equal", "operand": "source.sublimesnippetraw" }
  ]
}
```

ctrl+sを押すと、operand で選んだ scopeでのみ generate_snippet_from_raw_snippet が実行されます。

## key が panelの場合
`key` が `panel` になっている時は operand には panelの名称が入ります。

**panelの名称一覧**

* `find`
![find](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/PL00001.jpg)

* `incremental_find`
![incremental_find](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/PL00002.jpg)

* `find_in_files`
![find_in_files](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/PL00003.jpg)

* `replace`
![replace](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/PL00004.jpg)

* `console`
![console](https://raw.github.com/shibainurou/qiita/master/image/DetailSublimeKeybind/PL00005.jpg)

## Packageの開発者の人へ

```
windows: Ctrl+Alt+<alphanum>
OSX:  Option+<alphanum>
```

この組み合わせのキーは非ASCIIコードを入力できなくなる可能性があるので、使用しないほうがいいみたいです。