🐼

ddu.vimの基本設定概観

2023/08/17に公開
1

この記事は何ではあって何ではないか

  • ⭕: 私のddu.vimに対する基本的な理解を書く
    • あくまでも「私なりの理解」の記述に重きを置いています
    • 基本的な構造について書いています
  • ❌: 設計思想には言及しない
    • "思想"を語るべき立場にないと考えています
    • あくまでも「私からの見え方」を記しています
  • ❌: 網羅しない
    • ddu.vimのすべての使い方の説明はしません
    • 解説している内容はddu.vim全体のごく一部です
    • 列挙も「概観を理解するうえで重要なもの」だけを挙げています
  • ❌: ヘルプ・ガイドラインではない
    • 本体付属のヘルプドキュメントや、リファレンスを代替するものではありません
  • ❗: 断言する
    • 実直に書くとすべての文が「思われます」「ようです」といった推測・曖昧さを残す表現を伴ってしまいます
    • あえてこれらを省略して断言する表現に改め、読みやすくしています

はじめに: ddu.vimとは

ddu.vimは、Vim/Neovimその他テキストエディタによくみられるFuzzy Finderを代替するリストビュープラグインです。[1]

ddu.vimを構成する基本要素

ddu.vimは、それ本体だけでは何もできません。
これに次の3つの要素を組み合わせて、様々なリストの表示・選択・アクションを実現できます。

  • Source
    • Item のリストを生成する
  • Kind
    • Item のリストを受け取ってアクションを実行する関数群
  • UI
    • Itemのリストを表示する
    • ユーザーのItemの選択を受け付ける
    • ユーザーのアクション名の指定を受け付ける

また、3種類のFilterを組み合わせて、Fuzzy Finderのようなリストの抽出・変換・整列を実現できます。

  • Filter
    • Matcher: ユーザーのクエリ入力を受け取って、Itemのリストから抽出する
    • Converter: ユーザーのクエリ入力を受け取って、Itemのリストを変換する [2]
    • Sorter: ユーザーのクエリ入力を受け取って、Itemのリストを整列する [2:1]
  • UI
    • Filterに指定するためのユーザーのクエリ入力を受け取る

ddu.vim本体は、これらをつなぐ役目を負っています。

  • SourceからItemのリストを受け取る
  • ItemのリストをFilterに通す
  • ItemのリストをUIに渡す
  • UIから選択されたItemとアクションのリクエストを受け取る
  • Kindの、UIから指定されたアクションを呼び出す

ddu.vimの使用方法

ここからはddu.vimを設定して、実際の使用方法を概説します。

インストール

まず、次のセットをインストールします。

さらに、お好みのUI, Source, Kindをインストールします。
また、Itemをクエリの入力で絞り込みたい場合は、Matcherをインストールします。

以下の節では、試用する上で分かりやすい以下4つのプラグインセットを前提として説明します。

使用時の流れ

使う際には、次のようなフローをたどります。

# 目的 実際の操作
1 ddu.vimで一覧を表示する :call ddu#start({オプション})
2 Input用のバッファを表示する :call ddu#ui#do_action("openFilterWindow")
3 Itemを絞り込むワードを入力する i キーワードを入力 <ESC>
4 一覧のバッファに移動する :call ddu#ui#do_action("leaveFilterWindow")
5 Itemを選ぶ jk で移動する
6 Kindのアクションを呼び出す :call ddu#ui#do_action("itemAction", {"name": <アクション名>})

ここで呼び出している各処理をそれぞれ解説します。

ddu#start({オプション})

ddu.vimのSource取得、UI表示といったすべての処理の起点です。
オプションの例は後述します。

ddu#ui#do_action()

ddu.vimのUIにアクションの呼び出しを要求します。
今回は例としてUIにddu-ui-ffを使用しているため、呼び出しているアクションはddu-ui-ffで定義されたものです。

  • :help ddu-ui-ff-action-openFilterWindow
  • :help ddu-ui-ff-action-leaveFilterWindow
  • :help ddu-ui-ff-action-itemAction

itemAction が少し複雑ですが、Itemに対応するKindのアクションのうち、nameで指定された名前のものを呼び出すようddu.vimに要求します。

