🐼

An Overview of Basic Settings for ddu.vim

2023/12/15に公開

What This Article Is and Is Not About

  • ✅: Writing about my understanding of ddu.vim
    • Focused on "my understanding"
    • Describes the basic structure
  • ❌: Does not describe design philosophy
    • I do not consider myself in a position to talk about the "philosophy"
    • It's just "how I see it"
  • ❌: Not exhaustive
    • Does not explain all ways to use ddu.vim
    • Only covers a small part of ddu.vim
    • Only includes "important things for understanding the overview"
  • ❌: Not a help or guideline
    • Not a substitute for the help documentation or reference
  • ❗: Assertive
    • To avoid all sentences having a speculative or vague expression like "it seems," "it appears"
    • Deliberately omits these to make it easier to read

Introduction: What is ddu.vim?

ddu.vim is a list view plugin that replaces the Fuzzy Finder commonly seen in Vim/Neovim and other text editors. [1]

Basic Components of ddu.vim

ddu.vim itself can't do anything.
By combining the following three elements, it can display various lists, select items, and perform actions:

  • Source
    • Generates a list of Items
  • Kind
    • Takes a list of Items and executes actions
  • UI
    • Displays the list of Items
    • Accepts user selections of Items
    • Accepts user-specified action names

Also, by combining three types of Filter, you can extract, transform and sort items like Fuzzy Finder:

  • Filter
    • Matcher: Takes user query input and extracts from the list of Items
    • Converter: Takes user query input and transforms the list of Items [2]
    • Sorter: Takes user query input and sorts the list of Items [2:1]
  • UI
    • Receives user query input for passing to Filters

ddu.vim connects them:

  • Receives a list of Items from Source
  • Passes the list of Items through Filter
  • Gives the list of Items to UI
  • Receives the selected Item and action request from UI
  • Calls the action specified by UI in Kind

How to Use ddu.vim

I outline how to set up and use ddu.vim in practice.

Install

First, install the following set:

Additionally, install your preferred UI, Source, and Kind.
If you want to filter Items by query input, install a Matcher.

In the following sections, I assume a set of four plugins that are easy to try out:

Usage Flow

When using it, follow this flow:

# Purpose Actual Operation
1 Display the list with ddu.vim :call ddu#start({options})
2 Show the buffer for Input :call ddu#ui#do_action("openFilterWindow")
3 Enter the word to filter Items i, Enter keyword and <ESC>
4 Move to the list buffer :call ddu#ui#do_action("leaveFilterWindow")
5 Select Item Navigate with j or k
6 Call Kind action :call ddu#ui#do_action("itemAction", {"name": <action name>})

Now, let's explain each process.

ddu#start({options})

This is the starting point for all uses in ddu.vim, such as Source retrieval and UI display. Examples of options are given later.

ddu#ui#do_action()

Requests ddu.vim's UI to call an action. In this example, I'm using ddu-ui-ff as the UI, so the actions being called are defined in ddu-ui-ff.

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

itemAction is a bit complex, but it requests ddu.vim to call the action of the Kind corresponding to the Item, specified by the name.

Specifying Options

ddu.vim has four layers of options:

  • user: Options passed directly to ddu#start({dict})
  • local: Custom named option sets. Set with ddu#custom#patch_local({name}, {dict})
  • global: Common settings for all. Set by ddu#custom#patch_global({dict})
  • default: Default values

ddu.vim merges these options in the order of user→local→global→default.
Therefore, an option specified globally is overridden by the same local option, which is further overridden by the user option.
The name of the local option set (name value) also follows the specification in user and global.

For example,

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",  " Use the local option named "local-name"
    \     option4 = "corge",    " !
    \     option5 = "grault",   " !
    \ })

Calling it like this merges the options as follows:

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

These layers allow you to set:

  • Settings common to all ddu#start() in global
  • Settings independent of the context in which ddu#start() is called in local
  • Settings dependent on the context in which ddu#start() is called in user

Option Structure

Options are divided for each component:

  • UI settings
  • Source settings
  • Filter settings
  • Kind settings

