lazy-motion つくった

2017.2.17 追記 ここから

今パッケージはメンテしません。やっぱりこれも使わなくなりました。

これは見つけるのに失敗する、失敗率が関わっている気がする。
そもそも脳が疲れない、というのが重要なんですが。
キーストロークが多くても、確実に見つかるツールの方が疲れない。
失敗してやり直すのは、イラッとする、脳のコンテキストスイッチが発生する、結果疲れる。
自分では今は https://atom.io/packages/narrow を使ってます。
`narrow:scan` と `narrow:scan-by-current-word` がファイル内検索だとツールの目的は重なっている(動作は全然ちがうけども)

2017.2.17 追記 ここまで

# [lazy-motion](https://atom.io/packages/lazy-motion)

**曖昧(fuzzy)**、**テキトー(lazy)** なサーチで素早くカーソル位置を移動できる。

基本的に自分の Package はリリース直後は自画自賛してますが、今回のコレは自画自賛度が別格。
超便利。
自画自賛が溢れ漏れて、ここに書いちゃうレベル。

![gif](https://raw.githubusercontent.com/t9md/t9md/3379ed41ea6fd3725245f5d37b3bb36f7e9b0683/img/atom-lazy-motion.gif)

# 機能

* fuzzy サーチで、バッファ内の単語を検索し移動できる。fuzzy マッチ には [fuzzaldrin](https://github.com/atom/fuzzaldrin) を使用.
* `current / total` マッチカウントをインプットパネルや、ホバーインジケーターに表示
* マッチする毎にマッチ位置にスクロール移動(インクリメンタルサーチ的な移動)
* 決定しない限り、内部的なカーソル位置を変更しない([cursor-history](https://atom.io/packages/cursor-history) の様な package との共存に重要).
* ハイライトの色を Top(青) と Bottom(赤) で区別し色で分かるように.
* 元のカーソル位置のハイライト、カレントマッチのハイライト
* どれにもマッチしない時画面フラッシュ
* 候補がひとつの時は自動的に着地(設定可能)

# なにが良いか？

Atom を使っている人は、autocomplete-plus を使っている人は多いだろう。候補が表示された後は、曖昧な入力で候補を絞り込める。
Command Palette のコマンド絞込みもあいまい検索だ。これは [fuzzaldrin](https://github.com/atom/fuzzaldrin) を使っている。
この快適なあいまい検索で、バッファ内を移動できるようになる。

コード中で "あそこに移動したい" と思う時、その目的地は大抵は 'i' とか 'var' とかのユニークさの無い identifier ではなく、例えば、 `@editor.decorateMarker` とか、より具体性の高い、ユニークさの強い identifier なはずだ。

例えば 200 行を超える CoffeeScript のファイルをエディットしていたとする。
そして `@container?.destroy()` がファイル内のどこかにあると分かっていて、そこに移動したいと思った。
この状況で、lazy-motion では以下のように目的の場所にたどり着くことが出来る。

1. `lazy-motion:forward` をキーマップから呼び出し.
2. インプットパネルに `c?d` と入力.
3. `core:confirm` で着地 (`core:cancel` でキャンセル).

## 他の例
* `cancel()` に `ca)` で移動.
* `@flashingDecoration?.getMarker().destroy()` の位置に `gemade`(**ge** t **M** arker. **de** stroy()) で移動.

このように、怠けた入力、あいまいな入力で目的の位置にたどり着くことが出来る。

# コマンド

* `lazy-motion:forward`: 前方検索.
* `lazy-motion:backward`: 後方検索.
* `core:confirm`: 決定.
* `core:cancel`:  キャンセル.

*NOTE: サーチは常に、end-to-top, top-to-end でラップする。wrap scan.**

# 設定

* `autoLand`: 他に候補がない時に自動着地
* `minimumInputLength`: 入力がこの長さを超えたらサーチ開始
* `wordRegExp`: 候補の wordlist を作るのに使われる正規表現. 何を単語と見做すか？
* `showHoverIndicator`: ホバーインジケーターをサーチ中に出すか？

# キーマップ

デフォルト・キーマップは無し。
自分で `keymap.cson` に設定する必要がある。

```coffeescript
'atom-text-editor':
  'ctrl-s':     'lazy-motion:forward'
  'ctrl-cmd-r': 'lazy-motion:backward'

'atom-text-editor.lazy-motion':
  ']': 'lazy-motion:forward'
  '[': 'lazy-motion:backward'
```

* Emacs ライク

```coffeescript
'atom-text-editor':
  'ctrl-s': 'lazy-motion:forward'
  'ctrl-r': 'lazy-motion:backward'

'.platform-darwin atom-text-editor.lazy-motion':
  'ctrl-s': 'lazy-motion:forward'
  'ctrl-r': 'lazy-motion:backward'
  'ctrl-g': 'core:cancel'
```

* 私の設定

```coffeescript
'atom-text-editor.vim-mode.command-mode':
  's': 'lazy-motion:forward'

'.platform-darwin atom-text-editor.lazy-motion[mini]':
  ']':      'lazy-motion:forward'
  '[':      'lazy-motion:backward'
  ';':      'core:confirm'
  'ctrl-g': 'core:cancel'
```

# 制限事項

大きなサイズのバッファでは遅い。

候補を前もって生成しておく方式でも実装してみた(observerTextEditors, onWillChange, onDidChange とか) が、
大量の marker がセットされたバッファをエディットすると猛烈に遅くなる(マーカー位置を調整する必要があるため)。
なので、lazy-motion 開始時に候補を生成し(内部的に ハイライト等で marker を使う)、終わったら marker を削除、という現在の方式がシンプルだし、遅くならないしで、良い。
他に方法がないし。

# 言語毎の `wordRegExp` 設定

言語ごとに、`wordRegExp` の設定を変えることも可能。

詳細は [Scoped Settings, Scopes and Scope Descriptors](https://atom.io/docs/latest/behind-atom-scoped-settings-scopes-and-scope-descriptors) や [API/Config](https://atom.io/docs/api/latest/Config) を参照。

```coffeescript:config.cson
"*": # This is global scope. Used as default.
  # <snip>
  "lazy-motion":
    wordRegExp: 'xxxx'
  # <snip>
".go.source": # This is Go specific,
  "lazy-motion":
    wordRegExp: 'xxxx'
```

# TODO
- [x] Restore fold if canceled.
- [x] Support language specific `wordRegExp` configuration.
- [x] Show hover indicator to inform `current / total`.
- [x] `AutoLand` if there is no other candidate.

# なぜ私には *label jump* 方式が合わなかったのか？その考察

これまでに私は [vim-smalls](https://github.com/t9md/vim-smalls/blob/master/README-JP.md) や、その [Atom への移植](https://github.com/t9md/atom-smalls) をリリースして来た。
また、[jumpy](https://github.com/t9md/jumpy) や [vim-easymotion](https://github.com/t9md/vim-easymotion) を smalls 開発の練習として、改変(hack) したりもした。
[smalls をつくった](http://qiita.com/t9md/items/bca96d45af1a5244b5d1)のは一週間前だ。
しかしやはり私には合わなかった。
Atom では across Pane で検索、移動できるから vim-smalls よりもさらに便利なはずなのに。

私にはこれらのプラグインのラベルジャンプ システム自体が合わないのだ。

理由はシンプルだ。

ラベルジャンプ は私に、正確にラベルの文字をタイプするように強制する。
これにより貴重な集中状態(Zone, Flow)が切れてしまう。
ラベルジャンプは、最小のキータイプで、目的の位置のたどり着けるかもしれないが、それは多分にデモンストレーションの色合いが強いのではないか。

少なくも私の脳には合わない、現実のコーディングの場面でつかうには、**ラベルジャンプが強いる脳のコンテキストスイッチの代償が大きすぎ**、日常的に使うに至らなかった。

そういう意味では vim-easymotion や smalls よりも、Vim の標準の `f` や、clever-f の方が優れている。
脳のコンテキストスイッチが無いからだ。

別の観点から言うと、**寝ぼけてても、ぼーとしてても、目的の位置にたどり着けるか?** ということだ。
ラベルジャンプによる移動は、ちゃんとラベルを目視確認し、ラベル文字を認識し、その文字を入力する、という神経を使う作業だ。
一方、通常のカーソル移動(Vim の jkhl や Emacs の C-f,b,n,p) や サーチ等はボーっとしてても入力して目的地に辿り着ける。ラベルを確認する、という確認ステップが入らないから。

なので、集中している時にはラベルジャンプをしようという気が起こらないのだ。、


# これまでの経緯

1. vim-easymotion 知る。4年前ぐらいか。驚いた。試した。馴染まず。
2. 検索と組み合わせたらもう少し、楽に移動できるのでは？と思い vim-smalls 作る。
独自の excursion モードも作り、色々機能つけたが、結局使わず.
3. Atom で smalls つくってみた。ラベルジャンプが Pane またぎでやれるし、ラベルの見た目も綺麗だし。表示するラベルの数も、候補の数に応じて、1文字ラベルと、2文字ラベルを使い分けけたり、候補が最終段階じゃない時はラベルの色分けて、まだ次もラベルが出るよという事が色で分かるようにしたり、２文字ラベルの時に一文字目を入力済みであることが分かるように打ち消し線を入れたり。。。とここまでやってかなりラベルジャンプ package としてはイケているが、、それでも、やっぱり使わなそう。。。。
4. では incremental-search が本命だな。。Emacs の時はそれでガシガシ移動してたしな。
ということで Atom の isearch をつくる。まあまあ、良い。やっぱ勝手にスクロール移動するのがいいな。。ラベルジャンプは基本、スクロールなしだしな。。でもまてよ、これ、あいまい検索で incremental-search できたらいいのでは？※ ここで lazy-motion に開発興味が移ったので isearch はちょっと中途半端になってる。
5. lazy-motion つくる。できた。すごい。便利。あいまい検索。rapid-motion として release したが、ネーミングが変えたくなり、unpublish して lazy-motion に rename し re-publish。
6. そして今この Qiita の記事を書いている。。ラベルジャンプが合わないのは思い知ったし、lazy-motion は馴染めそうなので、これでやっとカーソル移動方式の模索から卒業出来そうな気がする。