LeaderF の紹介

## LeaderF とは

候補一覧を雑に絞り込んでコマンドを実行できるプラグインです。
似たようなプラグインには Denite.nvim や ctrlp.vim などがあります。

LeaderF の作者は、インデントの可視化をするプラグインの [indentLine](https://github.com/Yggdroot/indentLine) も作っている [Yggdroot](https://github.com/Yggdroot) さんです。


![leaderf.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116551/e3bb376e-4ac6-8040-594d-baec2f8babc8.gif)


[README](https://github.com/Yggdroot/LeaderF) に実際に LeaderF を使っている動画が載っています。



## インストール方法

Plug を使っている場合、以下のようにインストールします。

```vim
" Linux、Mac の場合
Plug 'Yggdroot/LeaderF', { 'do': './install.sh' }

" Windows の場合
Plug 'Yggdroot/LeaderF', { 'do': '.\install.bat' }
```

LeaderF は Python を使って実装されているため、Vim で Python が有効になっている必要があります。
この記事では Python を有効にする方法は解説しませんので、他の記事を参考にしてください。



## 設定

LeaderF にはアイコン表示の機能がありますが、正しく表示されない場合、以下のように設定してください。

```vim
set ambwidth=double
```

また、アイコンを表示しないようにするには、以下のように設定してください。

```vim
let g:Lf_ShowDevIcons = 0
```



## 使い方



### 操作の流れ

1. `:LeaderF カテゴリ` で LeaderF を起動
2. 絞り込み
3. コマンドを実行

`:Leaderf buffer` とすると現在開いているバッファの一覧、`:Leaderf command` とするとExコマンドの一覧が表示されます。


`:Leaderf buffer --popup` とすることでポップアップで表示することも可能です。
また、LeaderF の各カテゴリにはたくさんのオプションがあるため、`:Leaderf カテゴリ --help` で実際に使用できるオプションを確認してみてください。



### カテゴリ一覧

LeaderF が標準で使用可能なカテゴリは以下になります。

| カテゴリ名    | 説明                                           |
|---------------|------------------------------------------------|
| file          | ファイル                                       |
| tag           | tags ファイル内の候補                          |
| function      | バッファ内の関数                               |
| mru           | 最近、開いたファイル                           |
| searchHistory | 検索履歴                                       |
| cmdHistory    | コマンド実行履歴                               |
| help          | help のタグ                                    |
| line          | カレントバッファの行                           |
| colorscheme   | カラースキーマ                                 |
| gtags         | gtags を使った候補                             |
| self          | LeaderF で使用可能なカテゴリ                   |
| bufTag        | カレントバッファ内の tags ファイル内のシンボル |
| buffer        | バッファ                                       |
| rg            | rg を使った検索                                |
| filetype      | ファイルタイプ                                 |
| command       | Exコマンド                                     |
| window        | 開かれているウィンドウ.                        |



## マッピング


LeaderF のインサートモードではマッピングが定義されており、それをもとに操作を行います。



### インサートモードのマッピング

インサートモードでのマッピングは以下のようになっています。


基本

| マッピング              | 説明                                |
| --------                | ------------------                  |
| `<C-C>`<br>`<ESC>`      | LeaderF を終了                      |
| `<C-R>`                 | Fuzzy モード <-> Regex モード       |
| `<C-F>`                 | フルパスモード <-> ファイル名モード |
| `<Tab>`                 | ノーマルモードに移行                |
| `<C-V>`<br>`<S-Insert>` | クリップボードの文字を貼り付け      |
| `<C-J>`                 | 結果ウィンドウのカーソルを下に移動  |
| `<C-K>`                 | 結果ウィンドウのカーソルを上に移動  |
| `<F5>`                  | キャッシュをクリア                  |

コマンド関連

| マッピング                | 説明                               |
| --------                  | ------------------                 |
| `<CR>`<br>`<2-LeftMouse>` | カーソル下の候補を開く             |
| `<C-X>`                   | カーソル下の候補を水平分割で開く   |
| `<C-]>`                   | カーソル下の候補を垂直分割で開く   |
| `<C-T>`                   | カーソル下の候補を新しいタブで開く |


プロンプト内のカーソル操作関連

| マッピング      | 説明                       |
| --------        | ------------------         |
| `<BS>`          | カーソル左の文字を削除     |
| `<Del>`         | カーソル右の文字を削除     |
| `<C-U>`         | カーソルより左をすべて削除 |
| `<C-W>`         | カーソルより左の単語を削除 |
| `<Home>`        | カーソル位置を先頭に移動   |
| `<End>`         | カーソル位置を末尾に移動   |
| `<Left>`        | カーソル位置を1つ左に移動  |
| `<Right>`       | カーソル位置を1つ右に移動  |
| `<Up>`/`<Down>` | プロンプトの履歴を操作     |


選択関連

| マッピング                 | 説明                   |
| --------                   | ------------------     |
| `<C-LeftMouse>`<br>`<C-S>` | カーソル下の候補を選択 |
| `<S-LeftMouse>`            | 複数候補を選択         |
| `<C-A>`                    | すべての候補を選択     |
| `<C-L>`                    | 選択している候補を解除 |

プレビュー関連

| マッピング | 説明                                                     |
| --------   | ------------------                                       |
| `<C-P>`    | カーソル下の候補をプレビュー                             |
| `<C-Up>`   | ポップアップのプレビュー内を上にスクロール (neovim のみ) |
| `<C-Down>` | ポップアップのプレビュー内を下にスクロール (neovim のみ) |


※ カテゴリが `cmdHistory` と `searchHistory` と `command` には専用のコマンドがあり、`<C-e>` で`カーソル下の候補の編集`が行なえます。

| マッピング | 説明                                                             |
| --------   | ------------------                                               |
| `<C-o>`    | カーソル下の候補を編集 (`cmdHistory/searchHistory/command` のみ) |



### ノーマルモードのマッピング

インサートモードで `<Tab>` を入力することでノーマルモードになります。
ノーマルモードの場合、通常のマッピング + LeaderF が定義しているマッピングが使用できます。

この記事では基本的なマッピングのみ紹介します。
カテゴリによって、実行できるコマンドは異なるため、詳しくはノーマルモードで `<F1>` を押し、ヘルプを見てください。


基本的なマッピング

| マッピング                       | 説明                   |
| -------                          | -------                |
| `q`                              | LeaderF を終了         |
| `<CR>`<br>`o`<br>`<2-LeftMouse>` | カーソル下の候補を開く |
| `i`<br>`<Tab>`                            | インサートモードに移行 |
| `<F1>`                           | ヘルプの表示           |




## 検索モードについて



### Regex モード

正規表現で検索できます。



### Fuzzy モード

あいまい検索ができます。



#### フルパスモード と ファイル名モード (Fuzzy モードのみ)


カテゴリによっては、フルパスモードとファイル名モードを持っているものがあります。

フルパスモードを使うことで

* `file.txt` というファイル名で、`dir1` というディレクトリに入っているものを検索できます。

といったことができるようになります。


以下のカテゴリにはフルパスモードが使えます。

* buffer
* file
* mru
* window


フルパスモードを使うには2つの方法があります。

* インサートモードで `<C-F>` を入力し、フルパスモードを有効にする。
* プロンプトで `ファイル名;パス` のように入力する。
  * `abc;def` と入力した場合、ファイル名に `abc` が入り、パスに `def` が入る候補が絞り込まれます。


### and 検索 (Fuzzy モードのみ)

プロンプトへの入力をスペース (` `) でキーワードを区切ることで、and 検索することができます。



## カスタマイズ方法

ここからは LeaderF をカスタマイズする方法について書きます。



### インサートモードでのマッピング

インサートモードのマッピングは `g:Lf_CommandMap` でカスタマイズすることが可能です。

例えば、履歴の操作の `<Up>` と `<Down>` を `<C-p>` と `<C-n>` にしたい場合、以下のように設定します。

```vim
let g:Lf_CommandMap = {
\   '<Up>':     ['<C-p>'],
\   '<Down>':   ['<C-n>'],
\   '<C-p>':    ['<C-q>'],
\}
```

また、デフォルトの `<C-p>` が`カーソル下の候補をプレビュー`となっているので、`<C-q>` にマッピングしています。

定義方法が `map` コマンドと逆なので、注意してください。(`map` の場合、`nnoremap <C-p> <Up>` のようになります。)



### `:Leaderf file` と `:Leaderf rg` での起点となるディレクトリの設定

`g:Lf_WorkingDirectoryMode` で設定できます

指定できる値は以下の5つになります。

|値|説明|
|---|---|
|c | カレントディレクトリから検索|
|a | カレントディレクトリよりも上に `g:Lf_RootMakers` があれば、そのディレクトリから検索。<br>なければ、`c` と同じ|
|A | カレントバッファのディレクトリよりも上に `g:Lf_RootMakers` があれば、そのディレクトリから検索。<br>なければ、`c` と同じ|
|f | カレントバッファのディレクトリ|
|F | カレントバッファのディレクトリがカレントディレクトリの下にない場合、`f` と同じ|


`g:Lf_RootMakers` はデフォルトで `['.git', '.hg', '.svn']` が設定されています。

`F` の挙動について、カレントバッファのディレクトリが `C:/tmp/dir1/sub` で、カレントディレクトリが `C:/tmp2` だった場合、カレントバッファのディレクトリ (`C:/tmp/dir1/sub`) から検索されます。



## 設定例

私の設定を紹介します。

<details><summary>設定例</summary><div>

```vim
nnoremap [Leaderf] <Nop>
nmap     <Space>f [Leaderf]

" ! をつけるとノーマルモードから始まる
nnoremap          [Leaderf]g        :<C-u>Leaderf! rg --match-path -e ""<Left>
nnoremap <silent> 0                 :<C-u>Leaderf  rg --next<CR>
nnoremap <silent> 9                 :<C-u>Leaderf  rg --previous<CR>
nnoremap <silent> [Leaderf]r        :<C-u>Leaderf! rg --recall<CR>
nnoremap <silent> [Leaderf];        :<C-u>Leaderf  cmdHistory<CR>
nnoremap <silent> [Leaderf]f        :<C-u>Leaderf  file<CR>
nnoremap <silent> [Leaderf]h        :<C-u>Leaderf  help<CR>
nnoremap <silent> [Leaderf]j        :<C-u>Leaderf  buffer<CR>
nnoremap <silent> [Leaderf]k        :<C-u>Leaderf  mru --nowrap<CR>
nnoremap <silent> [Leaderf]t        :<C-u>Leaderf  filetype<CR>
nnoremap <silent> [Leaderf]w        :<C-u>Leaderf  window<CR>
nnoremap <silent> [Leaderf]l        :<C-u>Leaderf  line<CR>
nnoremap <silent> [Leaderf]s        :<C-u>Leaderf  bufTag<CR>

nnoremap <silent> <Space><Space>    :<C-u>Leaderf command --run-immediately<CR>
nnoremap <silent> <Space>ml         :<C-u>Leaderf filer ~/memo<CR>

" Reference
nnoremap <silent> gr                :<C-u><C-r>=printf('Leaderf! rg --match-path -e "%s" -w -F', expand('<cword>'))<CR><CR>
vnoremap <silent> gr                :<C-u><C-r>=printf('Leaderf! rg --match-path -e %s -w -F', leaderf#Rg#visual())<CR><CR>


function! s:leaderf_settings() abort
    silent! setlocal signcolumn=no
    silent! setlocal scrolloff=0
endfunction


augroup MyLeaderf
    autocmd!
    autocmd Filetype leaderf call <SID>leaderf_settings()
augroup END


" デフォルト
let g:Lf_DefaultMode = 'NameOnly'

" カーソルの点滅をなくす
let g:Lf_CursorBlink = 0

" ステータスラインのカラースキーム
if g:colors_name ==# 'one'
    let g:Lf_StlColorscheme = 'one'
else
    let g:Lf_StlColorscheme = 'default'
endif


" 検索に使う外部ツール
let g:Lf_DefaultExternalTool = 'rg'

" カレントバッファの名前を結果に表示しない (Leaderf file のみに有効)
let g:Lf_IgnoreCurrentBufferName = 1

let g:Lf_MruMaxFiles = 1000

" ヘルプを非表示
let g:Lf_HideHelp = 1

" カレントディレクトリよりも上に `g:Lf_RootMakers` があれば、そのディレクトリから検索
" なければ、カレントディレクトリから検索
let g:Lf_WorkingDirectoryMode = 'a'

" 履歴を3000
let g:Lf_HistoryNumber = 3000

" 表示を上下逆にする
let g:Lf_ReverseOrder = 1

" 結果ウィンドウの高さ
let g:Lf_WindowHeight = 0.4

" LeaderF ウィンドウを下に表示
let g:Lf_WindowPosition = 'bottom'

" history で除外する
let g:Lf_HistoryExclude = {
\   'cmd':  ['^wq?!?$', '^qa?!?$', '^.\s*$', '^\d+$'],
\   'search':  []
\}

" file カテゴリで除外するパターン
let g:Lf_WildIgnore = {
\   'dir': ['**/.mypy_cache/*', 'node_modules/*', '*.pyc'],
\   'file': ['tags']
\}

" 詳しくは Leaderf rg --help を見て
" --glob は .gitignore のような書き方で、複数かける
let g:Lf_RgConfig = [
\   '--smart-case',
\   '--glob=!*/.mypy_cache/*',
\   '--glob=!.node_modules/*',
\   '--glob=!tags*',
\]

" ステータスラインの区切り文字をなくす
let g:Lf_StlSeparator = { 'left': '', 'right': '' }

" ノーマルモードのカスタマイズ
let g:Lf_NormalMap = get(g:, 'Lf_NormalMap', {})


" <C-r> : 検索切り替え: fuzzy / regex 
" <C-f> : 検索切り替え: fullpath / name only
" <Tab> : モード切替: Normal / Insert
" <C-v> : クリップボードから貼り付け
" <C-u> : <C-u>
" <C-j> <C-k> : 上下移動
" <Up> <Down> : 履歴操作
" <CR>  : 開く
" <C-x> : horizonal split で開く
" <C-]> : vertical split で開く
" <C-t> : タブ で開く
" <F5>  : キャッシュ更新
" <C-s> : 複数ファイル選択
" <C-a> : 全ファイル選択
" <C-l> : 全ファイル選択の解除
" <BS>, <C-h>  : 前の文字削除
" <Del> : 後ろの文字削除
" <Home> : 先頭へカーソル移動
" <End>  : 末尾にカーソル移動
" <Left> : 左にカーソル移動
" <Right> : 右にカーソル移動
" <C-p> : プレビュー表示

" cmdHistory/searchHistory/command
" <C-o> : 編集

" ~/vimfiles/plugged/LeaderF/autoload/leaderf/python/leaderf/cli.py を見てね
" key <- value (value を key として解釈させる)
let g:Lf_CommandMap = {
\   '<Up>':     ['<C-p>'],
\   '<Down>':   ['<C-n>'],
\   '<C-v>':    ['<C-o>'],
\   '<C-x>':    ['<C-s>'],
\   '<C-]>':    ['<C-v>'],
\   '<C-p>':    ['<C-q>'],
\   '<C-o>':    ['<C-e>'],
\   '<Del>':    ['<C-d>'],
\   '<C-s>':    ['<C-x>'],
\}


let g:Lf_StlPalette = {
\   'stlName': {
\       'gui': 'bold',
\       'font': 'NONE',
\       'guifg': '#2F5C00',
\       'guibg': '#baf2a3',
\       'cterm': 'bold',
\       'ctermfg': 'NONE',
\       'ctermbg': 'NONE'
\   },
\   'stlCategory': {
\       'guifg': '#4d4d4d',
\       'guibg': '#c7dac3',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '239',
\       'ctermbg': '151',
\       'cterm': 'NONE',
\   },
\   'stlNameOnlyMode': {
\       'guifg': '#4d4d4d',
\       'guibg': '#c9d473',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '239',
\       'ctermbg': '186',
\       'cterm': 'NONE',
\   },
\   'stlFullPathMode': {
\       'guifg': '#4d4d4d',
\       'guibg': '#dbe8cf',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '239',
\       'ctermbg': '187',
\       'cterm': 'NONE',
\   },
\   'stlFuzzyMode': {
\       'guifg': '#4d4d4d',
\       'guibg': '#dbe8cf',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '239',
\       'ctermbg': '187',
\       'cterm': 'NONE',
\   },
\   'stlRegexMode': {
\       'guifg': '#4d4d4d',
\       'guibg': '#acbf97',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '239',
\       'ctermbg': '144',
\       'cterm': 'NONE',
\   },
\   'stlCwd': {
\       'guifg': '#595959',
\       'guibg': '#e9f7e9',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '240',
\       'ctermbg': '195',
\       'cterm': 'NONE',
\   },
\   'stlLineInfo': {
\       'guifg': '#595959',
\       'guibg': '#e9f7e9',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '240',
\       'ctermbg': '195',
\       'cterm': 'NONE',
\   },
\   'stlTotal': {
\       'guifg': '#4d4d4d',
\       'guibg': '#c7dac3',
\       'gui': 'NONE',
\       'font': 'NONE',
\       'ctermfg': '239',
\       'ctermbg': '151',
\       'cterm': 'NONE',
\   }
\ }


if g:colors_name =~# '^solarized8'
    let g:Lf_StlPalette.stlBlank = {
    \   'gui': 'NONE',
    \   'font': 'NONE',
    \   'guifg': '#073642',
    \   'guibg': '#eee8d5',
    \   'cterm': 'NONE',
    \   'ctermfg': 'NONE',
    \   'ctermbg': 'NONE'
    \}
elseif g:colors_name ==# 'one'
    let g:Lf_StlPalette.stlBlank = {
    \   'gui': 'NONE',
    \   'font': 'NONE',
    \   'guifg': '#494b53',
    \   'guibg': '#e1e1e1',
    \   'cterm': 'NONE',
    \   'ctermfg': 'NONE',
    \   'ctermbg': 'NONE'
    \}
endif


let g:Lf_PreviewResult = {
\   'File': 0,
\   'Buffer': 0,
\   'Mru': 0,
\   'Tag': 0,
\   'BufTag': 0,
\   'Function': 0,
\   'Line': 1,
\   'Colorscheme': 1,
\   'Rg': 0,
\   'Gtags': 0
\}

" アイコンの追加
let g:Lf_DevIconsExactSymbols = {
\   'vimrc':  '',
\   'gvimrc': '',
\   'tags':   '󿧸',
\}

let g:Lf_DevIconsExtensionSymbols = {
\   'lock': '󿫺',
\   'vue':  ''
\}
```

</div>
</details>

## Tips



### ノーマルモードから開始する

`!` をつける

```vim
:Leaderf! rg -e "function"
```



### :Leaderf command で引数なしのコマンドだったらすぐ実行する

`--run-immediately` を使います


```vim
:Leaderf command --run-immediately
```



### :Leaderf rg でファイルパスも絞り込み対象に入れる

`--match-path` を使います

```vim
:Leaderf rg --match-path -e "function"
```



### :Leaderf rg で単語検索する

`-w` を使います


```vim
:Leaderf rg -e "pattern" -w
```


`hello pattern world` はマッチするけど、`hellopatternworld` はマッチしなくなります。



### :Leaderf rg でリテラル検索する

`-F` を使います


```vim
:Leaderf rg -e "exists('*" -F
```



## おわりに

以上が LeaderF の紹介になります。LeaderF を使うことで、さまざまなものを同じインターフェースで扱えるため、とても便利になると思います。
この記事をきっかけとして使っていただけると嬉しいです！

また、LeaderF はカテゴリを自作するための仕組みも用意されているため、自分で拡張することもできます。VimScript と Python を使って拡張することが可能です。
最後に、宣伝になりますが [LeaderF-filer](https://github.com/tamago324/LeaderF-filer) というファイラーを作っていますので、よろしければ使ってみてください。