オプションの指定方法

ddu.vimのオプションには4つのレイヤーがあります。

  • user: ddu#start({dict})に直接渡すオプション
  • local: 独自に名前を付けたオプションセット。ddu#custom#patch_local({name}, {dict})を使って設定する
  • global: 全体に共通する設定。ddu#custom#patch_global({dict})によって設定される
  • default: デフォルト値

ddu.vimはこれらをuser→local→global→defaultという優先順位でマージして使います。
そのため、globalで指定したオプションは、localの同じ名前のオプションで上書きされ、さらにuserのオプションで上書きされます。
また、使用するlocalオプションの名前(nameの値)自体も、user, globalでの指定に従います。

たとえば、

call ddu#custom#patch_global({
    \     "option1": "foo"      " !
    \     "option2": "bar"
    \ })

call ddu#custom#patch_local("local-name", {
    \     "option2": "baz",     " !
    \     "option3": "qux",     " !
    \     "option4": "quux",
    \ })

call ddu#start({
    \     name = "local-name",  " local-nameと名付けたlocalオプションを使用する
    \     option4 = "corge",    " !
    \     option5 = "grault",   " !
    \ })

このように呼び出すと、次のようにオプションがマージされます。

  • option1: "foo"
  • option2: "baz"
  • option3: "qux"
  • option4: "corge"
  • option5: "grault"

これらのレイヤーにより、

  • すべてのddu#start()で共通する設定はglobal
  • ddu#start()する際のコンテキストに依存しない設定はlocal
  • ddu#start()する際のコンテキストに依存する設定はuser

というように設定できます。

オプションの構成

オプションは各構成要素ごとに分かれています。

  • UIの設定
  • Sourceの設定
  • Filterの設定
  • Kindの設定

各構成要素のためのオプションには、OptionParamがあります。
例えば、SourceのためのOptionは、次のように設定します。

\ "sourceOptions": {
\     "_": {
\          ... # すべてのSourceに適用するOptions
\     },
\     "file_rec" {
\          ... # file_rec Sourceだけに適用するOptions
\     },
\ }

内容の詳細は各ヘルプに詳しく記載されています。

  • :help ddu-option-ui
  • :help ddu-option-uiOptions
  • :help ddu-option-uiParams
  • :help ddu-option-filterOptions
  • :help ddu-option-filterParams
  • :help ddu-option-sources
  • :help ddu-option-sourceOptions
  • :help ddu-option-sourceParams

注意点として、uisourcesには

  • どのUI/Sourceを使うか
  • 適用するOptionsParams

を同時に指定できます。例えば

\ "sources": [{
\     "name": "file_rec",
\     "params": {
\         "ignoredDirectories": ["node_modules"]
\     },
\     "options:" {
\         "matchers": ["substring"]
\     }
\ }]

\ "sources": ["file_rec"],
\ "sourceParams": {
\     "file_rec": {
\         "ignoredDirectories": ["node_modules"]
\     }
\ },
\ "sourceOptions": {
\     "file_rec": {
\         "matchers": ["substring"]
\     }
\ }

は同じ意味です。
global, local, userといったオプションのレイヤーを分割する際や、複数のSourceをまたいで共通するOptionを設定する際など、場合によって使いやすい方を選んで使用します。

また、どのKindを使用するか、は原則的にSourceによって決定されます。
Sourceのヘルプには、どのKindを必要とするかが明記されています。

例::help ddu-source-file_rec-install

実際の設定例

実際に以下の様な設定を例示します。

  • 現在のディレクトリ(getcwd())配下のファイルをFuzzy Finder UIで表示する
  • 2種類の設定でddu.vimを利用する
    • コマンド :DduNodeFiles : .git/ node_modules/ディレクトリ配下を無視する
    • コマンド :DduWholeFiles : ファイルを無視せず、50,000件を上限とする
  • 部分文字列の完全一致による絞り込みを可能とする
  • 選んだItemeキーで開く
" 以下各プラグインをインストールしておく
" vim-denops/denops.vim
" Shougo/ddu.vim
" Shougo/ddu-ui-ff
" Shougo/ddu-source-file_rec
" Shougo/ddu-kind-file
" Shougo/ddu-filter-matcher_substring