Each component has Option and Param. For example, set Option for Source as follows:

\ "sourceOptions": {
\     "_": {
\          ... # Options applied to all Sources
\     },
\     "file_rec" {
\          ... # Options applied only to file_rec Source
\     },
\ }

Details are in each help document:

  • :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

Note that for ui and sources, you can specify:

  • Which UI/Source to use
  • The Options, Params to apply

For instance,

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

and

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

are equivalent. Choose the easier option depending on the situation, such as when dividing layers of options or setting common Options across multiple Sources.

Also, which Kind to use is generally determined by the Source. Each Source help document clearly states the required Kind.

Example: :help ddu-source-file_rec-install

A Configuration Example

Here's a configuration example:

  • Display files under the current directory (getcwd()) with a Fuzzy Finder UI
  • Use ddu.vim with two types of settings:
    • Command :DduNodeFiles: Ignore files under .git/, node_modules/ directories
    • Command :DduWholeFiles: Don't ignore files, limit to 50,000 items
  • Allow filtering by exact substring match
  • Open selected Item with the e key
" Install the following plugins first
" 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

" Set common settings for all
call ddu#custom#patch_global({
    \     "ui": "ff",
    \     "sourceOptions": {
    \         "_": {
    \             "matchers": ["matcher_substring"]
    \         },
    \     },
    \ })

" Prepare settings for use with DduNodeFiles
call ddu#custom#patch_local("node-files", {
    \     "sources": ["file_rec"],
    \     "sourceParams": {
    \         "file_rec": {
    \             "ignoredDirectories": [".git", "node_modules"],
    \         }
    \     }
    \ })

" Prepare settings for use with DduWholeFiles
call ddu#custom#patch_local("whole-files", {
    \     "sources": ["file_rec"],
    \     "sourceParams": {
    \         "file_rec": {
    \             "ignoredDirectories": [],
    \         }
    \     },
    \     "sourceOptions": {
    \         "file_rec": {
    \             "maxItems": 50000
    \         }
    \     }
    \ })

" Set a Keymap (`e`) effective only in ddu-ui-ff
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

" Prepare commands to call ddu#start with each option set name
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()}}})

Conclusion

This part includes practical tips and thoughts that deviate from the main theme (basic overview) of this article.

Practical Tips

ddu.vim is highly flexible and can accommodate a wide range of use cases. Many issues can be resolved within the "configuration" range. Here are some tips for when issues arise:

  • Don't try to absorb various use cases in Source
    • If you don't like that appearance → Use Filter (Converter) for transformation
    • If you're unhappy that XX isn't included in the list → Mix multiple Sources
    • If you don't like the actions → Use Custom Action or Action Overwriting (:help ddu-source-option-actions)
  • Don't demand completeness in Kind
    • Use Custom Action
    • Use push (:help ddu-option-push) in Custom Action to connect to another Source
  • Don't map everything to key
    • Utilize chooseAction (:help ddu-ui-ff-action-chooseAction or :help ddu-ui-filer-action-chooseAction)
  • Don't try to do various operations in insert mode
    • Separate the mode for entering queries (insert mode) and the mode for selecting Items (normal mode)
    • Vim-like, mode-separated behavior makes it less likely to encounter difficulties

These are just listed here, and details may be covered in a separate article later.

Loves ddu.vim

The settings in ddu.vim are quite flexible, and each setting provides a low degree of integrated information for its realization, making it difficult to obtain overarching, integrated knowledge.
As a challenge, I tried to write down my understanding in this article.

I'm greatly helped by the flexibility of ddu.vim, and I believe it's a delightful plugin, especially for those who have struggled to customize existing Fuzzy Finders.
If others are struggling similarly, I hope this article helps them get started.

脚注
  1. ddu.vim focuses on customizability in its setup, barely suggests "how to use it," and using it as a Fuzzy Finder is just one example. So, it's not just "Fuzzy Finder". ↩︎

  2. Not introduced in this article. I hope to cover them in an advanced post, but for now, just think of them as "something like that exists." ↩︎ ↩︎

Discussion