From 2c8f7eb833348fe9ae8b3264ab3b2f5189a899c0 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 21 Jun 2023 14:38:29 +0800 Subject: [PATCH 01/19] Parse args for files provider on Rust end --- .../src/stdio_server/provider/files.rs | 64 ++++++++++++++++--- crates/maple_core/src/stdio_server/vim.rs | 9 ++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index e5d44e2a4..f82901b14 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -1,26 +1,40 @@ 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::sync::atomic::AtomicBool; use std::sync::Arc; use types::Query; -#[derive(Debug)] -pub struct FilesProvider { +#[derive(Debug, Parser, PartialEq, Eq, Default)] +struct FilesArgs { + #[clap(long)] hidden: bool, + #[clap(long)] name_only: bool, +} + +#[derive(Debug)] +pub struct FilesProvider { + args: FilesArgs, 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 raw_args = ctx.vim.provider_raw_args().await?; + let args = + FilesArgs::try_parse_from(std::iter::once("".to_string()).chain(raw_args.into_iter())) + .map_err(|err| { + let _ = ctx.vim.echo_warn(format!( + "using default {:?} due to {err}", + FilesArgs::default() + )); + }) + .unwrap_or_default(); Ok(Self { - hidden, - name_only, + args, searcher_control: None, }) } @@ -38,7 +52,7 @@ impl FilesProvider { 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 @@ -52,7 +66,7 @@ impl FilesProvider { let join_handle = { let search_context = ctx.search_context(stop_signal.clone()); 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; @@ -100,3 +114,35 @@ 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 { + hidden: true, + name_only: true + } + ); + + assert_eq!( + FilesArgs::parse_from(["", "--hidden"]), + FilesArgs { + hidden: true, + name_only: false + } + ); + + assert_eq!( + FilesArgs::parse_from(["", "--name-only"]), + FilesArgs { + hidden: false, + name_only: true + } + ); + } +} diff --git a/crates/maple_core/src/stdio_server/vim.rs b/crates/maple_core/src/stdio_server/vim.rs index 7e328ee0d..166795cac 100644 --- a/crates/maple_core/src/stdio_server/vim.rs +++ b/crates/maple_core/src/stdio_server/vim.rs @@ -400,11 +400,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 +419,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 } From d0b29e60ce90cfff97968dca77e49db8797cb876 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 21 Jun 2023 15:09:40 +0800 Subject: [PATCH 02/19] Parse grep args on Rust end --- autoload/clap/api.vim | 10 +++ .../src/stdio_server/provider/grep.rs | 70 ++++++++++++++++--- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/autoload/clap/api.vim b/autoload/clap/api.vim index 697abace1..05d334b42 100644 --- a/autoload/clap/api.vim +++ b/autoload/clap/api.vim @@ -139,6 +139,16 @@ function! s:api.show_lines_in_preview(lines) abort endif endfunction +function! s:api.set_initial_query(query) abort + if s:is_nvim + call feedkeys(a:query) + else + call g:clap.input.set(a:query) + " Move the cursor to the end. + call feedkeys("\", 'xt') + endif +endfunction + function! clap#api#call(method, args) abort " Catch all the exceptions try diff --git a/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index dc251847e..0cedf6da4 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -1,11 +1,21 @@ use crate::stdio_server::provider::{ClapProvider, Context, SearcherControl}; use anyhow::Result; +use clap::Parser; use matcher::MatchScope; +use serde_json::json; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::Arc; use types::Query; +#[derive(Debug, Parser, PartialEq, Eq, Default)] +struct GrepArgs { + #[clap(long)] + query: String, + #[clap(long)] + path: Vec, +} + #[derive(Debug)] pub struct GrepProvider { paths: Vec, @@ -59,14 +69,26 @@ impl GrepProvider { 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?; + let GrepArgs { query, path } = + GrepArgs::try_parse_from(std::iter::once(String::from("")).chain(raw_args.into_iter())) + .map_err(|err| { + let _ = ctx.vim.echo_warn(format!( + "using default {:?} due to {err}", + GrepArgs::default() + )); + }) + .unwrap_or_default(); + let query = if query.is_empty() { + ctx.vim.input_get().await? + } else { + let query = match query.as_str() { + "@visual" => ctx.vim.bare_call("clap#util#get_visual_selection").await?, + _ => ctx.vim.call("clap#util#expand", json!([query])).await?, + }; + ctx.vim.call("set_initial_query", json!([query])).await?; + query + }; + self.paths.extend(path); if !query.is_empty() { self.process_query(query, ctx); } @@ -92,3 +114,35 @@ 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 { + query: String::from("@visual"), + path: vec![PathBuf::from("~/.vim/plugged/vim-clap")] + } + ); + + assert_eq!( + GrepArgs::parse_from(["", "--query=@visual"]), + GrepArgs { + query: String::from("@visual"), + path: vec![] + } + ); + + assert_eq!( + GrepArgs::parse_from([""]), + GrepArgs { + query: String::default(), + path: vec![] + } + ); + } +} From 5c767ff2fe06e4353a4714e56bef3edcbc9d2c3e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 21 Jun 2023 15:35:19 +0800 Subject: [PATCH 03/19] Clean up files.vim and move initialization into files provider --- autoload/clap/provider/files.vim | 34 ------------------- .../src/stdio_server/handler/on_initialize.rs | 8 ----- .../src/stdio_server/provider/files.rs | 8 ++--- 3 files changed, 2 insertions(+), 48 deletions(-) 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/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/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index f82901b14..d62e2b79d 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -1,4 +1,3 @@ -use crate::stdio_server::handler::initialize_provider; use crate::stdio_server::provider::{ClapProvider, Context, SearcherControl}; use anyhow::Result; use clap::Parser; @@ -88,11 +87,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(()) } From 5ba7327972b01c86f6dcfe174a7e9d175746ca44 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 22 Jun 2023 10:56:14 +0800 Subject: [PATCH 04/19] Remove manual args parsing in vimscript and `apply_query` --- autoload/clap.vim | 44 ++-------------------------------- autoload/clap/api/clap.vim | 14 ----------- autoload/clap/floating_win.vim | 2 -- autoload/clap/popup.vim | 2 -- 4 files changed, 2 insertions(+), 60 deletions(-) diff --git a/autoload/clap.vim b/autoload/clap.vim index 8d027d20f..442031c12 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.raw_args = a:000[2:] else let provider_id_or_alias = a:1 - call s:parse_opts(a:000[1:]) + let g:clap.provider.raw_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/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/floating_win.vim b/autoload/clap/floating_win.vim index 932cd388d..5adbc49ce 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/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 From 1080d76705cb6e29d0f2bb85fdbb295e12cc75a2 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 22 Jun 2023 10:56:48 +0800 Subject: [PATCH 05/19] Remove unused `clap#spinner#set_raw` --- autoload/clap/spinner.vim | 12 ------------ 1 file changed, 12 deletions(-) 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() From e9c8562ee7b3893ec57525022a3b3825a45bd0f1 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 22 Jun 2023 10:58:00 +0800 Subject: [PATCH 06/19] Remove outdated docs --- README.md | 14 --- doc/clap-provider.txt | 208 ------------------------------------------ doc/clap.txt | 81 +--------------- 3 files changed, 2 insertions(+), 301 deletions(-) delete mode 100644 doc/clap-provider.txt diff --git a/README.md b/README.md index 13a3c4f1e..46dccbcff 100644 --- a/README.md +++ b/README.md @@ -105,19 +105,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 +143,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 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..886f82408 100644 --- a/doc/clap.txt +++ b/doc/clap.txt @@ -244,21 +244,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| @@ -400,20 +385,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| @@ -535,20 +506,6 @@ g:clap_builtin_fuzzy_filter_threshold *g:clap_builtin_fuzzy_filter_threshold 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| @@ -581,6 +538,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 +549,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 ------------------------------------------------------------------------------- @@ -858,23 +795,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* From b9aa47f3f1d8dd53beb81e6adfe9ef96d37d8551 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 24 Jun 2023 10:20:18 +0800 Subject: [PATCH 07/19] Integrate ProviderArgs into Provider --- crates/maple_core/Cargo.toml | 1 + .../stdio_server/provider/dumb_jump/mod.rs | 4 +- .../src/stdio_server/provider/files.rs | 61 +++++++++++++++---- .../src/stdio_server/provider/grep.rs | 44 +++++-------- .../src/stdio_server/provider/mod.rs | 45 +++++++++++++- crates/rpc/src/lib.rs | 4 +- 6 files changed, 111 insertions(+), 48 deletions(-) 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/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 d62e2b79d..4eadd2b49 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -2,16 +2,28 @@ 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; +use super::BaseArgs; + #[derive(Debug, Parser, PartialEq, Eq, Default)] 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, + + #[clap(long)] + path: Option, } #[derive(Debug)] @@ -22,16 +34,24 @@ pub struct FilesProvider { impl FilesProvider { pub async fn new(ctx: &Context) -> Result { - let raw_args = ctx.vim.provider_raw_args().await?; - let args = - FilesArgs::try_parse_from(std::iter::once("".to_string()).chain(raw_args.into_iter())) - .map_err(|err| { - let _ = ctx.vim.echo_warn(format!( - "using default {:?} due to {err}", - FilesArgs::default() - )); - }) - .unwrap_or_default(); + let mut args: FilesArgs = ctx.parse_provider_args().await?; + ctx.handle_base_args(&args.base).await?; + + let mut ignore_path_arg = false; + if let Some(path) = &args.path { + if !path.try_exists().unwrap_or(false) { + ignore_path_arg = true; + let _ = ctx.vim.echo_warn(format!( + "Ignore `--path {:?}` as it does not exist", + path.display() + )); + } + } + + if ignore_path_arg { + args.path.take(); + } + Ok(Self { args, searcher_control: None, @@ -63,7 +83,10 @@ impl FilesProvider { 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 let Some(dir) = &self.args.path { + search_context.paths = vec![dir.clone()]; + } let vim = ctx.vim.clone(); let hidden = self.args.hidden; tokio::spawn(async move { @@ -120,6 +143,8 @@ mod tests { assert_eq!( FilesArgs::parse_from(["", "--hidden", "--name-only"]), FilesArgs { + base: BaseArgs::default(), + path: None, hidden: true, name_only: true } @@ -128,6 +153,8 @@ mod tests { assert_eq!( FilesArgs::parse_from(["", "--hidden"]), FilesArgs { + base: BaseArgs::default(), + path: None, hidden: true, name_only: false } @@ -136,6 +163,18 @@ mod tests { assert_eq!( FilesArgs::parse_from(["", "--name-only"]), FilesArgs { + base: BaseArgs::default(), + path: None, + hidden: false, + name_only: true + } + ); + + assert_eq!( + FilesArgs::parse_from(["", "--path=/Users", "--name-only"]), + FilesArgs { + base: BaseArgs::default(), + path: Some(PathBuf::from("~")), hidden: false, name_only: true } diff --git a/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index 0cedf6da4..f0773f1d9 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -2,18 +2,20 @@ use crate::stdio_server::provider::{ClapProvider, Context, SearcherControl}; use anyhow::Result; use clap::Parser; use matcher::MatchScope; -use serde_json::json; 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)] struct GrepArgs { + #[clap(flatten)] + base: BaseArgs, + #[clap(long)] - query: String, - #[clap(long)] - path: Vec, + paths: Vec, } #[derive(Debug)] @@ -23,11 +25,11 @@ pub struct GrepProvider { } impl GrepProvider { - pub fn new() -> Self { - Self { + pub async fn new(_ctx: &Context) -> Result { + Ok(Self { paths: Vec::new(), searcher_control: None, - } + }) } fn process_query(&mut self, query: String, ctx: &Context) { @@ -68,29 +70,11 @@ 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?; - let GrepArgs { query, path } = - GrepArgs::try_parse_from(std::iter::once(String::from("")).chain(raw_args.into_iter())) - .map_err(|err| { - let _ = ctx.vim.echo_warn(format!( - "using default {:?} due to {err}", - GrepArgs::default() - )); - }) - .unwrap_or_default(); - let query = if query.is_empty() { - ctx.vim.input_get().await? - } else { - let query = match query.as_str() { - "@visual" => ctx.vim.bare_call("clap#util#get_visual_selection").await?, - _ => ctx.vim.call("clap#util#expand", json!([query])).await?, - }; - ctx.vim.call("set_initial_query", json!([query])).await?; - query - }; - self.paths.extend(path); - if !query.is_empty() { - self.process_query(query, ctx); + let GrepArgs { base, paths } = ctx.parse_provider_args().await?; + self.paths.extend(paths); + let initial_query = ctx.handle_base_args(&base).await?; + if !initial_query.is_empty() { + self.process_query(initial_query, ctx); } Ok(()) } diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index f0e7de095..5f0235f2a 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -33,13 +33,25 @@ 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 all providers opend in this session. + #[clap(long)] + cwd: Option, +} + 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 +359,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 +375,34 @@ impl Context { }) } + pub async fn parse_provider_args(&self) -> Result { + let raw_args = self.vim.provider_raw_args().await?; + let provider_args = if raw_args.is_empty() { + T::default() + } else { + T::try_parse_from(std::iter::once(String::from("")).chain(raw_args.into_iter())) + .map_err(|err| { + 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, cwd: _ } = 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 fn set_provider_source(&self, new: ProviderSource) { let mut provider_source = self.provider_source.write(); *provider_source = new; 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" ) From 443aae19ff5ee38e0be3b8e49637cf65e3d94f4a Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 24 Jun 2023 10:28:45 +0800 Subject: [PATCH 08/19] docs --- Cargo.lock | 1 + README.md | 9 +++++---- autoload/clap/api.vim | 12 ++++++++++-- .../src/stdio_server/provider/grep.rs | 18 ++++++++++++------ doc/clap.txt | 13 +++++-------- 5 files changed, 33 insertions(+), 20 deletions(-) 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/README.md b/README.md index 46dccbcff..b28f4e9bd 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ Vim-clap is a modern generic performant finder using the `floating_win` of neovi - [x] ~~Work out of the box, without any extra dependency~~. - The Rust binary is required to have a decent user experience. - [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. @@ -151,10 +152,10 @@ The paradigm is `Clap [provider_id_or_alias] {provider_args}`, where the `provid - 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. diff --git a/autoload/clap/api.vim b/autoload/clap/api.vim index 05d334b42..d374ec521 100644 --- a/autoload/clap/api.vim +++ b/autoload/clap/api.vim @@ -140,13 +140,21 @@ function! s:api.show_lines_in_preview(lines) abort 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(a:query) + call feedkeys(query) else - call g:clap.input.set(a:query) + 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 diff --git a/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index f0773f1d9..19f1e40b0 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -108,24 +108,30 @@ mod tests { assert_eq!( GrepArgs::parse_from(["", "--query=@visual", "--path=~/.vim/plugged/vim-clap"]), GrepArgs { - query: String::from("@visual"), - path: vec![PathBuf::from("~/.vim/plugged/vim-clap")] + 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 { - query: String::from("@visual"), - path: vec![] + base: BaseArgs { + query: Some(String::from("@visual")), + ..Default::default() + }, + paths: vec![] } ); assert_eq!( GrepArgs::parse_from([""]), GrepArgs { - query: String::default(), - path: vec![] + base: BaseArgs::default(), + paths: vec![] } ); } diff --git a/doc/clap.txt b/doc/clap.txt index 886f82408..0ff3bcfec 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. =============================================================================== From 58f5c31456bf475de6487e51cba063e22be913b0 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 24 Jun 2023 11:39:47 +0800 Subject: [PATCH 09/19] Display help when `-h, --help` is used --- autoload/clap/api.vim | 4 ++++ .../src/stdio_server/provider/files.rs | 2 ++ .../src/stdio_server/provider/grep.rs | 24 ++++++++++++++----- .../src/stdio_server/provider/mod.rs | 18 +++++++++++--- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/autoload/clap/api.vim b/autoload/clap/api.vim index d374ec521..87d5099f6 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 diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index 4eadd2b49..d66efd07b 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -10,6 +10,8 @@ use types::Query; 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, diff --git a/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index 19f1e40b0..ef594e5c8 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -10,12 +10,20 @@ 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, + /* + /// Do not search the current working directory and search this path only. #[clap(long)] - paths: Vec, + path_only: Option, + */ + /// Specify additional search paths apart from the current working directory. + #[clap(long)] + path: Vec, } #[derive(Debug)] @@ -70,8 +78,12 @@ impl GrepProvider { #[async_trait::async_trait] impl ClapProvider for GrepProvider { async fn on_initialize(&mut self, ctx: &mut Context) -> Result<()> { - let GrepArgs { base, paths } = ctx.parse_provider_args().await?; - self.paths.extend(paths); + let GrepArgs { base, path } = ctx.parse_provider_args().await?; + for p in path { + if let Ok(path) = ctx.vim.expand(p.to_string_lossy()).await { + self.paths.push(path.into()); + } + } let initial_query = ctx.handle_base_args(&base).await?; if !initial_query.is_empty() { self.process_query(initial_query, ctx); @@ -112,7 +124,7 @@ mod tests { query: Some(String::from("@visual")), ..Default::default() }, - paths: vec![PathBuf::from("~/.vim/plugged/vim-clap")] + path: vec![PathBuf::from("~/.vim/plugged/vim-clap")] } ); @@ -123,7 +135,7 @@ mod tests { query: Some(String::from("@visual")), ..Default::default() }, - paths: vec![] + path: vec![] } ); @@ -131,7 +143,7 @@ mod tests { GrepArgs::parse_from([""]), GrepArgs { base: BaseArgs::default(), - paths: vec![] + path: vec![] } ); } diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index 5f0235f2a..4bd139df8 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -377,14 +377,26 @@ impl Context { pub async fn parse_provider_args(&self) -> Result { let raw_args = self.vim.provider_raw_args().await?; + let provider_args = if raw_args.is_empty() { T::default() } else { T::try_parse_from(std::iter::once(String::from("")).chain(raw_args.into_iter())) .map_err(|err| { - let _ = self - .vim - .echo_warn(format!("using default {:?} due to {err}", T::default())); + 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() }; From 56fc835232ad496f6e52c8a3a1924eb7d068b6b9 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 25 Jun 2023 08:17:28 +0800 Subject: [PATCH 10/19] Remove deprecated flags --- autoload/clap/provider/blines.vim | 2 - .../src/stdio_server/provider/grep.rs | 46 ++++++----- .../src/stdio_server/provider/mod.rs | 8 +- doc/clap.txt | 79 ++++++------------- 4 files changed, 53 insertions(+), 82 deletions(-) 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/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index ef594e5c8..147785e44 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -16,26 +16,32 @@ struct GrepArgs { #[clap(flatten)] base: BaseArgs, - /* - /// Do not search the current working directory and search this path only. - #[clap(long)] - path_only: Option, - */ /// Specify additional search paths apart from the current working directory. - #[clap(long)] - path: Vec, + #[clap(long = "path")] + paths: Vec, } #[derive(Debug)] pub struct GrepProvider { - paths: Vec, + args: GrepArgs, searcher_control: Option, } impl GrepProvider { - pub async fn new(_ctx: &Context) -> Result { + pub async fn new(ctx: &Context) -> Result { + let GrepArgs { base, paths } = ctx.parse_provider_args().await?; + let mut expanded_paths = Vec::with_capacity(paths.len()); + for p in paths { + if let Ok(path) = ctx.vim.expand(p.to_string_lossy()).await { + expanded_paths.push(path.into()); + } + } + Ok(Self { - paths: Vec::new(), + args: GrepArgs { + base, + paths: expanded_paths, + }, searcher_control: None, }) } @@ -58,7 +64,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; @@ -78,13 +88,7 @@ impl GrepProvider { #[async_trait::async_trait] impl ClapProvider for GrepProvider { async fn on_initialize(&mut self, ctx: &mut Context) -> Result<()> { - let GrepArgs { base, path } = ctx.parse_provider_args().await?; - for p in path { - if let Ok(path) = ctx.vim.expand(p.to_string_lossy()).await { - self.paths.push(path.into()); - } - } - let initial_query = ctx.handle_base_args(&base).await?; + let initial_query = ctx.handle_base_args(&self.args.base).await?; if !initial_query.is_empty() { self.process_query(initial_query, ctx); } @@ -124,7 +128,7 @@ mod tests { query: Some(String::from("@visual")), ..Default::default() }, - path: vec![PathBuf::from("~/.vim/plugged/vim-clap")] + paths: vec![PathBuf::from("~/.vim/plugged/vim-clap")] } ); @@ -135,7 +139,7 @@ mod tests { query: Some(String::from("@visual")), ..Default::default() }, - path: vec![] + paths: vec![] } ); @@ -143,7 +147,7 @@ mod tests { GrepArgs::parse_from([""]), GrepArgs { base: BaseArgs::default(), - path: vec![] + 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 4bd139df8..cff7b6ea9 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -40,9 +40,13 @@ pub struct BaseArgs { #[clap(long)] query: Option, - /// Specify the working directory for all providers opend in this session. + /// 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> { @@ -404,7 +408,7 @@ impl Context { } pub async fn handle_base_args(&self, base: &BaseArgs) -> Result { - let BaseArgs { query, cwd: _ } = base; + let BaseArgs { query, .. } = base; let query = if let Some(query) = query { self.vim.call("set_initial_query", json!([query])).await? diff --git a/doc/clap.txt b/doc/clap.txt index 0ff3bcfec..4a84375fd 100644 --- a/doc/clap.txt +++ b/doc/clap.txt @@ -181,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| @@ -208,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': '*' }` @@ -347,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| @@ -482,44 +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_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| @@ -554,23 +525,17 @@ g:clap_background_shadow_blend *g:clap_background_shadow_blend* 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* From 69a40e1cf1fea4de1907af4f2f67707ad89e8f49 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 25 Jun 2023 08:27:01 +0800 Subject: [PATCH 11/19] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ crates/maple_core/src/stdio_server/provider/files.rs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6721b25b..c35bebdfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## [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` and `--opt` in a consistent way. Ref to #981 for upgrade guide. + ## [0.44] 2023-05-27 diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index d66efd07b..869f881cd 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -173,7 +173,7 @@ mod tests { ); assert_eq!( - FilesArgs::parse_from(["", "--path=/Users", "--name-only"]), + FilesArgs::parse_from(["", "--path=~", "--name-only"]), FilesArgs { base: BaseArgs::default(), path: Some(PathBuf::from("~")), From 46041c9ceb58314754638ffc760e3e944005a384 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 25 Jun 2023 11:06:01 +0800 Subject: [PATCH 12/19] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b28f4e9bd..b35ecd835 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ 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] Consistent command interface with [clap-rs/clap](https://github.com/clap-rs/clap) - [x] Support writing new providers in both Vimscript and Rust. @@ -303,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" ``` From 8c3d01db0fa8de4a9432744d4183ea86829b0b3e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 25 Jun 2023 11:45:17 +0800 Subject: [PATCH 13/19] Fixes --- .../src/stdio_server/provider/files.rs | 34 +++++++------------ .../src/stdio_server/provider/grep.rs | 9 +---- .../src/stdio_server/provider/mod.rs | 10 ++++++ 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index 869f881cd..1ff7bb13b 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -24,8 +24,9 @@ struct FilesArgs { #[clap(long)] name_only: bool, - #[clap(long)] - path: Option, + /// Specify additional search paths apart from the current working directory. + #[clap(long = "path")] + paths: Vec, } #[derive(Debug)] @@ -36,26 +37,15 @@ pub struct FilesProvider { impl FilesProvider { pub async fn new(ctx: &Context) -> Result { - let mut args: FilesArgs = ctx.parse_provider_args().await?; + let args: FilesArgs = ctx.parse_provider_args().await?; ctx.handle_base_args(&args.base).await?; - let mut ignore_path_arg = false; - if let Some(path) = &args.path { - if !path.try_exists().unwrap_or(false) { - ignore_path_arg = true; - let _ = ctx.vim.echo_warn(format!( - "Ignore `--path {:?}` as it does not exist", - path.display() - )); - } - } - - if ignore_path_arg { - args.path.take(); - } - + let expanded_paths = ctx.expanded_paths(&args.paths).await?; Ok(Self { - args, + args: FilesArgs { + paths: expanded_paths, + ..args + }, searcher_control: None, }) } @@ -86,8 +76,10 @@ impl FilesProvider { let join_handle = { let mut search_context = ctx.search_context(stop_signal.clone()); - if let Some(dir) = &self.args.path { - search_context.paths = vec![dir.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.args.hidden; diff --git a/crates/maple_core/src/stdio_server/provider/grep.rs b/crates/maple_core/src/stdio_server/provider/grep.rs index 147785e44..4d2670957 100644 --- a/crates/maple_core/src/stdio_server/provider/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/grep.rs @@ -30,17 +30,10 @@ pub struct GrepProvider { impl GrepProvider { pub async fn new(ctx: &Context) -> Result { let GrepArgs { base, paths } = ctx.parse_provider_args().await?; - let mut expanded_paths = Vec::with_capacity(paths.len()); - for p in paths { - if let Ok(path) = ctx.vim.expand(p.to_string_lossy()).await { - expanded_paths.push(path.into()); - } - } - Ok(Self { args: GrepArgs { base, - paths: expanded_paths, + paths: ctx.expanded_paths(&paths).await?, }, searcher_control: None, }) diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index cff7b6ea9..07acb3c95 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -419,6 +419,16 @@ impl Context { 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; From 194aef31772733e498231c4f146cb440fc17bc51 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 25 Jun 2023 13:12:12 +0800 Subject: [PATCH 14/19] Fixes --- .../src/stdio_server/provider/files.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index 1ff7bb13b..bbe2c15ed 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -138,9 +138,9 @@ mod tests { FilesArgs::parse_from(["", "--hidden", "--name-only"]), FilesArgs { base: BaseArgs::default(), - path: None, hidden: true, - name_only: true + name_only: true, + paths: vec![], } ); @@ -148,9 +148,9 @@ mod tests { FilesArgs::parse_from(["", "--hidden"]), FilesArgs { base: BaseArgs::default(), - path: None, hidden: true, - name_only: false + name_only: false, + paths: vec![], } ); @@ -158,9 +158,9 @@ mod tests { FilesArgs::parse_from(["", "--name-only"]), FilesArgs { base: BaseArgs::default(), - path: None, hidden: false, - name_only: true + name_only: true, + paths: vec![], } ); @@ -168,9 +168,9 @@ mod tests { FilesArgs::parse_from(["", "--path=~", "--name-only"]), FilesArgs { base: BaseArgs::default(), - path: Some(PathBuf::from("~")), hidden: false, - name_only: true + name_only: true, + paths: vec![PathBuf::from("~")], } ); } From 3b6d9ac65e687306c3ad7f10d88dfcd106e18c7e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Wed, 28 Jun 2023 20:43:57 +0800 Subject: [PATCH 15/19] Store recent n files in FilesProvider --- .../maple_core/src/stdio_server/provider/files.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/maple_core/src/stdio_server/provider/files.rs b/crates/maple_core/src/stdio_server/provider/files.rs index bbe2c15ed..5362ececf 100644 --- a/crates/maple_core/src/stdio_server/provider/files.rs +++ b/crates/maple_core/src/stdio_server/provider/files.rs @@ -32,6 +32,7 @@ struct FilesArgs { #[derive(Debug)] pub struct FilesProvider { args: FilesArgs, + recent_files_bonus: Bonus, searcher_control: Option, } @@ -41,11 +42,18 @@ impl FilesProvider { 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 { args: FilesArgs { paths: expanded_paths, ..args }, + recent_files_bonus, searcher_control: None, }) } @@ -57,10 +65,6 @@ 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.args.name_only { @@ -68,7 +72,7 @@ impl FilesProvider { } else { MatchScope::Full }) - .bonuses(vec![recent_files_bonus]) + .bonuses(vec![self.recent_files_bonus.clone()]) .build(Query::from(&query)); let new_control = { From 9c9f3db380f6c7bda162f711e20efd28f4b029bb Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 30 Jun 2023 10:38:04 +0800 Subject: [PATCH 16/19] Remove raw_provider_args() Use provider_args() for simplicity --- autoload/clap.vim | 4 ++-- autoload/clap/api.vim | 4 ---- autoload/clap/file_explorer.vim | 1 + crates/maple_core/src/stdio_server/provider/mod.rs | 6 +++--- crates/maple_core/src/stdio_server/vim.rs | 4 ---- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/autoload/clap.vim b/autoload/clap.vim index 442031c12..4e85c0e01 100644 --- a/autoload/clap.vim +++ b/autoload/clap.vim @@ -335,10 +335,10 @@ function! clap#(bang, ...) abort if a:1 ==# '!' let g:clap.context['no-cache'] = v:true let provider_id_or_alias = a:2 - let g:clap.provider.raw_args = a:000[2:] + let g:clap.provider.args = a:000[2:] else let provider_id_or_alias = a:1 - let g:clap.provider.raw_args = a:000[1:] + let g:clap.provider.args = a:000[1:] endif endif diff --git a/autoload/clap/api.vim b/autoload/clap/api.vim index 87d5099f6..e70e6725b 100644 --- a/autoload/clap/api.vim +++ b/autoload/clap/api.vim @@ -102,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 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/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index 07acb3c95..f4146e060 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -380,12 +380,12 @@ impl Context { } pub async fn parse_provider_args(&self) -> Result { - let raw_args = self.vim.provider_raw_args().await?; + let args = self.vim.provider_args().await?; - let provider_args = if raw_args.is_empty() { + let provider_args = if args.is_empty() { T::default() } else { - T::try_parse_from(std::iter::once(String::from("")).chain(raw_args.into_iter())) + T::try_parse_from(std::iter::once(String::from("")).chain(args.into_iter())) .map_err(|err| { match err.kind() { clap::error::ErrorKind::DisplayHelp => { diff --git a/crates/maple_core/src/stdio_server/vim.rs b/crates/maple_core/src/stdio_server/vim.rs index 166795cac..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 } From 0def0b80ddb02b3f038e81e16c0318abcc52a797 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 30 Jun 2023 10:38:36 +0800 Subject: [PATCH 17/19] Delete PROVIDER.md Outdated anyway --- PROVIDER.md | 196 ---------------------------------------------------- 1 file changed, 196 deletions(-) delete mode 100644 PROVIDER.md 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. From 22bc58b03b1fd947476651043e55b967392d8bee Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 30 Jun 2023 21:53:43 +0800 Subject: [PATCH 18/19] TODO: Proper completion help --- autoload/clap/helper.vim | 1 + 1 file changed, 1 insertion(+) 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') From 21cdee69ace97a6651e477c8609eda67cad5eb67 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 1 Jul 2023 13:04:36 +0800 Subject: [PATCH 19/19] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c35bebdfe..8dbd556f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,7 @@ ### Changed -- `++opt` and `+opt` have been replaced with `--opt value` and `--opt` in a consistent way. Ref to #981 for upgrade guide. - +- `++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 @@ -22,6 +21,7 @@ ## [0.40] 2023-01-27 ## [0.39] 2023-01-13 + ## [0.38] 2023-01-08 ### Added