" 全体に共通する設定を行う
call ddu#custom#patch_global({
    \     "ui": "ff",
    \     "sourceOptions": {
    \         "_": {
    \             "matchers": ["matcher_substring"]
    \         },
    \     },
    \ })

" DduNodeFilesで使用する設定を用意する
call ddu#custom#patch_local("node-files", {
    \     "sources": ["file_rec"],
    \     "sourceParams": {
    \         "file_rec": {
    \             "ignoredDirectories": [".git", "node_modules"],
    \         }
    \     }
    \ })

" DduWholeFilesで使用する設定を用意する
call ddu#custom#patch_local("whole-files", {
    \     "sources": ["file_rec"],
    \     "sourceParams": {
    \         "file_rec": {
    \             "ignoredDirectories": [],
    \         }
    \     },
    \     "sourceOptions": {
    \         "file_rec": {
    \             "maxItems": 50000
    \         }
    \     }
    \ })

" ddu-ui-ff上でのみ有効なKeymap(`e`)を設定する
autocmd FileType ddu-ff call s:ddu_ff_settings()
function s:ddu_ff_settings() abort
    nnoremap <buffer> e <Cmd>call ddu#ui#do_action('itemAction', {'name': 'open'})<CR>
endfunction

" ddu#startを各オプションセット名で呼び出すコマンドを準備する
command! DduNodeFiles call ddu#start({"name": "node-files", "sourceOptions": {"file_rec": {"path": getcwd()}}})
command! DduWholeFiles call ddu#start({"name": "whole-files", "sourceOptions": {"file_rec": {"path": getcwd()}}})

終わりに

ここからは、今回の記事の主旨(基礎的な概観)からは外れた実用、感想を含む内容です。

実用上のコツ

ddu.vimはかなりのユースケースに対応可能な柔軟性を持っています。
「設定」の範囲内で、かなりの不満を解消できます。
何か不満が生じたときのためのコツがいくつかあります。

  • Sourceで様々なユースケースを吸収しようとしない
    • 表示が気に食わない→Filter(Converter)で変換すればよい
    • XXがリストに含まれないのが気に食わない→複数のSourceを混ぜればよい
    • アクションが気に食わない→Custom Actionや、Action Overwriting(:help ddu-source-option-actions)を設定すれば良い
  • Kindに完全性を求めない
    • Custom Actionを使う
    • Custom Actionでpush(:help ddu-option-push)を使って別のSourceにつなぐ
  • すべてをKeymapしない
    • chooseAction (:help ddu-ui-ff-action-chooseAction:help ddu-ui-filer-action-chooseAction)を活用する
  • insertモードで色々と操作しようとしない
    • クエリを入力するモード(insertモード)とItemを選択するモード(normalモード)は分ける
    • Vimらしい、モードごとに分かれた挙動とした方が何かと困難に遭遇しにくい

これらについてはこの記事では列挙するのみにとどめ、詳細はいずれ別記事を起こすかもしれません。

Loves ddu.vim

ddu.vimの設定はかなり柔軟性をもっており、その実現のために1つ1つの設定から得られる情報の結合度が低く、俯瞰して統合した知識を得ることが難しいように感じました。
そこで1つの挑戦として、私なりの理解した内容を俯瞰して記事にしてみました。

ddu.vimの柔軟性には大変助けられる部分が大きく、既存のFuzzy Finderをカスタムしようと苦労した人ほど、嬉しいプラグインだと感じています。
他にも同じように苦しんでいる人がいれば、この記事が導入の一助となれれば幸いです。

脚注
  1. ddu.vimはカスタマイズの自由度に重きを置いた設定方法になっているため、「どのように使うべきか」がほとんど提示されておらず、FuzzyFinderとして使うのも飽くまで1つの用例に過ぎません。
    そのため「Fuzzy Finderそのもの」ではありません。 ↩︎

  2. この記事では紹介していません。応用編では紹介できればと思っていますが、この記事では「何かそういうのがあるのね…」くらいに思っていただければ。 ↩︎ ↩︎

Discussion

hokorobihokorobi

ddu#custom#patch_local() の使い方もわかって設定をだいぶ整理できました。ありがとうございます。