From 88b481adaf64db20a7a1804bdcb82e9e928d9352 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 1 Jul 2023 13:12:19 +0800 Subject: [PATCH] Rework provider argument parsing in Rust (#982) * Parse args for files provider on Rust end * Parse grep args on Rust end * Clean up files.vim and move initialization into files provider * Remove manual args parsing in vimscript and `apply_query` * Remove unused `clap#spinner#set_raw` * Remove outdated docs * Integrate ProviderArgs into Provider * docs * Display help when `-h, --help` is used * Remove deprecated flags * Update CHANGELOG.md * Update README.md * Fixes * Fixes * Store recent n files in FilesProvider * Remove raw_provider_args() Use provider_args() for simplicity * Delete PROVIDER.md Outdated anyway * TODO: Proper completion help * Update CHANGELOG.md --- CHANGELOG.md | 8 + Cargo.lock | 1 + PROVIDER.md | 196 ----------------- README.md | 28 +-- autoload/clap.vim | 44 +--- autoload/clap/api.vim | 26 ++- autoload/clap/api/clap.vim | 14 -- autoload/clap/file_explorer.vim | 1 + autoload/clap/floating_win.vim | 2 - autoload/clap/helper.vim | 1 + autoload/clap/popup.vim | 2 - autoload/clap/provider/blines.vim | 2 - autoload/clap/provider/files.vim | 34 --- autoload/clap/spinner.vim | 12 - crates/maple_core/Cargo.toml | 1 + .../src/stdio_server/handler/on_initialize.rs | 8 - .../stdio_server/provider/dumb_jump/mod.rs | 4 +- .../src/stdio_server/provider/files.rs | 121 ++++++++-- .../src/stdio_server/provider/grep.rs | 87 ++++++-- .../src/stdio_server/provider/mod.rs | 71 +++++- crates/maple_core/src/stdio_server/vim.rs | 13 +- crates/rpc/src/lib.rs | 4 +- doc/clap-provider.txt | 208 ------------------ doc/clap.txt | 173 +++------------ 24 files changed, 319 insertions(+), 742 deletions(-) delete mode 100644 PROVIDER.md delete mode 100644 doc/clap-provider.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index c6721b25b..8dbd556f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## [unreleased] +### Removed + +- Remove a bunch of deprecated flags: `g:clap_maple_delay`, `g:clap_dispatcher_drop_cache`, `g:clap_default_external_filter`, `g:clap_builtin_fuzzy_filter_threshold`, `g:clap_cache_threshold`, `g:clap_force_matchfuzzy`, `g:clap_force_python`. They are unused now and I believe most of them are hardly really used by users. + +### Changed + +- `++opt` and `+opt` have been replaced with `--opt value`/`--opt=value` and `--opt` in a consistent way. Ref to #981 for upgrade guide. ## [0.44] 2023-05-27 @@ -14,6 +21,7 @@ ## [0.40] 2023-01-27 ## [0.39] 2023-01-13 + ## [0.38] 2023-01-08 ### Added diff --git a/Cargo.lock b/Cargo.lock index 52617dda7..6e2575224 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1054,6 +1054,7 @@ dependencies = [ "base64 0.13.1", "bytecount", "chrono", + "clap", "directories", "dumb_analyzer", "filter", diff --git a/PROVIDER.md b/PROVIDER.md deleted file mode 100644 index e5d2d7509..000000000 --- a/PROVIDER.md +++ /dev/null @@ -1,196 +0,0 @@ -# Clap provider - - - -* [Introduction](#introduction) - * [1. Non-pure-async provider](#1-non-pure-async-provider) - * [2. Pure async provider](#2-pure-async-provider) -* [Provider arguments](#provider-arguments) -* [Create non-pure-async provider](#create-non-pure-async-provider) -* [Create pure async provider](#create-pure-async-provider) - * [Non-RPC based](#non-rpc-based) - * [RPC-based](#rpc-based) -* [Register provider](#register-provider) -* [FAQ](#faq) - * [How to add the preview support for my provider?](#how-to-add-the-preview-support-for-my-provider) - - - -### Introduction - -The provider of vim-clap is actually a Dict that specifies the action of your move in the input window. The idea is simple, once you have typed something, the `source` will be filtered or a job will be spawned, and then the result retrived later will be shown in the dispaly window. - -There are generally two kinds of providers in vim-clap. - -#### 1. Non-pure-async provider - -suitable for these which are able to collect all the items in a short time, e.g., open buffers, command history.It will run sync if the source is not large. But it's also able to deal with the list that is huge, let's say 100,000+ lines/items, in which case vim-clap will choose to run the external filter in async. In a word, vim-clap can always be fast responsive. What's more, it's extremely easy to introduce a new non-pure-async clap provider as vim-clap provides the default implementation of `on_typed` and `source_async`. - -Caveat: if you have some synchronous operations in `source`, e.g., read multiple files, ensure it won't slow clap down, as in which case the default implementation of `source_async` won't help. - -#### 2. Pure async provider - -suitable for the time-consuming jobs, e.g., grep a word in a directory. Checkout out [grep provider](autoload/clap/provider/grep.vim). - -### Provider arguments - -```vim -:Clap [provider_id_or_alias] [++opt] [+opt] -``` - -All the opts are accessible via `g:clap.context[opt]`. - -The form of `[++opt]` is `++{optname}={value}`, where {optname} is one of: - - - `++externalfilter=fzf` or `++ef=fzf`. - -`[+opt]` is used for the bool arguments: - - - `+async` - -`Clap! [provider_id_or_alias]` is equal to `Clap [provider_id_or_alias] +async`. - -`++opt` and `+opt` will be stored in the Dict `g:clap.context`, the rest arguments will be stored in a List of String `g:clap.provider.args`. - -### Create non-pure-async provider - -For the non-pure-async providers, you could run it in async or sync way. By default vim-clap will choose the best strategy, running async for the source consisted of 5000+ lines or otherwise run it in sync way. [See the discussion about the non-pure-async providers](https://github.com/liuchengxu/vim-clap/issues/17#issue-501470657). - -Field | Type | Required | Has default implementation -:---- | :---- | :---- | :---- -`sink` | String/Funcref | **mandatory** | No -`sink*` | Funcref | optional | No -`source` | String/List/Funcref | **mandatory** | No -`source_type` | Number | optional | No -`source_async` | String | optional | **Yes** -`filter` | Funcref | **mandatory** | **Yes** -`on_typed` | Funcref | **mandatory** | **Yes** -`on_move` | Funcref | optional | No -`on_move_async` | Funcref | optional | No -`on_enter` | Funcref | optional | No -`on_exit` | Funcref | optional | No -`support_open_action` | Bool | optional | **Yes** if the `sink` is `e`/`edit`/`edit!` -`enable_rooter` | Bool | Optional | No -`syntax` | String | Optional | No -`prompt_format` | String | Optional | No -`init` | Funcref | Optional | **Yes** -`action` | Dict | Optional | No - -- `sink`: - - String: vim command to handle the selected entry. - - Funcref: reference to function to process the selected entry. - -- `sink*`: similar to `sink`, but takes the list of multiple selected entries as input. - -- `source`: - - List: vim List as input to vim-clap. - - String: external command to generate input to vim-clap (e.g. `find .`). - - Funcref: reference to function that returns a List to generate input to vim-clap. - -- `source_type`: type of `source`, vim-clap can detect it itself, but could be slow in some edge cases, e.g., `blines` for a file having 1 million lines. Setting this property explicitly can save the time for checking the source type. - - `g:__t_string` - - `g:__t_list` - - `g:__t_func_string` - - `g:__t_func_list` - -- `source_async`: String, job command to filter the items of `source` based on the external tools. The default implementation is to feed the output of `source` into the external fuzzy filters and then display the filtered result, which could have some limitations, e.g., the matched input is not highlighted. - -- `filter`: given what you have typed, use `filter(entry)` to evaluate each entry in the display window, when the result is zero remove the item from the current result list. The default implementation is to match the input using vim's regex. - -- `on_typed`: reference to function to filter the `source`. - -- `on_move`: when navigating the result list, can be used for the preview purpose, see [clap/provider/colors](autoload/clap/provider/colors.vim). - -- `on_move_async`: async preview implementation, normally done by the Rust binary maple, see [autoload/clap/provider/filer.vim](autoload/clap/provider/filer.vim). - -- `on_enter`: when entering the clap window, can be used for recording the current state. - -- `on_exit`: can be used for restoring the state on start. - -- `enable_rooter`: try to run the `source` from the project root. - -- `syntax`: used to set the syntax highlight for the display buffer easier. `let s:provider.syntax = 'provider_syntax'` is equal to `let s:provider.on_enter = { -> g:clap.display.setbufvar('&syntax', 'provider_syntax')}`. - -- `prompt_format`: used for showing some dynamic information, checkout [autoload/clap/provider/tags.vim](autoload/clap/provider/tags.vim) for the usage. Don't forget to call `clap#spinner#refresh()` to reveal the changes after setting a new `prompt_format` in the provider. - -- `init`: used for initializing the display window. - -- `action`: used for performing some action on the entry, e.g., delete buffer in `buffers` provider, based on `confirm()`. Each key except `title` uses the rule of `choices` of `confirm()`, each value is a `Funcref` called when the shortcut key for the choice is triggered. - - `title` is a special key for defining the title of this dialog, it's optional, the default one would be `Choose action:`. - - ```vim - let s:buffers.action = { - \ 'title': function('s:actions_title'), - \ '&Delete': function('s:action_delete'), - \ 'OpenInNew&Tab': { -> clap#selection#try_open('ctrl-t') }, - \ 'Open&Vertically': { -> clap#selection#try_open('ctrl-v') }, - \ } - ``` - -You have to provide `sink` and `source` option. The `source` field is indispensable for a synchronous provider. In another word, if you provide the `source` option this provider will be seen as a sync one, which means you could use the default `on_typed` implementation of vim-clap. - -### Create pure async provider - -#### Non-RPC based - -Everytime your input is changed, a new job will be spawned. - -Field | Type | Required | Has default implementation -:---- | :---- | :---- | :---- -`sink` | funcref | **mandatory** | No -`on_typed` | funcref | **mandatory** | No -`on_move` | funcref | optional | No -`on_enter` | funcref | optional | No -`converter` | funcref | optional | No -`jobstop` | funcref | **mandatory** | **Yes** if you use `clap#dispatcher#job_start(cmd)` -`support_open_action` | Bool | optional | **Yes** if the `sink` is `e`/`edit`/`edit!` -`enable_rooter` | Bool | Optional | No -`prompt_format` | String | Optional | No -`syntax` | String | Optional | No - -- `on_typed`: reference to function to spawn an async job. -- `converter`: reference to function to convert the raw output of job to another form, e.g., prepend an icon to the grep result, see [clap/provider/grep.vim](autoload/clap/provider/grep.vim). -- `jobstop`: Reference to function to stop the current job of an async provider. By default you could utilize `clap#dispatcher#job_start(cmd)` to start a new job, and then the job stop part will be handled by vim-clap as well, otherwise you'll have to take care of the `jobstart` and `jobstop` on your own. - -You must provide `sink`, `on_typed` option. It's a bit of complex to write an asynchornous provider, you'll need to prepare the command for spawning the job and overal workflow, although you could use `clap#dispatcher#job_start(cmd)` to let vim-clap deal with the job control and display update. Take [clap/provider/grep.vim](autoload/clap/provider/grep.vim) for a reference. - -#### RPC-based - -The RPC service will be started on initializing the display window when this kind of provider is invoked. Everytime your input is changed, the filtering happens or the request will be send the stdio RPC server powered by the Rust binary `maple`. The `source_typ` has to be `g:__t_tpc`. Additional properties for the provider are: - -Field | Type | Required | Has default implementation -:---- | :---- | :---- | :---- -`on_no_matches` | funcref | optional | No -`cr_action` | funcref | optional | No -`init` | funcref | **mandatory** | No - -TODO: `provider.mappings` - -This kind of provider requires you to be experienced in VimScript and Rust. Checkout the source code [autoload/clap/provider/filer.vim](autoload/clap/provider/filer.vim) and [src/rpc.rs](src/rpc.rs) directly. - -### Register provider - -Vim-clap will try to load the providers with such convention: - -- vimrc - -Define `g:clap_provider_{provider_id}` in your vimrc, e.g., - -```vim -" `:Clap quick_open` to open some dotfiles quickly. -let g:clap_provider_quick_open = { - \ 'source': ['~/.vimrc', '~/.spacevim', '~/.bashrc', '~/.tmux.conf'], - \ 'sink': 'e', - \ } -``` - -- autoload - -`g:clap#provider#{provider_id}#`. See `:h autoload` and [clap/provider](autoload/clap/provider). - -### FAQ - -#### How to add the preview support for my provider? - -Use `on_move()` and `g:clap.preview.show([lines])`, ensure it always runs fast. diff --git a/README.md b/README.md index 13a3c4f1e..b35ecd835 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,10 @@ Vim-clap is a modern generic performant finder using the `floating_win` of neovi - [x] ~~Pure vimscript~~. - Pin to some early version of vim-clap if you prefer the pure vimscript plugin. - [x] ~~Work out of the box, without any extra dependency~~. - - The Rust binary is required to have a decent user experience. + - The Rust binary is now a must-have to make everything work smoothly. - [x] Blazingly fast thanks to the powerful Rust backend. -- [x] Extensible, easy to add new providers. +- [x] Consistent command interface with [clap-rs/clap](https://github.com/clap-rs/clap) +- [x] Support writing new providers in both Vimscript and Rust. - [x] Support [the search syntax borrowed from fzf](https://github.com/junegunn/fzf#search-syntax) and more. - [x] Flexible UI layout. - [ ] Support searching by multiple providers simultaneously. @@ -105,19 +106,6 @@ Vim-clap is utterly easy to use, just type, press Ctrl-J/K to locate the wanted The paradigm is `Clap [provider_id_or_alias] {provider_args}`, where the `provider_id_or_alias` is obviously either the name or alias of provider. Technically the `provider_id` can be anything that can be used a key of a Dict, but I recommend you using an _identifier_ like name as the provider id, and use the alias rule if you prefer a special name. -
- cache is no longer necessary since v0.37. - -You can use `+no-cache` option to disable/refresh the cache, e.g., `:Clap files +no-cache ~` for searching files under the home directory without cache, the shortcut for `+no-cache` option: - -- `:Clap!! [provider_id_or_alias] {provider_args}`, e.g, `:Clap!! files ~`. -- `:Clap [provider_id_or_alias][!] {provider_args}`, e.g, `:Clap files! ~`. (Recommended) - -Note the `*` in the spinner, it tells you are using the cache, use `g:clap_forerunner_status_sign` to configure it. - -cache spinner -
- #### Providers | Command | List | Requirement | @@ -156,7 +144,6 @@ Note the `*` in the spinner, it tells you are using the cache, use `g:clap_forer | `Clap proj_tags` | Tags in the current project | **[maple][maple]** and **[universal-ctags][universal-ctags]** (`+json`) | | `Clap recent_files` | Persistent ordered history of recent files | **[maple][maple]** | -[fd]: https://github.com/sharkdp/fd [rg]: https://github.com/BurntSushi/ripgrep [git]: https://github.com/git/git [maple]: https://github.com/liuchengxu/vim-clap/blob/master/INSTALL.md#maple-binary @@ -165,10 +152,10 @@ Note the `*` in the spinner, it tells you are using the cache, use `g:clap_forer - The command with a superscript `!` means that it is not yet implemented or not tested. - The command with a superscript `+` means that it supports multi-selection via Tab. - `:Clap grep` - - Use `:Clap grep ++query=` to grep the word under cursor. - - Use `:Clap grep ++query=@visual` to grep the visual selection. + - Use `:Clap grep --query=` to grep the word under cursor. + - Use `:Clap grep --query=@visual` to grep the visual selection. - `cwd` will be searched by default, specify the extra paths in the end to search multiple directories. - - `:Clap grep ~/.vim/plugged/ale` with `cwd` is `~/.vim/plugged/vim-clap` will both search vim-clap and ale. + - `:Clap grep --path ~/.vim/plugged/ale` with `cwd` is `~/.vim/plugged/vim-clap` will both search vim-clap and ale. [Send a pull request](https://github.com/liuchengxu/vim-clap/pulls) if you want to get your provider listed here. @@ -316,7 +303,8 @@ User config file is loaded from: ```toml [matcher] -# There are four sort keys for results: score, begin, end, length, you can specify how the records are sorted using `tiebreak`. +# There are four sort keys for results: score, begin, end, length, +# you can specify how the records are sorted using `tiebreak`. tiebreak = "score,-begin,-end,-length" ``` diff --git a/autoload/clap.vim b/autoload/clap.vim index 8d027d20f..4e85c0e01 100644 --- a/autoload/clap.vim +++ b/autoload/clap.vim @@ -297,41 +297,6 @@ function! clap#for(provider_id_or_alias) abort call clap#indicator#render() endfunction -function! s:parse_opts(args) abort - let idx = 0 - let g:clap.provider.raw_args = a:args - " TODO: Switch the argument parsing to CLI interface? - let g:clap.provider.args = [] - for arg in a:args - if arg ==# '--' - let g:clap.context.query = join(a:args[idx+1 :], ' ') - break - endif - if arg =~? '^++\w*=\w*' - let matched = matchlist(arg, '^++\(\w*\)=\(\S*\)') - let [k, v] = [matched[1], matched[2]] - if has_key(g:clap.context, k) - let g:clap.context[k] .= ' '.v - else - let g:clap.context[k] = v - endif - elseif arg =~? '^+\w*' - let opt = arg[1:] - let g:clap.context[opt] = v:true - else - call add(g:clap.provider.args, arg) - endif - let idx += 1 - endfor - if has_key(g:clap.context, 'query') - if g:clap.context.query ==# '@visual' - let g:clap.context.query = clap#util#get_visual_selection() - else - let g:clap.context.query = clap#util#expand(g:clap.context.query) - endif - endif -endfunction - function! clap#(bang, ...) abort if !exists('g:clap') call clap#init#() @@ -370,18 +335,13 @@ function! clap#(bang, ...) abort if a:1 ==# '!' let g:clap.context['no-cache'] = v:true let provider_id_or_alias = a:2 - call s:parse_opts(a:000[2:]) + let g:clap.provider.args = a:000[2:] else let provider_id_or_alias = a:1 - call s:parse_opts(a:000[1:]) + let g:clap.provider.args = a:000[1:] endif endif - if provider_id_or_alias =~# '!$' - let g:clap.context['no-cache'] = v:true - let provider_id_or_alias = provider_id_or_alias[:-2] - endif - call clap#for(provider_id_or_alias) endfunction diff --git a/autoload/clap/api.vim b/autoload/clap/api.vim index 697abace1..e70e6725b 100644 --- a/autoload/clap/api.vim +++ b/autoload/clap/api.vim @@ -67,6 +67,10 @@ function! s:api.display_getcurline() abort return [g:clap.display.getcurline(), get(g:, '__clap_icon_added_by_maple', v:false)] endfunction +function! s:api.display_set_lines(lines) abort + call g:clap.display.set_lines(a:lines) +endfunction + function! s:api.provider_source() abort if has_key(g:clap.provider, 'source_type') && has_key(g:clap.provider._(), 'source') if g:clap.provider.source_type == g:__t_string @@ -98,10 +102,6 @@ function! s:api.provider_args() abort return get(g:clap.provider, 'args', []) endfunction -function! s:api.provider_raw_args() abort - return get(g:clap.provider, 'raw_args', []) -endfunction - function! s:api.input_set(value) abort call g:clap.input.set(a:value) endfunction @@ -139,6 +139,24 @@ function! s:api.show_lines_in_preview(lines) abort endif endfunction +function! s:api.set_initial_query(query) abort + if a:query ==# '@visual' + let query = clap#util#get_visual_selection() + else + let query = clap#util#expand(a:query) + endif + + if s:is_nvim + call feedkeys(query) + else + call g:clap.input.set(query) + " Move the cursor to the end. + call feedkeys("\", 'xt') + endif + + return query +endfunction + function! clap#api#call(method, args) abort " Catch all the exceptions try diff --git a/autoload/clap/api/clap.vim b/autoload/clap/api/clap.vim index 731027e22..e130d73da 100644 --- a/autoload/clap/api/clap.vim +++ b/autoload/clap/api/clap.vim @@ -377,20 +377,6 @@ function! s:init_provider() abort endif endfunction - function! provider.apply_query() abort - if has_key(g:clap.context, 'query') - if s:is_nvim - call feedkeys(g:clap.context.query) - else - call g:clap.input.set(g:clap.context.query) - " Move the cursor to the end. - call feedkeys("\", 'xt') - endif - call clap#indicator#set_none() - call g:clap.provider.on_typed() - endif - endfunction - function! provider._apply_source() abort let Source = self._().source diff --git a/autoload/clap/file_explorer.vim b/autoload/clap/file_explorer.vim index 7ae62fc77..665c9bc20 100644 --- a/autoload/clap/file_explorer.vim +++ b/autoload/clap/file_explorer.vim @@ -37,6 +37,7 @@ function! clap#file_explorer#init_current_dir() abort return current_dir endif + " TODO: Specify --path let maybe_dir = g:clap.provider.args[0] " %:p:h, % is actually g:clap.start.bufnr if maybe_dir =~# '^%.\+' diff --git a/autoload/clap/floating_win.vim b/autoload/clap/floating_win.vim index da95aa91b..e5ebc4f6f 100644 --- a/autoload/clap/floating_win.vim +++ b/autoload/clap/floating_win.vim @@ -503,8 +503,6 @@ function! clap#floating_win#open() abort startinsert let g:clap.context.visible = v:true - - call g:clap.provider.apply_query() endfunction function! s:win_close(winid) abort diff --git a/autoload/clap/helper.vim b/autoload/clap/helper.vim index 870d66d05..292b4bde7 100644 --- a/autoload/clap/helper.vim +++ b/autoload/clap/helper.vim @@ -27,6 +27,7 @@ function! s:relativize(ArgLead, abs_dirs) abort endfunction function! clap#helper#complete(ArgLead, CmdLine, P) abort + " TODO: Proper completion help if a:CmdLine =~# '^Clap \(files\|grep\|filer\)' if a:ArgLead =~# '\(/\|\\\)$' || isdirectory(expand(a:ArgLead)) let parent_dir = fnamemodify(resolve(expand(a:ArgLead)), ':p') diff --git a/autoload/clap/popup.vim b/autoload/clap/popup.vim index ce6eaa20c..61ab68285 100644 --- a/autoload/clap/popup.vim +++ b/autoload/clap/popup.vim @@ -400,8 +400,6 @@ function! clap#popup#open() abort call g:clap.provider.on_enter() silent doautocmd User ClapOnEnter - - call g:clap.provider.apply_query() endfunction function! clap#popup#close() abort diff --git a/autoload/clap/provider/blines.vim b/autoload/clap/provider/blines.vim index b689f8a2b..448366b53 100644 --- a/autoload/clap/provider/blines.vim +++ b/autoload/clap/provider/blines.vim @@ -4,8 +4,6 @@ let s:save_cpo = &cpoptions set cpoptions&vim -let s:ALWAYS_ASYNC = exists('g:clap_builtin_fuzzy_filter_threshold') && g:clap_builtin_fuzzy_filter_threshold == 0 - let s:blines = {} function! s:format(lines) abort diff --git a/autoload/clap/provider/files.vim b/autoload/clap/provider/files.vim index 8dc833d0a..e556de82a 100644 --- a/autoload/clap/provider/files.vim +++ b/autoload/clap/provider/files.vim @@ -6,40 +6,6 @@ set cpoptions&vim let s:files = {} -let s:default_opts = { - \ 'fd': '--type f', - \ 'rg': '--files', - \ 'git': 'ls-tree -r --name-only HEAD', - \ 'find': '. -type f', - \ } -let s:options = filter(['fd', 'rg', 'git', 'find'], 'executable(v:val)') - -if empty(s:options) - let s:default_finder = v:null - let s:default_source = ['No usable tools found for the files provider'] -else - let s:default_finder = s:options[0] - let s:default_source = join([s:default_finder, s:default_opts[s:default_finder]], ' ') -endif - -function! s:files.source() abort - call clap#rooter#try_set_cwd() - - if has_key(g:clap.context, 'name-only') - let g:__clap_match_scope_enum = 'FileName' - endif - - if has_key(g:clap.context, 'finder') - let finder = g:clap.context.finder - return finder.' '.join(g:clap.provider.args, ' ') - elseif get(g:clap.provider, 'args', []) == ['--hidden'] - if s:default_finder ==# 'fd' || s:default_finder ==# 'rg' - return join([s:default_finder, s:default_opts[s:default_finder], '--hidden'], ' ') - endif - endif - return s:default_source -endfunction - function! s:into_filename(line) abort if g:clap_enable_icon && clap#maple#is_available() return a:line[4:] diff --git a/autoload/clap/spinner.vim b/autoload/clap/spinner.vim index 46f9eeb8a..cf16e18c5 100644 --- a/autoload/clap/spinner.vim +++ b/autoload/clap/spinner.vim @@ -59,12 +59,6 @@ if has('nvim') call g:clap#floating_win#spinner.shrink() endfunction - function! clap#spinner#set_raw(text) abort - let s:current_prompt = a:text - call setbufline(g:clap.spinner.bufnr, 1, s:current_prompt) - call g:clap#floating_win#spinner.shrink() - endfunction - function! s:set_spinner() abort let s:current_prompt = s:generate_prompt() call clap#spinner#set(s:current_prompt) @@ -76,12 +70,6 @@ else call clap#popup#shrink_spinner() endfunction - function! clap#spinner#set_raw(text) abort - let s:current_prompt = a:text - call popup_settext(g:clap_spinner_winid, s:current_prompt) - call clap#popup#shrink_spinner() - endfunction - function! s:set_spinner() abort if exists('g:clap_spinner_winid') let s:current_prompt = s:generate_prompt() diff --git a/crates/maple_core/Cargo.toml b/crates/maple_core/Cargo.toml index cb2e0f416..cc8c20f9f 100644 --- a/crates/maple_core/Cargo.toml +++ b/crates/maple_core/Cargo.toml @@ -14,6 +14,7 @@ async-trait = "0.1" base64 = "0.13" bytecount = { version = "0.6", features = ["runtime-dispatch-simd"] } chrono = { version = "0.4", features = ["serde"] } +clap = { version = "4.2", features = ["derive"] } directories = "4.0" futures = "0.3" # ripgrep for global search diff --git a/crates/maple_core/src/stdio_server/handler/on_initialize.rs b/crates/maple_core/src/stdio_server/handler/on_initialize.rs index c363e56de..c123f79c3 100644 --- a/crates/maple_core/src/stdio_server/handler/on_initialize.rs +++ b/crates/maple_core/src/stdio_server/handler/on_initialize.rs @@ -95,14 +95,6 @@ async fn initialize_provider_source(ctx: &Context) -> Result { match value { // Source is a String: g:__t_string, g:__t_func_string Value::String(command) => { - // Always try recreating the source. - if ctx.provider_id() == "files" { - let mut tokio_cmd = crate::process::tokio::TokioCommand::new(command); - tokio_cmd.current_dir(&ctx.cwd); - let lines = tokio_cmd.lines().await?; - return Ok(to_small_provider_source(lines)); - } - let shell_cmd = ShellCommand::new(command, ctx.cwd.to_path_buf()); let cache_file = shell_cmd.cache_file_path()?; diff --git a/crates/maple_core/src/stdio_server/provider/dumb_jump/mod.rs b/crates/maple_core/src/stdio_server/provider/dumb_jump/mod.rs index 91ef3db49..a7a3aea6a 100644 --- a/crates/maple_core/src/stdio_server/provider/dumb_jump/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/dumb_jump/mod.rs @@ -281,7 +281,7 @@ impl DumbJumpProvider { impl ClapProvider for DumbJumpProvider { async fn on_initialize(&mut self, ctx: &mut Context) -> Result<()> { let cwd = ctx.vim.working_dir().await?; - let source_file_extension = ctx.start_buffer_extension()?; + let source_file_extension = ctx.start_buffer_extension()?.to_string(); tokio::task::spawn({ let cwd = cwd.clone(); @@ -382,7 +382,7 @@ impl ClapProvider for DumbJumpProvider { let search_worker = SearchWorker { cwd, query_info: query_info.clone(), - source_file_extension: ctx.start_buffer_extension()?, + source_file_extension: ctx.start_buffer_extension()?.to_string(), }; let search_results = self.start_search(search_worker, query, query_info).await?; diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index e5d44e2a4..5362ececf 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -1,26 +1,59 @@ -use crate::stdio_server::handler::initialize_provider; use crate::stdio_server::provider::{ClapProvider, Context, SearcherControl}; use anyhow::Result; +use clap::Parser; use matcher::{Bonus, MatchScope}; +use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::Arc; use types::Query; -#[derive(Debug)] -pub struct FilesProvider { +use super::BaseArgs; + +#[derive(Debug, Parser, PartialEq, Eq, Default)] +#[command(name = ":Clap files")] +#[command(about = "files provider", long_about = None)] +struct FilesArgs { + #[clap(flatten)] + base: BaseArgs, + + /// Whether to search hidden files. + #[clap(long)] hidden: bool, + + /// Whether to match the file name only. + #[clap(long)] name_only: bool, + + /// Specify additional search paths apart from the current working directory. + #[clap(long = "path")] + paths: Vec, +} + +#[derive(Debug)] +pub struct FilesProvider { + args: FilesArgs, + recent_files_bonus: Bonus, searcher_control: Option, } impl FilesProvider { pub async fn new(ctx: &Context) -> Result { - let provider_args = ctx.vim.provider_args().await?; - let hidden = provider_args.iter().any(|s| s == "--hidden"); - let name_only = ctx.vim.files_name_only().await?; + let args: FilesArgs = ctx.parse_provider_args().await?; + ctx.handle_base_args(&args.base).await?; + + let expanded_paths = ctx.expanded_paths(&args.paths).await?; + + let recent_files = crate::datastore::RECENT_FILES_IN_MEMORY + .lock() + .recent_n_files(100); + let recent_files_bonus = Bonus::RecentFiles(recent_files.into()); + Ok(Self { - hidden, - name_only, + args: FilesArgs { + paths: expanded_paths, + ..args + }, + recent_files_bonus, searcher_control: None, }) } @@ -32,27 +65,28 @@ impl FilesProvider { }); } - let recent_files = crate::datastore::RECENT_FILES_IN_MEMORY - .lock() - .recent_n_files(50); - let recent_files_bonus = Bonus::RecentFiles(recent_files.into()); let matcher = ctx .matcher_builder() - .match_scope(if self.name_only { + .match_scope(if self.args.name_only { MatchScope::FileName } else { MatchScope::Full }) - .bonuses(vec![recent_files_bonus]) + .bonuses(vec![self.recent_files_bonus.clone()]) .build(Query::from(&query)); let new_control = { let stop_signal = Arc::new(AtomicBool::new(false)); let join_handle = { - let search_context = ctx.search_context(stop_signal.clone()); + let mut search_context = ctx.search_context(stop_signal.clone()); + if self.args.base.no_cwd { + search_context.paths = self.args.paths.clone(); + } else { + search_context.paths.extend_from_slice(&self.args.paths); + } let vim = ctx.vim.clone(); - let hidden = self.hidden; + let hidden = self.args.hidden; tokio::spawn(async move { let _ = vim.bare_exec("clap#spinner#set_busy"); crate::searcher::files::search(query, hidden, matcher, search_context).await; @@ -74,11 +108,8 @@ impl FilesProvider { impl ClapProvider for FilesProvider { async fn on_initialize(&mut self, ctx: &mut Context) -> Result<()> { let query = ctx.vim.context_query_or_input().await?; - if !query.is_empty() { - self.process_query(query, ctx); - } else { - initialize_provider(ctx).await?; - } + // All files will be collected if query is empty + self.process_query(query, ctx); Ok(()) } @@ -100,3 +131,51 @@ impl ClapProvider for FilesProvider { ctx.signify_terminated(session_id); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_files_args() { + assert_eq!( + FilesArgs::parse_from(["", "--hidden", "--name-only"]), + FilesArgs { + base: BaseArgs::default(), + hidden: true, + name_only: true, + paths: vec![], + } + ); + + assert_eq!( + FilesArgs::parse_from(["", "--hidden"]), + FilesArgs { + base: BaseArgs::default(), + hidden: true, + name_only: false, + paths: vec![], + } + ); + + assert_eq!( + FilesArgs::parse_from(["", "--name-only"]), + FilesArgs { + base: BaseArgs::default(), + hidden: false, + name_only: true, + paths: vec![], + } + ); + + assert_eq!( + FilesArgs::parse_from(["", "--path=~", "--name-only"]), + FilesArgs { + base: BaseArgs::default(), + hidden: false, + name_only: true, + paths: vec![PathBuf::from("~")], + } + ); + } +} diff --git a/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index dc251847e..4d2670957 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -1,23 +1,42 @@ use crate::stdio_server::provider::{ClapProvider, Context, SearcherControl}; use anyhow::Result; +use clap::Parser; use matcher::MatchScope; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::Arc; use types::Query; +use super::BaseArgs; + +#[derive(Debug, Parser, PartialEq, Eq, Default)] +#[command(name = ":Clap grep")] +#[command(about = "grep provider", long_about = None)] +struct GrepArgs { + #[clap(flatten)] + base: BaseArgs, + + /// Specify additional search paths apart from the current working directory. + #[clap(long = "path")] + paths: Vec, +} + #[derive(Debug)] pub struct GrepProvider { - paths: Vec, + args: GrepArgs, searcher_control: Option, } impl GrepProvider { - pub fn new() -> Self { - Self { - paths: Vec::new(), + pub async fn new(ctx: &Context) -> Result { + let GrepArgs { base, paths } = ctx.parse_provider_args().await?; + Ok(Self { + args: GrepArgs { + base, + paths: ctx.expanded_paths(&paths).await?, + }, searcher_control: None, - } + }) } fn process_query(&mut self, query: String, ctx: &Context) { @@ -38,7 +57,11 @@ impl GrepProvider { let vim = ctx.vim.clone(); let mut search_context = ctx.search_context(stop_signal.clone()); // cwd + extra paths - search_context.paths.extend_from_slice(&self.paths); + if self.args.base.no_cwd { + search_context.paths = self.args.paths.clone(); + } else { + search_context.paths.extend_from_slice(&self.args.paths); + } let join_handle = tokio::spawn(async move { let _ = vim.bare_exec("clap#spinner#set_busy"); crate::searcher::grep::search(query, matcher, search_context).await; @@ -58,17 +81,9 @@ impl GrepProvider { #[async_trait::async_trait] impl ClapProvider for GrepProvider { async fn on_initialize(&mut self, ctx: &mut Context) -> Result<()> { - let raw_args = ctx.vim.provider_raw_args().await?; - for args in &raw_args { - let abs_path = ctx.vim.fnamemodify(args, ":p").await?; - let abs_path = PathBuf::from(abs_path); - if abs_path.is_absolute() { - self.paths.push(abs_path); - } - } - let query = ctx.vim.context_query_or_input().await?; - if !query.is_empty() { - self.process_query(query, ctx); + let initial_query = ctx.handle_base_args(&self.args.base).await?; + if !initial_query.is_empty() { + self.process_query(initial_query, ctx); } Ok(()) } @@ -92,3 +107,41 @@ impl ClapProvider for GrepProvider { ctx.signify_terminated(session_id); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_files_args() { + assert_eq!( + GrepArgs::parse_from(["", "--query=@visual", "--path=~/.vim/plugged/vim-clap"]), + GrepArgs { + base: BaseArgs { + query: Some(String::from("@visual")), + ..Default::default() + }, + paths: vec![PathBuf::from("~/.vim/plugged/vim-clap")] + } + ); + + assert_eq!( + GrepArgs::parse_from(["", "--query=@visual"]), + GrepArgs { + base: BaseArgs { + query: Some(String::from("@visual")), + ..Default::default() + }, + paths: vec![] + } + ); + + assert_eq!( + GrepArgs::parse_from([""]), + GrepArgs { + base: BaseArgs::default(), + paths: vec![] + } + ); + } +} diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index f0e7de095..f4146e060 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -33,13 +33,29 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use types::{ClapItem, MatchedItem}; +/// [`BaseArgs`] represents the arguments common to all the providers. +#[derive(Debug, clap::Parser, PartialEq, Eq, Default)] +pub struct BaseArgs { + /// Specify the initial query. + #[clap(long)] + query: Option, + + /// Specify the working directory for this provider and all subsequent providers. + #[clap(long)] + cwd: Option, + + /// Skip the default working directory in searching. + #[clap(long)] + no_cwd: bool, +} + pub async fn create_provider(provider_id: &str, ctx: &Context) -> Result> { let provider: Box = match provider_id { "blines" => Box::new(blines::BlinesProvider::new()), "dumb_jump" => Box::new(dumb_jump::DumbJumpProvider::new()), "filer" => Box::new(filer::FilerProvider::new(ctx).await?), "files" => Box::new(files::FilesProvider::new(ctx).await?), - "grep" => Box::new(grep::GrepProvider::new()), + "grep" => Box::new(grep::GrepProvider::new(ctx).await?), "igrep" => Box::new(igrep::IgrepProvider::new(ctx).await?), "recent_files" => Box::new(recent_files::RecentFilesProvider::new(ctx)), "tagfiles" => Box::new(tagfiles::TagfilesProvider::new()), @@ -347,12 +363,11 @@ impl Context { Ok(out.stdout) } - pub fn start_buffer_extension(&self) -> std::io::Result { + pub fn start_buffer_extension(&self) -> std::io::Result<&str> { self.env .start_buffer_path .extension() .and_then(|s| s.to_str()) - .map(|s| s.to_string()) .ok_or_else(|| { std::io::Error::new( std::io::ErrorKind::Other, @@ -364,6 +379,56 @@ impl Context { }) } + pub async fn parse_provider_args(&self) -> Result { + let args = self.vim.provider_args().await?; + + let provider_args = if args.is_empty() { + T::default() + } else { + T::try_parse_from(std::iter::once(String::from("")).chain(args.into_iter())) + .map_err(|err| { + match err.kind() { + clap::error::ErrorKind::DisplayHelp => { + // Show help in the display window. + let err_msg = err.to_string(); + let lines = err_msg.split('\n').collect::>(); + let _ = self.vim.exec("display_set_lines", json!([lines])); + } + _ => { + let _ = self.vim.echo_warn(format!( + "using default {:?} due to {err}", + T::default() + )); + } + } + }) + .unwrap_or_default() + }; + Ok(provider_args) + } + + pub async fn handle_base_args(&self, base: &BaseArgs) -> Result { + let BaseArgs { query, .. } = base; + + let query = if let Some(query) = query { + self.vim.call("set_initial_query", json!([query])).await? + } else { + self.vim.input_get().await? + }; + + Ok(query) + } + + pub async fn expanded_paths(&self, paths: &[PathBuf]) -> Result> { + let mut expanded_paths = Vec::with_capacity(paths.len()); + for p in paths { + if let Ok(path) = self.vim.expand(p.to_string_lossy()).await { + expanded_paths.push(path.into()); + } + } + Ok(expanded_paths) + } + pub fn set_provider_source(&self, new: ProviderSource) { let mut provider_source = self.provider_source.write(); *provider_source = new; diff --git a/crates/maple_core/src/stdio_server/vim.rs b/crates/maple_core/src/stdio_server/vim.rs index 7e328ee0d..a4ce87a91 100644 --- a/crates/maple_core/src/stdio_server/vim.rs +++ b/crates/maple_core/src/stdio_server/vim.rs @@ -388,10 +388,6 @@ impl Vim { self.eval("g:clap.provider.id").await } - pub async fn provider_raw_args(&self) -> Result> { - self.bare_call("provider_raw_args").await - } - pub async fn working_dir(&self) -> Result { self.bare_call("clap#rooter#working_dir").await } @@ -400,11 +396,6 @@ impl Vim { self.bare_call("context_query_or_input").await } - pub async fn files_name_only(&self) -> Result { - let context: HashMap = self.eval("g:clap.context").await?; - Ok(context.contains_key("name-only")) - } - pub async fn current_buffer_path(&self) -> Result { self.bare_call("current_buffer_path").await } @@ -424,6 +415,10 @@ impl Vim { self.exec("clap#helper#echo_info", json!([msg.as_ref()])) } + pub fn echo_warn(&self, msg: impl AsRef) -> Result<()> { + self.exec("clap#helper#echo_warn", json!([msg.as_ref()])) + } + pub async fn current_winid(&self) -> Result { self.bare_call("win_getid").await } diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 3312d2241..6569c450c 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -254,14 +254,14 @@ async fn loop_write( let msg_size = s.len(); match msg { RpcMessage::Request(request) => { - tracing::trace!(method = request.method, msg_size, "=> Vim Request") + tracing::trace!(method = ?request.method, msg_size, "=> Vim Request") } RpcMessage::Response(response) => { tracing::trace!(id = response.id(), msg_size, "=> Vim Response") } RpcMessage::Notification(notification) => { tracing::trace!( - method = notification.method, + method = ?notification.method, msg_size, "=> Vim Notification" ) diff --git a/doc/clap-provider.txt b/doc/clap-provider.txt deleted file mode 100644 index 7b3d361d9..000000000 --- a/doc/clap-provider.txt +++ /dev/null @@ -1,208 +0,0 @@ -=============================================================================== -CONTENTS *clap-provider-contents* - - Provider.............................|clap-provider| - Non-pure-async Providers..........|clap-non-pure-async-providers| - Pure async Providers..............|clap-pure-async-providers| - Registering Providers.............|clap-registering-providers| - -=============================================================================== -Clap Provider *clap-provider* - *write-clap-provider* - - -The provider of vim-clap is actually a |Dict| that specifies the action of your -move in the input window. The idea is simple, every time you typed something, -the `source` will be filtered or a job `source_async` will be spawned, and then -the result retrived later will be shown in the dispaly window. - -There are generally two kinds of providers in vim-clap. - -1. Non-pure-async provider: suitable for these which are able to collect all - the items in a short time, e.g., open buffers, command history. It will run - in synchoronous if the source size is not large. - - But it's also able to deal with the list that is potentially huge, let's say - 100,000+ lines/items, in which case vim-clap will try to run the external filter - asynchronously. In a word, vim-clap can always be fast responsive. - - What's more, it's extremely easy to introduce a new non-pure-async clap provider - as vim-clap provides the default implementation of `on_typed` and `source_async`. - -2. Pure async provider: suitable for the time-consuming jobs, e.g., - grep a word in a directory. - - -------------------------------------------------------------------------------- -4.1. Non-pure-async Providers *clap-non-pure-async-providers* - - - `sink` |String| - vim command to handle the selected entry. - |Funcref| - reference to function to process the selected entry. - - This field is mandatory. - - - `sink*` |Funcref| - similar to `sink*`, but takes the list of multiple - selected entries as input. - - This field is optional. - - - `source` |List| - vim List as input to vim-clap. - |String| - external command to generate input to vim-clap, - e.g. `find .` . - |Funcref| - reference to function that returns a List to - generate input to vim-clap. - - This field is mandatory. - - - `source_async` |String| - job command to filter the items of `source` based - on the external tools. The default implementation - is to feed the output of `source` into the external - fuzzy filters and then display the filtered result, - which could have some limitations, e.g., the - matched indices is unable to be highlighted. - - This field is optional. - - - `filter` |Funcref| - given what you have typed, use `filter(entry)` to - evaluate each entry in the display window, when - the result is zero remove the item from the current - result list. The default implementation is to - match the input using vim's regex. - - This field is mandatory. - - - `on_typed` |Funcref| - reference to function to filter the `source`. - - This field is mandatory. - - - `on_move` |Funcref| - can be used for the preview purpose, when navigating - the result list, see clap/provider/colors.vim. - - It won't be called if you merely input some characters - and do not scroll the list. - - This field is optional. - - - `on_enter` |Funcref| - when entering the clap window, can be used - for recording the current state. - - This field is optional. - - - `on_exit` |Funcref| - can be used for restoring the state on start. - - This field is optional. - - - `enable_rooter` |Bool| - - This field is optional. - - - `support_open_action` |Bool| - - This field is optional. - - - `syntax` |String| - for setting the syntax highlight for the display buffer easier. - `let s:provider.syntax = 'provider_syntax'` is equal to - `let s:provider.syon_enter = { -> g:clap.display.setbufvar('&syntax', 'provider_syntax')}` . - - This field is optional. - -------------------------------------------------------------------------------- -4.2 Pure async Providers *clap-pure-async-providers* - - - `sink` |Funcref| - reference to function to process the selected - entry. - - This field is mandatory and has no default - implementation. - - - `on_typed` |Funcref| - reference to function to spawn an async job. - - This field is mandatory. - - - `on_move` |Funcref| - - This field is optional. - - - `on_enter` |Funcref| - - This field is optional. - - - `on_exit` |Funcref| - - This field is optional. - - - `converter` |Funcref| - reference to function to convert the raw output - of job to another form, e.g., prepend an icon - to the grep result. - - This field is optional. - - - `jobstop` |Funcref| - Stop the current job. - - This field is mandatory. - - - `enable_rooter` |Bool| - - This field is optional. - - - `support_open_action` |Bool| - - This field is optional. - - -------------------------------------------------------------------------------- -4.3 Registering Providers *clap-registering-providers* - - -Vim-clap will load the providers automatically when neccessary if it's defined -properly. - -- vimrc - -Define `g:clap_provider_{provider_id}` in your vimrc, e.g., - -> - " `:Clap quick_open` to open some dotfiles quickly. - let g:clap_provider_quick_open = { - \ 'source': ['~/.vimrc', '~/.spacevim', '~/.bashrc', '~/.tmux.conf'], - \ 'sink': 'e', - \ } -< - -- autoload - -`g:clap#provider#{provider_id}#`. See `:h autoload` and autoload/clap/provider.vim. - - *clap-provider-description* - -Each autoload provider should start with these two comment lines, the -Description line will be extracted as the brief introduction when displaying -all the avaliable providers via `:Clap` . -> - " Author: liuchengxu - " Description: List the windows. - -=============================================================================== - vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/clap.txt b/doc/clap.txt index 102e476c4..4a84375fd 100644 --- a/doc/clap.txt +++ b/doc/clap.txt @@ -41,14 +41,11 @@ to work everywhere out of the box, with fast response. 2. Features *clap-features* -- Pure vimscript. -- Work out of the box, without any extra dependency. -- Extensible, easy to add new source providers. -- Find or dispatch anything on the fly, with smart cache strategy. -- Untouch your current window layout, less eye movement. -- Support multi-selection, use vim's regexp as filter by default. -- Support the preview functionality when navigating the result list. -- Support builtin match and external fuzzy filter tools. +- Blazingly fast thanks to the powerful Rust backend. +- Consistent command interface with [clap-rs/clap](https://github.com/clap-rs/clap) +- Support writing new providers in both Vimscript and Rust. +- Support [the search syntax borrowed from fzf](https://github.com/junegunn/fzf#search-syntax) and more. +- Flexible UI layout. =============================================================================== @@ -184,15 +181,6 @@ g:clap_popup_move_manager *g:clap_popup_move_manager* the example at |change-clap-default-keybindings| . -g:clap_maple_delay *g:clap_maple_delay* - - Type: |Number| - Default: `100` - - This variable controls the milliseconds delay after which `maple` will be - run in case of you have 1 million items. - - g:clap_popup_cursor_shape *g:clap_popup_cursor_shape* Type: |String| @@ -211,7 +199,7 @@ g:clap_on_move_delay *g:clap_on_move_delay* provider will be run when you navigate the result list. -g:clap_forerunner_status_sign *g:clap_forerunner_status_sign* +g:clap_forerunner_status_sign *g:clap_forerunner_status_sign* Type: |Dict| Default: `{ 'running': '!', 'done': '•', 'using_cache': '*' }` @@ -244,21 +232,6 @@ g:clap_no_matches_msg *g:clap_no_matches_msg* This message will be used when there is no matches found. -g:clap_dispatcher_drop_cache *g:clap_dispatcher_drop_cache* - - Type: |Bool| - Default: `v:true` - - Normally when the external async job returns thousands of results, we won't - view them all. By default only the first N items will be actually remembered - and the others will only be counted. - - Set this variable to `v:true` to save all the results, and then when you - reach the end of the display window, the cached results will be loaded - automatically. However, it may have some performance loss to cache all the - results especially when there are hundreds of thousands of results. - - g:clap_insert_mode_only *g:clap_insert_mode_only* Type: |Bool| @@ -365,6 +338,24 @@ g:clap_preview_direction *g:clap_preview_direction* - `UD` means the display window is up and the preview window is down. + +g:clap_preview_size *g:clap_preview_size* + + Type: |Number| or |Dict| + Default: `5` + + This variable controls the size of preview, normally the number of preview + lines is `2 * g:clap_preview_size` . + + If |g:clap_preview_size| is a |Number|, all providers use the same size. + + If |g:clap_preview_size| is a |Dict|, you can use different size for various + providers, use provider id as the key, `*` is a special key for the default size. + > + " 20 preview lines for files provider, 10 lines for the other providers. + let g:clap_preview_size = { '*': 5, 'files': 10 } +< + g:clap_open_action *g:clap_open_action* Type: |Dict| @@ -400,20 +391,6 @@ g:clap_search_box_border_style *g:clap_search_box_border_style* Set this variable to `nil` to disable the search box border symbol. -g:clap_default_external_filter *g:clap_default_external_filter* - - Type: |String| or |v:null| - Default: `'maple' | 'fzy' | 'fzf' | 'sk' | v:null` - - The default provider's async implementation is based on these external - filter programs in the following order: - - `maple` - actually using the algorithm of skim, with the highlight of matched indices. - `fzy` - no highlight of matched indices. - `fzf` - no highlight of matched indices. - `sk` - no highlight of matched indices. - - g:clap_prompt_format *g:clap_prompt_format* Type: |String| @@ -514,58 +491,6 @@ g:clap_fuzzy_match_hl_groups *g:clap_fuzzy_match_hl_groups* item of |g:clap_fuzzy_match_hl_groups| is `[ctermfg, guifg]` . -g:clap_builtin_fuzzy_filter_threshold *g:clap_builtin_fuzzy_filter_threshold* - - Type: |Number| - Default: `10000`(no Python dylib), `100000`(with Python dylib) - - Vim-clap will try to spawn a asynchronous job to get the total possible - results on entering the main window, instead of dispatching with the - user's input later, to get the total result number and use the built-in - fuzzy filter which uses the fzy algorithm. - - The benefit of the built-in fzy filter is that no redraw that happens in the - async job, that means no flicker on your every input. Its drawback is that - the built-in filter is synchoronous that will block the UI if you have a - monster of candidates. - - Set the value to `0` to always use the full-featured async implementation - powerd by Rust backend. - > - let g:clap_builtin_fuzzy_filter_threshold = 0 -< - -g:clap_cache_threshold *g:clap_cache_threshold* - - Type: |Number| - Default: `100000` - - When the number of execution results of some command exceeds this variable, - the output will be wrote to a cache file, which will be reused next time - when you invoke the same command against the same directory. - - If you want to run the command without using the cache or want to rebuild - the cache, append `!` to the provider argument in `:Clap` command, e.g., - `:Clap files!` . - - -g:clap_preview_size *g:clap_preview_size* - - Type: |Number| or |Dict| - Default: `5` - - This variable controls the size of preview, normally the number of preview - lines is `2 * g:clap_preview_size` . - - If |g:clap_preview_size| is a |Number|, all providers use the same size. - - If |g:clap_preview_size| is a |Dict|, you can use different size for various - providers, use provider id as the key, `*` is a special key for the default size. - > - " 20 preview lines for files provider, 10 lines for the other providers. - let g:clap_preview_size = { '*': 5, 'files': 10 } -< - g:clap_enable_background_shadow *g:clap_enable_background_shadow* Type: |Bool| @@ -581,6 +506,7 @@ g:clap_enable_background_shadow *g:clap_enable_background_shadow - https://github.com/liuchengxu/vim-clap/issues/670, - https://github.com/liuchengxu/vim-clap/issues/836 + g:clap_background_shadow_blend *g:clap_background_shadow_blend* Type: |Number| @@ -591,27 +517,6 @@ g:clap_background_shadow_blend *g:clap_background_shadow_blend* > " Lighter shadow let g:clap_background_shadow_blend = 75 -< - -g:clap_force_matchfuzzy *g:clap_force_matchfuzzy* - - Type: |Number| - Default: `Undefined` - - Force use |matchfuzzypos()| as the sync filter engine, otherwise vim-clap - will try to use the other sync implementation in the order of Lua>Python>VimL. - - -g:clap_force_python *g:clap_force_python* - - Type: |Bool| - Default: `Undefined` - - Force use Python as the sync filter engine. Some matching improvements are - only implemented on the Rust end, you also need to compile the Python dynamic - module to use that. - - Ref https://github.com/liuchengxu/vim-clap/pull/614 ------------------------------------------------------------------------------- @@ -620,23 +525,17 @@ g:clap_force_python *g:clap_force_python* ClapInput *ClapInput* - Default: `hi default link ClapInput Visual` - - The highlight for input window. + Highlight for input window. Default: `hi default link ClapInput Visual` ClapDisplay *ClapDisplay* - Default: `hi default link ClapDisplay Pmenu` - - The highlight for display window. + Highlight for display window. Default: `hi default link ClapDisplay Pmenu` ClapPreview *ClapPreview* - Default: `hi default link ClapPreview PmenuSel` - - The highlight for preview window. + Highlight for preview window. Default: `hi default link ClapPreview PmenuSel` ClapDefaultSelected *ClapDefaultSelected* @@ -858,23 +757,9 @@ g:clap_provider_tags_force_vista *g:clap_provider_tags_force_vista* 7. Commands *clap-commands* > - :Clap[!] [provider_id_or_alias] [++opt] [+opt] + :Clap [provider_id_or_alias] < -All the opts are accessible via `g:clap.context[opt]`. - -The form of `[++opt]` is `++{optname}={value}`, where {optname} is one of: - - - `++externalfilter=fzf` or `++ef=fzf`. - -`[+opt]` is used for the bool arguments: - - - `+async` - - `+ignorecase`: case-insensitive search. - -`Clap! [provider_id_or_alias]` is equal to `Clap [provider_id_or_alias] +async` . - - =============================================================================== 8. Movement/Keybindings *clap-movement* *clap-keybindings*