From 097d2dbf55cf57816eed82a02af71b843f14a292 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 4 May 2024 10:50:11 +0800 Subject: [PATCH 1/8] Update deps --- Cargo.lock | 141 ++++++++++-------- Cargo.toml | 7 +- crates/cli/src/command/rpc.rs | 2 + crates/maple_config/doc_gen/Cargo.toml | 6 +- .../maple_config/doc_gen/default_config.toml | 1 + crates/maple_config/doc_gen/src/main.rs | 29 ++-- crates/maple_core/src/stdio_server/service.rs | 22 ++- crates/maple_core/src/tools/rg/jsont.rs | 4 +- crates/printer/Cargo.toml | 4 +- crates/tree_sitter/src/language.rs | 4 +- docs/src/plugins/config.md | 1 + 11 files changed, 123 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a03a20d49..6a3f91050 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,7 +109,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -126,7 +126,7 @@ checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", - "base64 0.21.7", + "base64", "bytes", "futures-util", "http 1.1.0", @@ -214,12 +214,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -324,7 +318,7 @@ checksum = "031718ddb8f78aa5def78a09e90defe30151d1f6c672f937af4dd916429ed996" dependencies = [ "semver", "serde", - "toml", + "toml 0.5.11", "url", ] @@ -461,7 +455,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -520,7 +514,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "toml", + "toml 0.8.12", "tracing", "which", ] @@ -713,7 +707,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.52", + "syn", ] [[package]] @@ -724,7 +718,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -808,8 +802,8 @@ dependencies = [ "itertools 0.12.1", "maple_config", "quote", - "syn 1.0.109", - "toml", + "syn", + "toml 0.8.12", "toml_edit", ] @@ -966,7 +960,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -1173,7 +1167,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "headers-core", "http 1.1.0", @@ -1566,7 +1560,18 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.2", "libc", - "redox_syscall 0.4.1", + "redox_syscall", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", ] [[package]] @@ -1676,7 +1681,7 @@ dependencies = [ "paths", "serde", "serde_json", - "toml", + "toml 0.8.12", "types", ] @@ -1685,7 +1690,7 @@ name = "maple_core" version = "0.1.53" dependencies = [ "async-trait", - "base64 0.13.1", + "base64", "chrono", "chrono-humanize", "clap", @@ -1717,12 +1722,12 @@ dependencies = [ "rpc", "serde", "serde_json", - "strsim 0.10.0", + "strsim 0.11.0", "sublime_syntax", "subprocess", "thiserror", "tokio", - "toml", + "toml 0.8.12", "tracing", "tree_sitter", "types", @@ -1740,7 +1745,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn", "types", ] @@ -1758,7 +1763,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "toml", + "toml 0.8.12", "tracing", "which", ] @@ -2043,7 +2048,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -2090,7 +2095,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2117,7 +2122,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ - "base64 0.21.7", + "base64", "indexmap", "line-wrap", "quick-xml", @@ -2283,15 +2288,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -2314,7 +2310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "libredox", + "libredox 0.0.1", "thiserror", ] @@ -2368,7 +2364,7 @@ version = "0.11.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "encoding_rs", "futures-core", @@ -2472,7 +2468,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.7", + "base64", ] [[package]] @@ -2554,7 +2550,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2586,7 +2582,16 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", ] [[package]] @@ -2708,17 +2713,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.52" @@ -2787,13 +2781,13 @@ dependencies = [ [[package]] name = "termion" -version = "1.5.6" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" +checksum = "417813675a504dfbbf21bfde32c03e5bf9f2413999962b479023c02848c1c7a5" dependencies = [ "libc", + "libredox 0.0.2", "numtoa", - "redox_syscall 0.2.16", "redox_termios", ] @@ -2814,7 +2808,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2930,7 +2924,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -2978,19 +2972,36 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -3055,7 +3066,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", ] [[package]] @@ -3257,7 +3268,7 @@ dependencies = [ "once_cell", "rand", "serde", - "toml", + "toml 0.8.12", "tracing", "tree-sitter", "tree-sitter-bash", @@ -3470,7 +3481,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn", "wasm-bindgen-shared", ] @@ -3504,7 +3515,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3801,9 +3812,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 21f03195b..ea83c6993 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ default-members = ["crates/maple"] [workspace.dependencies] anyhow = "1.0" async-trait = "0.1" -base64 = "0.13" +base64 = "0.21" bytecount = { version = "0.6", features = ["runtime-dispatch-simd"] } cargo_metadata = "0.18.0" chrono = { version = "0.4", features = ["serde"] } @@ -75,12 +75,13 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" shellexpand = "3.1.0" simdutf8 = "0.1" -strsim = "0.10.0" +strsim = "0.11" syntect = "5.0.0" subprocess = { git = "https://github.com/hniksic/rust-subprocess" } +termion = "3" thiserror = "1.0" tokio = "1.36" -toml = "0.5" +toml = "0.8" tracing = "0.1" tracing-appender = "0.2" tracing-subscriber = "0.3" diff --git a/crates/cli/src/command/rpc.rs b/crates/cli/src/command/rpc.rs index 93941d4f2..91d4705ef 100644 --- a/crates/cli/src/command/rpc.rs +++ b/crates/cli/src/command/rpc.rs @@ -4,6 +4,7 @@ use clap::Parser; use maple_core::stdio_server::ConfigError; use std::io::IsTerminal; use tracing_subscriber::filter::EnvFilter; +use tracing_subscriber::fmt::format::FmtSpan; /// Starts a RPC service using stdio. #[derive(Parser, Debug, Clone)] @@ -68,6 +69,7 @@ impl Rpc { } let subscriber = tracing_subscriber::FmtSubscriber::builder() + .with_span_events(FmtSpan::FULL) .with_max_level(max_level) .with_env_filter(env_filter) .with_line_number(true) diff --git a/crates/maple_config/doc_gen/Cargo.toml b/crates/maple_config/doc_gen/Cargo.toml index ca2e2dd98..8e03466b5 100644 --- a/crates/maple_config/doc_gen/Cargo.toml +++ b/crates/maple_config/doc_gen/Cargo.toml @@ -7,8 +7,8 @@ description = "Generate config markdown document from the inline docs" [dependencies] inflections = "1.1.1" maple_config = { path = "../" } -toml = "0.5" -syn = { version = "1", features = ["full"] } +toml = "0.8" +syn = { version = "2", features = ["full"] } quote = "1" itertools = "0.12.0" -toml_edit = { version = "0.21.0", features = [ "parse" ] } +toml_edit = { version = "0.22", features = [ "parse" ] } diff --git a/crates/maple_config/doc_gen/default_config.toml b/crates/maple_config/doc_gen/default_config.toml index 4eeb9d22d..63c596cab 100644 --- a/crates/maple_config/doc_gen/default_config.toml +++ b/crates/maple_config/doc_gen/default_config.toml @@ -23,6 +23,7 @@ file-path-style = "one-segment-per-component" [matcher] # Specify how the results are sorted. tiebreak = "score,-begin,-end,-length" + [plugin.colorizer] # Whether to enable this plugin. enable = false diff --git a/crates/maple_config/doc_gen/src/main.rs b/crates/maple_config/doc_gen/src/main.rs index 3292de21b..f3fbd4d28 100644 --- a/crates/maple_config/doc_gen/src/main.rs +++ b/crates/maple_config/doc_gen/src/main.rs @@ -5,9 +5,9 @@ use quote::ToTokens; use std::collections::BTreeMap; use std::str::FromStr; use syn::{ - Attribute, Field, Fields, ItemEnum, ItemStruct, Lit, Meta, MetaNameValue, PathSegment, Type, + Attribute, Expr, Field, Fields, ItemEnum, ItemStruct, Lit, MetaNameValue, PathSegment, Type, }; -use toml_edit::Document; +use toml_edit::DocumentMut; fn main() { let source_code = include_str!("../../src/lib.rs"); @@ -99,14 +99,13 @@ impl FieldInfo { } fn extract_doc_comment(attr: &Attribute) -> Option { - if let Ok(Meta::NameValue(MetaNameValue { - path, - lit: Lit::Str(comment), - .. - })) = attr.parse_meta() - { - if path.is_ident("doc") { - return Some(comment.value()); + if let Ok(MetaNameValue { path, value, .. }) = attr.meta.require_name_value() { + if let Expr::Lit(expr_lit) = value { + if let Lit::Str(comment) = &expr_lit.lit { + if path.is_ident("doc") { + return Some(comment.value()); + } + } } } None @@ -178,7 +177,7 @@ fn parse_enum(e: &ItemEnum) -> BTreeMap { /// Conventions: /// - All structs with a suffix of `Config` are considered as part of the config file. /// - `Config` struct is the entry of various configs. -fn process_ast(ast: &syn::File) -> Document { +fn process_ast(ast: &syn::File) -> DocumentMut { let mut all_struct_docs = BTreeMap::new(); // Traverse the AST and perform actions on each struct. @@ -200,7 +199,7 @@ fn process_ast(ast: &syn::File) -> Document { // Inject the extracted docs into the default toml config. let default_config_toml = toml::to_string(&Config::default()).expect("Failed to convert Config::default() to toml"); - let mut doc = toml_edit::Document::from_str(&default_config_toml) + let mut doc = toml_edit::DocumentMut::from_str(&default_config_toml) .expect("Must be valid toml as it was just constructed internally"); // Iterate the fields in Config: log, matcher, plugin, provider, global_ignore @@ -250,7 +249,7 @@ fn process_ast(ast: &syn::File) -> Document { t.decor_mut().set_prefix(format!("\n{comments}\n")); } } else { - t_key.decor_mut().set_prefix(format!("{comments}\n")); + t_key.leaf_decor_mut().set_prefix(format!("{comments}\n")); } } } @@ -279,12 +278,12 @@ fn process_ast(ast: &syn::File) -> Document { if let Some(t) = t_item.as_table_mut() { t.decor_mut().set_prefix(format!("\n{comments}\n")); } else if t_item.is_value() { - t_key.decor_mut().set_prefix(format!("{comments}\n")); + t_key.leaf_decor_mut().set_prefix(format!("{comments}\n")); } } } } else if t_item.is_value() { - t_key.decor_mut().set_prefix(format!("{comments}\n")); + t_key.leaf_decor_mut().set_prefix(format!("{comments}\n")); } } } diff --git a/crates/maple_core/src/stdio_server/service.rs b/crates/maple_core/src/stdio_server/service.rs index df0d8b447..38bcc627c 100644 --- a/crates/maple_core/src/stdio_server/service.rs +++ b/crates/maple_core/src/stdio_server/service.rs @@ -14,6 +14,7 @@ use std::ops::ControlFlow; use std::time::Duration; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::time::Instant; +use tracing::Instrument; pub type ProviderSessionId = u64; @@ -209,22 +210,29 @@ impl ProviderSession { if let Some(_params) = on_move.take() { on_move_timer.as_mut().reset(Instant::now() + NEVER); - if let Err(err) = self.provider.on_move(&mut self.ctx).await { - tracing::error!(?err, "Failed to process ProviderEvent::OnMove"); + async { + if let Err(err) = self.provider.on_move(&mut self.ctx).await { + tracing::error!(?err, "Failed to process ProviderEvent::OnMove"); + } } + .instrument(tracing::info_span!("process_on_move")).await } } _ = on_typed_timer.as_mut(), if on_typed.is_some() => { if let Some(_params) = on_typed.take() { on_typed_timer.as_mut().reset(Instant::now() + NEVER); - let _ = self.ctx.record_input().await; + let process_on_typed = async { + let _ = self.ctx.record_input().await; - if let Err(err) = self.provider.on_typed(&mut self.ctx).await { - tracing::error!(?err, "Failed to process ProviderEvent::OnTyped"); - } + if let Err(err) = self.provider.on_typed(&mut self.ctx).await { + tracing::error!(?err, "Failed to process ProviderEvent::OnTyped"); + } + + let _ = self.provider.on_move(&mut self.ctx).await; + }; - let _ = self.provider.on_move(&mut self.ctx).await; + process_on_typed.instrument(tracing::info_span!("process_on_typed")).await } } } diff --git a/crates/maple_core/src/tools/rg/jsont.rs b/crates/maple_core/src/tools/rg/jsont.rs index cca068967..b84aa12cc 100644 --- a/crates/maple_core/src/tools/rg/jsont.rs +++ b/crates/maple_core/src/tools/rg/jsont.rs @@ -1,4 +1,6 @@ use super::stats::Stats; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; use serde::Deserialize; use std::borrow::Cow; @@ -85,7 +87,7 @@ where D: serde::Deserializer<'de>, { let s: &str = serde::Deserialize::deserialize(deserializer)?; - base64::decode(s).map_err(serde::de::Error::custom) + STANDARD.decode(s).map_err(serde::de::Error::custom) } #[cfg(test)] diff --git a/crates/printer/Cargo.toml b/crates/printer/Cargo.toml index 001d63b99..c4f657fb8 100644 --- a/crates/printer/Cargo.toml +++ b/crates/printer/Cargo.toml @@ -18,6 +18,6 @@ types = { workspace = true } utils = { workspace = true } [target.'cfg(not(windows))'.dev-dependencies] -rayon = { workspace = true } -termion = "1.5.1" filter = { workspace = true } +rayon = { workspace = true } +termion = { workspace = true } diff --git a/crates/tree_sitter/src/language.rs b/crates/tree_sitter/src/language.rs index 445726712..b6a15d197 100644 --- a/crates/tree_sitter/src/language.rs +++ b/crates/tree_sitter/src/language.rs @@ -31,8 +31,8 @@ struct ConfigInner { } static CONFIG: Lazy = Lazy::new(|| { - let tree_sitter_config = include_bytes!("../tree_sitter_config.toml"); - let config: Config = toml::from_slice(tree_sitter_config).unwrap(); + let tree_sitter_config = include_str!("../tree_sitter_config.toml"); + let config: Config = toml::from_str(tree_sitter_config).unwrap(); ConfigInner { language: config .language diff --git a/docs/src/plugins/config.md b/docs/src/plugins/config.md index 4ecc85a92..504db1c69 100644 --- a/docs/src/plugins/config.md +++ b/docs/src/plugins/config.md @@ -34,6 +34,7 @@ file-path-style = "one-segment-per-component" [matcher] # Specify how the results are sorted. tiebreak = "score,-begin,-end,-length" + [plugin.colorizer] # Whether to enable this plugin. enable = false From 4d0d078f4156a6a7b6a574c22308e1a19e0f80da Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 9 May 2024 16:55:49 +0800 Subject: [PATCH 2/8] Investigate on_move impl --- .../src/searcher/grep/stoppable_searcher.rs | 7 +- crates/maple_core/src/stdio_server/input.rs | 4 +- .../src/stdio_server/provider/impls/blines.rs | 20 +++-- .../src/stdio_server/provider/impls/grep.rs | 1 + .../src/stdio_server/provider/impls/lsp.rs | 9 +- .../provider/impls/recent_files.rs | 21 +++-- .../src/stdio_server/provider/mod.rs | 8 +- crates/maple_core/src/stdio_server/service.rs | 90 +++++++++++++------ 8 files changed, 111 insertions(+), 49 deletions(-) diff --git a/crates/maple_core/src/searcher/grep/stoppable_searcher.rs b/crates/maple_core/src/searcher/grep/stoppable_searcher.rs index 6cb9c0c04..e20ac8e44 100644 --- a/crates/maple_core/src/searcher/grep/stoppable_searcher.rs +++ b/crates/maple_core/src/searcher/grep/stoppable_searcher.rs @@ -360,18 +360,21 @@ pub async fn search(query: String, matcher: Matcher, search_context: SearchConte return; } - let elapsed = now.elapsed().as_millis(); + let searching_elapsed = now.elapsed().as_millis(); let display_lines = to_display_lines(&best_results.results, icon); let total_matched = search_info.total_matched.load(Ordering::SeqCst); let total_processed = search_info.total_processed.load(Ordering::SeqCst); + + let now = std::time::Instant::now(); progressor.on_finished(display_lines, total_matched, total_processed); + let ui_update_elapsed = now.elapsed().as_millis(); tracing::debug!( total_processed, total_matched, ?query, - "Searching completed in {elapsed:?}ms" + "Searching completed in {searching_elapsed:?}ms, UI updated in {ui_update_elapsed:?}ms" ); } diff --git a/crates/maple_core/src/stdio_server/input.rs b/crates/maple_core/src/stdio_server/input.rs index 4c918531b..1f16dfa78 100644 --- a/crates/maple_core/src/stdio_server/input.rs +++ b/crates/maple_core/src/stdio_server/input.rs @@ -36,7 +36,7 @@ impl PluginEvent { } /// Provider specific events. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ProviderEvent { OnMove(Params), OnTyped(Params), @@ -47,7 +47,7 @@ pub enum ProviderEvent { Internal(InternalProviderEvent), } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum InternalProviderEvent { Initialize, InitialQuery(String), diff --git a/crates/maple_core/src/stdio_server/provider/impls/blines.rs b/crates/maple_core/src/stdio_server/provider/impls/blines.rs index 361fe5feb..55eae05de 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/blines.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/blines.rs @@ -166,14 +166,18 @@ impl ClapProvider for BlinesProvider { if !ctx.env.preview_enabled { return Ok(()); } - ctx.preview_manager.reset_scroll(); - let curline = ctx.vim.display_getcurline().await?; - let Some(line_number) = pattern::extract_blines_lnum(&curline) else { - return Ok(()); - }; - let preview_target = - PreviewTarget::location_in_file(self.preview_file.clone(), line_number); - ctx.update_preview(Some(preview_target)).await + let mut ctx = ctx.clone(); + let preview_file = self.preview_file.clone(); + tokio::spawn(async move { + ctx.preview_manager.reset_scroll(); + let curline = ctx.vim.display_getcurline().await?; + let Some(line_number) = pattern::extract_blines_lnum(&curline) else { + return Ok(()); + }; + let preview_target = PreviewTarget::location_in_file(preview_file, line_number); + ctx.update_preview(Some(preview_target)).await + }); + Ok(()) } async fn on_typed(&mut self, ctx: &mut Context) -> Result<()> { diff --git a/crates/maple_core/src/stdio_server/provider/impls/grep.rs b/crates/maple_core/src/stdio_server/provider/impls/grep.rs index 907c39bea..f92c18e44 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/grep.rs @@ -80,6 +80,7 @@ impl ClapProvider for GrepProvider { async fn on_typed(&mut self, ctx: &mut Context) -> Result<()> { let query = ctx.vim.input_get().await?; + tracing::debug!("============= procesing query: {query}"); if query.is_empty() { ctx.update_on_empty_query().await?; } else { diff --git a/crates/maple_core/src/stdio_server/provider/impls/lsp.rs b/crates/maple_core/src/stdio_server/provider/impls/lsp.rs index 2f3dc2d2e..7b35ff637 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/lsp.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/lsp.rs @@ -433,13 +433,16 @@ impl ClapProvider for LspProvider { if !ctx.env.preview_enabled { return Ok(()); } - ctx.preview_manager.reset_scroll(); - let line_number = ctx.vim.display_getcurlnum().await?; let loc = self .fetch_location_at(line_number - 1) .ok_or(ProviderError::PreviewItemNotFound { line_number })?; - ctx.update_preview(Some(loc.into_preview_target())).await + let mut ctx = ctx.clone(); + tokio::spawn(async move { + ctx.preview_manager.reset_scroll(); + ctx.update_preview(Some(loc.into_preview_target())).await; + }); + Ok(()) } async fn remote_sink(&mut self, ctx: &mut Context, line_numbers: Vec) -> Result<()> { diff --git a/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs b/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs index f69701851..d532d7fa9 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs @@ -1,6 +1,8 @@ use crate::datastore::RECENT_FILES_IN_MEMORY; use crate::stdio_server::provider::hooks::CachedPreviewImpl; -use crate::stdio_server::provider::{BaseArgs, ClapProvider, Context, ProviderResult as Result}; +use crate::stdio_server::provider::{ + BaseArgs, ClapProvider, Context, ProviderError, ProviderResult as Result, +}; use parking_lot::Mutex; use paths::AbsPathBuf; use printer::Printer; @@ -167,12 +169,17 @@ impl ClapProvider for RecentFilesProvider { if let Some(curline) = maybe_curline { let preview_height = ctx.preview_height().await?; - let (preview_target, preview) = CachedPreviewImpl::new(curline, preview_height, ctx)? - .get_preview() - .await?; - ctx.preview_manager.reset_scroll(); - ctx.update_picker_preview(preview)?; - ctx.preview_manager.set_preview_target(preview_target); + let mut ctx = ctx.clone(); + tokio::spawn(async move { + let (preview_target, preview) = + CachedPreviewImpl::new(curline, preview_height, &ctx)? + .get_preview() + .await?; + ctx.preview_manager.reset_scroll(); + ctx.update_picker_preview(preview)?; + ctx.preview_manager.set_preview_target(preview_target); + Ok::<(), ProviderError>(()) + }); } Ok(()) diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index aeb4dcbe1..6cb25de75 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -872,8 +872,12 @@ pub trait ClapProvider: Debug + Send + Sync + 'static { if !ctx.env.preview_enabled { return Ok(()); } - ctx.preview_manager.reset_scroll(); - ctx.update_preview(None).await + let mut ctx = ctx.clone(); + tokio::spawn(async move { + ctx.preview_manager.reset_scroll(); + let _ = ctx.update_preview(None).await; + }); + Ok(()) } async fn on_typed(&mut self, ctx: &mut Context) -> ProviderResult<()>; diff --git a/crates/maple_core/src/stdio_server/service.rs b/crates/maple_core/src/stdio_server/service.rs index 38bcc627c..cca4948de 100644 --- a/crates/maple_core/src/stdio_server/service.rs +++ b/crates/maple_core/src/stdio_server/service.rs @@ -11,6 +11,8 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; use std::fmt::Debug; use std::ops::ControlFlow; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use tokio::time::Instant; @@ -29,6 +31,8 @@ pub struct ProviderSession { /// Each provider session can have its own message processing logic. provider: Box, provider_events: UnboundedReceiver, + /// Whether the provider handler is still busy with processing the last event. + is_busy: Arc, } struct DebounceTimer { @@ -46,12 +50,15 @@ impl DebounceTimer { fn should_emit(&mut self) -> bool { let now = std::time::Instant::now(); - if self.last_emitted.is_none() - || now.duration_since(self.last_emitted.expect("Must be Some as checked")) - > self.debounce_period - { + if self.last_emitted.is_none() { self.last_emitted.replace(now); return true; + } else { + let elapsed = now.duration_since(self.last_emitted.expect("Must be Some as checked")); + if elapsed > self.debounce_period { + self.last_emitted.replace(now); + return true; + } } false } @@ -74,10 +81,16 @@ impl ProviderSession { let id = ctx.env.provider_id.clone(); let debounce_delay = ctx.provider_debounce(); + let is_busy = Arc::new(AtomicBool::new(false)); + + let provider_is_busy = is_busy.clone(); + tokio::spawn(async move { let mut on_move_timer = DebounceTimer::new(Duration::from_millis(200)); let mut on_typed_timer = DebounceTimer::new(Duration::from_millis(debounce_delay)); + let mut event_cache = Vec::new(); + // TODO: this does not fully resolve the problem, find another solution. // Text input from users could be overloaded in a short period of time, e.g., OnMove // and OnTyped can be too frequent if user types too fast, in which case we observe @@ -89,17 +102,26 @@ impl ProviderSession { // provider event in a separate task, that requires more effoets however, now we choose to // debounce the stream to avoid overwhelming the system. while let Some(event) = origin_provider_event_receiver.recv().await { - tracing::debug!("Recv origin event: {event:?}"); - let should_emit = match &event { ProviderEvent::OnMove(..) => on_move_timer.should_emit(), ProviderEvent::OnTyped(..) => on_typed_timer.should_emit(), _ => true, }; + // tracing::debug!(should_emit, "Recv origin event: {event:?}"); + // Send event after debounce period - if should_emit && debounced_provider_event_sender.send(event).is_err() { - return; + if should_emit { + if provider_is_busy.load(Ordering::SeqCst) { + tracing::debug!( + "=============== provider is busy, caching {event:?}, cache size: {}", + event_cache.len() + ); + event_cache.push(event); + } else if debounced_provider_event_sender.send(event.clone()).is_err() { + tracing::debug!("Sending debounced event: {event:?}"); + return; + } } } }); @@ -110,6 +132,7 @@ impl ProviderSession { provider_session_id, provider, provider_events: debounced_provider_event_receiver, + is_busy, }; (provider_session, origin_provider_event_sender) @@ -125,13 +148,24 @@ impl ProviderSession { "Spawning a new provider session task", ); - tokio::spawn(async move { - if debounce_delay > 0 { - self.run_event_loop_with_debounce(debounce_delay).await; - } else { + let _join_handle = std::thread::spawn(move || { + let tokio_runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .max_blocking_threads(32) + .build() + .unwrap(); + tokio_runtime.block_on(async move { self.run_event_loop_without_debounce().await; - } + }); }); + + // tokio::spawn(async move { + // if debounce_delay > 0 { + // self.run_event_loop_with_debounce(debounce_delay).await; + // } else { + // self.run_event_loop_without_debounce().await; + // } + // }); } // https://github.com/denoland/deno/blob/1fb5858009f598ce3f917f9f49c466db81f4d9b0/cli/lsp/diagnostics.rs#L141 @@ -206,18 +240,6 @@ impl ProviderSession { None => break, // channel has closed. } } - _ = on_move_timer.as_mut(), if on_move.is_some() => { - if let Some(_params) = on_move.take() { - on_move_timer.as_mut().reset(Instant::now() + NEVER); - - async { - if let Err(err) = self.provider.on_move(&mut self.ctx).await { - tracing::error!(?err, "Failed to process ProviderEvent::OnMove"); - } - } - .instrument(tracing::info_span!("process_on_move")).await - } - } _ = on_typed_timer.as_mut(), if on_typed.is_some() => { if let Some(_params) = on_typed.take() { on_typed_timer.as_mut().reset(Instant::now() + NEVER); @@ -235,6 +257,18 @@ impl ProviderSession { process_on_typed.instrument(tracing::info_span!("process_on_typed")).await } } + _ = on_move_timer.as_mut(), if on_move.is_some() => { + if let Some(_params) = on_move.take() { + on_move_timer.as_mut().reset(Instant::now() + NEVER); + + async { + if let Err(err) = self.provider.on_move(&mut self.ctx).await { + tracing::error!(?err, "Failed to process ProviderEvent::OnMove"); + } + } + .instrument(tracing::info_span!("process_on_move")).await + } + } } } } @@ -250,15 +284,21 @@ impl ProviderSession { } } ProviderEvent::OnMove(_params) => { + self.is_busy.store(true, Ordering::SeqCst); + // OnMove implementation may contain blocking operation, let's not make it + // overloaded. if let Err(err) = self.provider.on_move(&mut self.ctx).await { tracing::debug!(?err, "Failed to process OnMove"); } + self.is_busy.store(false, Ordering::SeqCst); } ProviderEvent::OnTyped(_params) => { + self.is_busy.store(true, Ordering::SeqCst); let _ = self.ctx.record_input().await; if let Err(err) = self.provider.on_typed(&mut self.ctx).await { tracing::debug!(?err, "Failed to process OnTyped"); } + self.is_busy.store(false, Ordering::SeqCst); } ProviderEvent::Key(key_event) => { if let Err(err) = self.provider.on_key_event(&mut self.ctx, key_event).await { From 201cd9c26cb8c6c6811c26cd23a470b3638431a4 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 19 May 2024 09:30:08 +0800 Subject: [PATCH 3/8] Run context tag and syntax highlight job in separate threads --- crates/maple_core/src/stdio_server/job.rs | 15 +++ .../stdio_server/provider/hooks/on_move.rs | 124 +++++++++++++++--- .../src/stdio_server/provider/impls/grep.rs | 1 - .../src/stdio_server/provider/impls/lsp.rs | 6 +- .../maple_core/src/tools/ctags/context_tag.rs | 2 + 5 files changed, 128 insertions(+), 20 deletions(-) diff --git a/crates/maple_core/src/stdio_server/job.rs b/crates/maple_core/src/stdio_server/job.rs index 180f282b2..a56ae06c2 100644 --- a/crates/maple_core/src/stdio_server/job.rs +++ b/crates/maple_core/src/stdio_server/job.rs @@ -34,3 +34,18 @@ pub fn unreserve(job_id: u64) { let mut jobs = JOBS.lock(); jobs.remove(&job_id); } + +// Define a function to spawn a new thread and run a future +pub fn spawn_on_new_thread(future: F) -> std::thread::JoinHandle<()> +where + F: Future + Send + 'static, +{ + std::thread::spawn(move || { + let tokio_runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .max_blocking_threads(32) + .build() + .unwrap(); + tokio_runtime.block_on(future); + }) +} diff --git a/crates/maple_core/src/stdio_server/provider/hooks/on_move.rs b/crates/maple_core/src/stdio_server/provider/hooks/on_move.rs index eef32409d..5993f2924 100644 --- a/crates/maple_core/src/stdio_server/provider/hooks/on_move.rs +++ b/crates/maple_core/src/stdio_server/provider/hooks/on_move.rs @@ -8,7 +8,7 @@ use crate::stdio_server::plugin::syntax::sublime::{ }; use crate::stdio_server::provider::{read_dir_entries, Context, ProviderSource}; use crate::stdio_server::vim::{preview_syntax, VimResult}; -use crate::tools::ctags::{current_context_tag_async, BufferTag}; +use crate::tools::ctags::{current_context_tag, BufferTag}; use paths::{expand_tilde, truncate_absolute_path}; use pattern::*; use serde::{Deserialize, Serialize}; @@ -18,6 +18,7 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; use std::time::Duration; use sublime_syntax::TokenHighlight; +use tokio::sync::oneshot; use utils::display_width; type SublimeHighlights = Vec<(usize, Vec)>; @@ -323,9 +324,11 @@ impl<'a> CachedPreviewImpl<'a> { return Ok((self.preview_target.clone(), preview)); } + let now = std::time::Instant::now(); + let preview = match &self.preview_target { PreviewTarget::Directory(path) => self.preview_directory(path)?, - PreviewTarget::StartOfFile(path) => self.preview_file(path)?, + PreviewTarget::StartOfFile(path) => self.preview_file(path).await?, PreviewTarget::LocationInFile { path, line_number, @@ -343,6 +346,11 @@ impl<'a> CachedPreviewImpl<'a> { } => self.preview_help_subject(subject, doc_filename, runtimepath), }; + let elapsed = now.elapsed().as_millis(); + if elapsed > 1000 { + tracing::warn!("Fetching preview took too long: {elapsed:?} ms"); + } + self.ctx .preview_manager .insert_preview(self.preview_target.clone(), preview.clone()); @@ -408,7 +416,7 @@ impl<'a> CachedPreviewImpl<'a> { Ok(Preview::new(lines)) } - fn preview_file>(&self, path: P) -> Result { + async fn preview_file>(&self, path: P) -> Result { let path = path.as_ref(); if !path.is_file() { @@ -457,8 +465,17 @@ impl<'a> CachedPreviewImpl<'a> { } }; - let sublime_or_ts_highlights = - fetch_syntax_highlights(&lines, path, 0, self.max_line_width(), 0..lines.len(), None); + let sublime_or_ts_highlights = SyntaxHighlighter { + lines: lines.clone(), + path: path.to_path_buf(), + line_number_offset: 0, + max_line_width: self.max_line_width(), + range: 0..lines.len(), + maybe_code_context: None, + timeout: 200, + } + .fetch_highlights() + .await; let total = utils::line_count(path)?; let end = lines.len(); @@ -522,14 +539,18 @@ impl<'a> CachedPreviewImpl<'a> { // 1 (header line) + 1 (1-based line number) let line_number_offset = 1 + 1 + if maybe_code_context.is_some() { 3 } else { 0 }; - let sublime_or_ts_highlights = fetch_syntax_highlights( - &lines, - path, + + let sublime_or_ts_highlights = SyntaxHighlighter { + lines: lines.clone(), + path: path.to_path_buf(), line_number_offset, - self.max_line_width(), - start..end + 1, - maybe_code_context.as_ref(), - ); + max_line_width: self.max_line_width(), + range: start..end + 1, + maybe_code_context: maybe_code_context.clone(), + timeout: 200, + } + .fetch_highlights() + .await; let context_lines = maybe_code_context .map(|code_context| { @@ -662,10 +683,20 @@ impl<'a> CachedPreviewImpl<'a> { } async fn context_tag_with_timeout(path: &Path, lnum: usize) -> Option { - const TIMEOUT: Duration = Duration::from_millis(300); + let (tag_sender, tag_receiver) = oneshot::channel(); + + const TIMEOUT: Duration = Duration::from_millis(200); - match tokio::time::timeout(TIMEOUT, current_context_tag_async(path, lnum)).await { - Ok(res) => res, + std::thread::spawn({ + let path = path.to_path_buf(); + move || { + let result = current_context_tag(&path, lnum); + let _ = tag_sender.send(result); + } + }); + + match tokio::time::timeout(TIMEOUT, tag_receiver).await { + Ok(res) => res.ok().flatten(), Err(_) => { tracing::debug!(timeout = ?TIMEOUT, ?path, lnum, "⏳ Did not get the context tag in time"); None @@ -682,6 +713,7 @@ async fn context_tag_with_timeout(path: &Path, lnum: usize) -> Option /// /// The idea here is similar to how we find the nearest symbol at cursor using ctags, finding the /// line containing the context line and displaying it along with the normal preview content. +#[derive(Clone)] struct CodeContext { /// Full context line. /// @@ -795,6 +827,68 @@ enum SublimeOrTreeSitter { Neither, } +struct SyntaxHighlighter { + lines: Vec, + path: PathBuf, + line_number_offset: usize, + max_line_width: usize, + range: Range, + maybe_code_context: Option, + // Timeout in milliseconds. + timeout: u64, +} + +impl SyntaxHighlighter { + // Fetch with highlights with a timeout. + // + // `fetch_syntax_highlights` might be slow for larger files (over 100k lines) as tree-sitter will + // have to parse the whole file to obtain the highlight info. Therefore, we must run the actual + // worker in a separated task to not make the async runtime blocked, otherwise we may run into + // the issue of frozen UI. + async fn fetch_highlights(self) -> SublimeOrTreeSitter { + let (result_sender, result_receiver) = oneshot::channel(); + + let Self { + lines, + path, + line_number_offset, + max_line_width, + range, + maybe_code_context, + timeout, + } = self; + + std::thread::spawn({ + let path = path.clone(); + move || { + let result = fetch_syntax_highlights( + &lines, + &path, + line_number_offset, + max_line_width, + range, + maybe_code_context.as_ref(), + ); + let _ = result_sender.send(result); + } + }); + + let timeout = Duration::from_millis(timeout); + + match tokio::time::timeout(timeout, result_receiver).await { + Ok(res) => res.unwrap_or(SublimeOrTreeSitter::Neither), + Err(_) => { + tracing::debug!( + ?timeout, + ?path, + "⏳ Did not get the preview highlight in time" + ); + SublimeOrTreeSitter::Neither + } + } + } +} + // TODO: this might be slow for larger files (over 100k lines) as tree-sitter will have to // parse the whole file to obtain the highlight info. We may make the highlighting async. fn fetch_syntax_highlights( diff --git a/crates/maple_core/src/stdio_server/provider/impls/grep.rs b/crates/maple_core/src/stdio_server/provider/impls/grep.rs index f92c18e44..907c39bea 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/grep.rs @@ -80,7 +80,6 @@ impl ClapProvider for GrepProvider { async fn on_typed(&mut self, ctx: &mut Context) -> Result<()> { let query = ctx.vim.input_get().await?; - tracing::debug!("============= procesing query: {query}"); if query.is_empty() { ctx.update_on_empty_query().await?; } else { diff --git a/crates/maple_core/src/stdio_server/provider/impls/lsp.rs b/crates/maple_core/src/stdio_server/provider/impls/lsp.rs index 7b35ff637..b58a7a3c5 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/lsp.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/lsp.rs @@ -438,10 +438,8 @@ impl ClapProvider for LspProvider { .fetch_location_at(line_number - 1) .ok_or(ProviderError::PreviewItemNotFound { line_number })?; let mut ctx = ctx.clone(); - tokio::spawn(async move { - ctx.preview_manager.reset_scroll(); - ctx.update_preview(Some(loc.into_preview_target())).await; - }); + ctx.preview_manager.reset_scroll(); + ctx.update_preview(Some(loc.into_preview_target())).await; Ok(()) } diff --git a/crates/maple_core/src/tools/ctags/context_tag.rs b/crates/maple_core/src/tools/ctags/context_tag.rs index 56facf2a7..9f86370c4 100644 --- a/crates/maple_core/src/tools/ctags/context_tag.rs +++ b/crates/maple_core/src/tools/ctags/context_tag.rs @@ -91,6 +91,8 @@ fn find_context_tag(superset_tags: Vec, at: usize) -> Option Option { let superset_tags = if *CTAGS_HAS_JSON_FEATURE.deref() { collect_superset_context_tags_async(tokio_cmd(file, true), BufferTag::from_json_line, at) From ebcd0ad9f1747c31dccd013ac35ae2c26a0d5d64 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 19 May 2024 09:39:04 +0800 Subject: [PATCH 4/8] Remove temperory debug statements --- .../src/searcher/grep/stoppable_searcher.rs | 6 ++---- .../src/stdio_server/provider/impls/blines.rs | 20 +++++++----------- .../src/stdio_server/provider/impls/lsp.rs | 7 +++---- .../provider/impls/recent_files.rs | 17 ++++++--------- .../src/stdio_server/provider/mod.rs | 8 ++----- crates/maple_core/src/stdio_server/service.rs | 21 +++++-------------- 6 files changed, 26 insertions(+), 53 deletions(-) diff --git a/crates/maple_core/src/searcher/grep/stoppable_searcher.rs b/crates/maple_core/src/searcher/grep/stoppable_searcher.rs index e20ac8e44..8a90b57d6 100644 --- a/crates/maple_core/src/searcher/grep/stoppable_searcher.rs +++ b/crates/maple_core/src/searcher/grep/stoppable_searcher.rs @@ -360,21 +360,19 @@ pub async fn search(query: String, matcher: Matcher, search_context: SearchConte return; } - let searching_elapsed = now.elapsed().as_millis(); + let elapsed = now.elapsed().as_millis(); let display_lines = to_display_lines(&best_results.results, icon); let total_matched = search_info.total_matched.load(Ordering::SeqCst); let total_processed = search_info.total_processed.load(Ordering::SeqCst); - let now = std::time::Instant::now(); progressor.on_finished(display_lines, total_matched, total_processed); - let ui_update_elapsed = now.elapsed().as_millis(); tracing::debug!( total_processed, total_matched, ?query, - "Searching completed in {searching_elapsed:?}ms, UI updated in {ui_update_elapsed:?}ms" + "Searching completed in {elapsed:?}ms" ); } diff --git a/crates/maple_core/src/stdio_server/provider/impls/blines.rs b/crates/maple_core/src/stdio_server/provider/impls/blines.rs index 55eae05de..361fe5feb 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/blines.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/blines.rs @@ -166,18 +166,14 @@ impl ClapProvider for BlinesProvider { if !ctx.env.preview_enabled { return Ok(()); } - let mut ctx = ctx.clone(); - let preview_file = self.preview_file.clone(); - tokio::spawn(async move { - ctx.preview_manager.reset_scroll(); - let curline = ctx.vim.display_getcurline().await?; - let Some(line_number) = pattern::extract_blines_lnum(&curline) else { - return Ok(()); - }; - let preview_target = PreviewTarget::location_in_file(preview_file, line_number); - ctx.update_preview(Some(preview_target)).await - }); - Ok(()) + ctx.preview_manager.reset_scroll(); + let curline = ctx.vim.display_getcurline().await?; + let Some(line_number) = pattern::extract_blines_lnum(&curline) else { + return Ok(()); + }; + let preview_target = + PreviewTarget::location_in_file(self.preview_file.clone(), line_number); + ctx.update_preview(Some(preview_target)).await } async fn on_typed(&mut self, ctx: &mut Context) -> Result<()> { diff --git a/crates/maple_core/src/stdio_server/provider/impls/lsp.rs b/crates/maple_core/src/stdio_server/provider/impls/lsp.rs index b58a7a3c5..2f3dc2d2e 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/lsp.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/lsp.rs @@ -433,14 +433,13 @@ impl ClapProvider for LspProvider { if !ctx.env.preview_enabled { return Ok(()); } + ctx.preview_manager.reset_scroll(); + let line_number = ctx.vim.display_getcurlnum().await?; let loc = self .fetch_location_at(line_number - 1) .ok_or(ProviderError::PreviewItemNotFound { line_number })?; - let mut ctx = ctx.clone(); - ctx.preview_manager.reset_scroll(); - ctx.update_preview(Some(loc.into_preview_target())).await; - Ok(()) + ctx.update_preview(Some(loc.into_preview_target())).await } async fn remote_sink(&mut self, ctx: &mut Context, line_numbers: Vec) -> Result<()> { diff --git a/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs b/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs index d532d7fa9..bb6522d29 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs @@ -169,17 +169,12 @@ impl ClapProvider for RecentFilesProvider { if let Some(curline) = maybe_curline { let preview_height = ctx.preview_height().await?; - let mut ctx = ctx.clone(); - tokio::spawn(async move { - let (preview_target, preview) = - CachedPreviewImpl::new(curline, preview_height, &ctx)? - .get_preview() - .await?; - ctx.preview_manager.reset_scroll(); - ctx.update_picker_preview(preview)?; - ctx.preview_manager.set_preview_target(preview_target); - Ok::<(), ProviderError>(()) - }); + let (preview_target, preview) = CachedPreviewImpl::new(curline, preview_height, &ctx)? + .get_preview() + .await?; + ctx.preview_manager.reset_scroll(); + ctx.update_picker_preview(preview)?; + ctx.preview_manager.set_preview_target(preview_target); } Ok(()) diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index 6cb25de75..aeb4dcbe1 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -872,12 +872,8 @@ pub trait ClapProvider: Debug + Send + Sync + 'static { if !ctx.env.preview_enabled { return Ok(()); } - let mut ctx = ctx.clone(); - tokio::spawn(async move { - ctx.preview_manager.reset_scroll(); - let _ = ctx.update_preview(None).await; - }); - Ok(()) + ctx.preview_manager.reset_scroll(); + ctx.update_preview(None).await } async fn on_typed(&mut self, ctx: &mut Context) -> ProviderResult<()>; diff --git a/crates/maple_core/src/stdio_server/service.rs b/crates/maple_core/src/stdio_server/service.rs index cca4948de..652c17cbc 100644 --- a/crates/maple_core/src/stdio_server/service.rs +++ b/crates/maple_core/src/stdio_server/service.rs @@ -148,24 +148,13 @@ impl ProviderSession { "Spawning a new provider session task", ); - let _join_handle = std::thread::spawn(move || { - let tokio_runtime = tokio::runtime::Builder::new_current_thread() - .enable_all() - .max_blocking_threads(32) - .build() - .unwrap(); - tokio_runtime.block_on(async move { + tokio::spawn(async move { + if debounce_delay > 0 { + self.run_event_loop_with_debounce(debounce_delay).await; + } else { self.run_event_loop_without_debounce().await; - }); + } }); - - // tokio::spawn(async move { - // if debounce_delay > 0 { - // self.run_event_loop_with_debounce(debounce_delay).await; - // } else { - // self.run_event_loop_without_debounce().await; - // } - // }); } // https://github.com/denoland/deno/blob/1fb5858009f598ce3f917f9f49c466db81f4d9b0/cli/lsp/diagnostics.rs#L141 From bd755ef57addab5ba5c41d744a61b7bcae99beb7 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Fri, 10 May 2024 11:12:54 +0800 Subject: [PATCH 5/8] Make clippy happy --- crates/maple_core/src/find_usages/search_engine/mod.rs | 1 - crates/maple_core/src/stdio_server/provider/impls/filer.rs | 2 +- crates/maple_core/src/stdio_server/provider/impls/files.rs | 2 +- crates/maple_core/src/stdio_server/provider/impls/grep.rs | 2 +- crates/maple_core/src/stdio_server/provider/impls/igrep.rs | 4 ++-- crates/maple_core/src/tools/ctags/buffer_tag.rs | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/maple_core/src/find_usages/search_engine/mod.rs b/crates/maple_core/src/find_usages/search_engine/mod.rs index 16d958517..88464dddb 100644 --- a/crates/maple_core/src/find_usages/search_engine/mod.rs +++ b/crates/maple_core/src/find_usages/search_engine/mod.rs @@ -24,7 +24,6 @@ pub enum QueryType { Exact, /// Substring match. Contain, - /// Inherit, } diff --git a/crates/maple_core/src/stdio_server/provider/impls/filer.rs b/crates/maple_core/src/stdio_server/provider/impls/filer.rs index f24aef835..4cf668fdd 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/filer.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/filer.rs @@ -300,7 +300,7 @@ impl FilerProvider { } fn goto_dir(&mut self, dir: PathBuf, ctx: &Context) -> Result<()> { - self.current_dir = dir.clone(); + self.current_dir.clone_from(&dir); self.load_dir(dir, ctx)?; ctx.vim.exec("input_set", [""])?; ctx.vim.exec( diff --git a/crates/maple_core/src/stdio_server/provider/impls/files.rs b/crates/maple_core/src/stdio_server/provider/impls/files.rs index bf4945b46..7560c3916 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/files.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/files.rs @@ -79,7 +79,7 @@ impl FilesProvider { let join_handle = { let mut search_context = ctx.search_context(stop_signal.clone()); if self.args.base.no_cwd { - search_context.paths = self.args.paths.clone(); + search_context.paths.clone_from(&self.args.paths); } else { search_context.paths.extend_from_slice(&self.args.paths); } diff --git a/crates/maple_core/src/stdio_server/provider/impls/grep.rs b/crates/maple_core/src/stdio_server/provider/impls/grep.rs index 907c39bea..11edd9291 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/grep.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/grep.rs @@ -53,7 +53,7 @@ impl GrepProvider { let mut search_context = ctx.search_context(stop_signal.clone()); // cwd + extra paths if self.args.base.no_cwd { - search_context.paths = self.args.paths.clone(); + search_context.paths.clone_from(&self.args.paths); } else { search_context.paths.extend_from_slice(&self.args.paths); } diff --git a/crates/maple_core/src/stdio_server/provider/impls/igrep.rs b/crates/maple_core/src/stdio_server/provider/impls/igrep.rs index 152ddbf2d..0a1c6ed68 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/igrep.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/igrep.rs @@ -110,7 +110,7 @@ impl Explorer { let response = json!({ "entries": &entries, "dir": cwd, "total": entries.len() }); ctx.vim .exec("clap#file_explorer#handle_on_initialize", response)?; - self.current_lines = entries.clone(); + self.current_lines.clone_from(&entries); } self.dir_entries_cache.insert( @@ -281,7 +281,7 @@ impl Explorer { } fn goto_dir(&mut self, dir: PathBuf, ctx: &Context) -> Result<()> { - self.current_dir = dir.clone(); + self.current_dir.clone_from(&dir); if let Err(err) = self.read_entries_if_not_in_cache(dir) { ctx.vim.exec("show_lines_in_preview", [err.to_string()])?; } diff --git a/crates/maple_core/src/tools/ctags/buffer_tag.rs b/crates/maple_core/src/tools/ctags/buffer_tag.rs index dd2698c27..7278041c3 100644 --- a/crates/maple_core/src/tools/ctags/buffer_tag.rs +++ b/crates/maple_core/src/tools/ctags/buffer_tag.rs @@ -73,7 +73,7 @@ impl BufferTag { let others = items.join("\t"); if let Some((tagaddress, kind_line_scope)) = others.rsplit_once(";\"") { - t.pattern = tagaddress.to_owned(); + tagaddress.clone_into(&mut t.pattern); let mut iter = kind_line_scope.split_whitespace(); From 874a233a5287191da2dff7a8a284190a47396668 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 19 May 2024 09:52:33 +0800 Subject: [PATCH 6/8] clippy fixes --- crates/maple_core/src/stdio_server/job.rs | 1 + .../src/stdio_server/provider/impls/recent_files.rs | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/maple_core/src/stdio_server/job.rs b/crates/maple_core/src/stdio_server/job.rs index a56ae06c2..e5220bbd6 100644 --- a/crates/maple_core/src/stdio_server/job.rs +++ b/crates/maple_core/src/stdio_server/job.rs @@ -36,6 +36,7 @@ pub fn unreserve(job_id: u64) { } // Define a function to spawn a new thread and run a future +#[allow(unused)] pub fn spawn_on_new_thread(future: F) -> std::thread::JoinHandle<()> where F: Future + Send + 'static, diff --git a/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs b/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs index bb6522d29..f69701851 100644 --- a/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs +++ b/crates/maple_core/src/stdio_server/provider/impls/recent_files.rs @@ -1,8 +1,6 @@ use crate::datastore::RECENT_FILES_IN_MEMORY; use crate::stdio_server::provider::hooks::CachedPreviewImpl; -use crate::stdio_server::provider::{ - BaseArgs, ClapProvider, Context, ProviderError, ProviderResult as Result, -}; +use crate::stdio_server::provider::{BaseArgs, ClapProvider, Context, ProviderResult as Result}; use parking_lot::Mutex; use paths::AbsPathBuf; use printer::Printer; @@ -169,7 +167,7 @@ impl ClapProvider for RecentFilesProvider { if let Some(curline) = maybe_curline { let preview_height = ctx.preview_height().await?; - let (preview_target, preview) = CachedPreviewImpl::new(curline, preview_height, &ctx)? + let (preview_target, preview) = CachedPreviewImpl::new(curline, preview_height, ctx)? .get_preview() .await?; ctx.preview_manager.reset_scroll(); From e814b2eb16a918ad80c0d8528f17f58b235db5af Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 19 May 2024 10:47:22 +0800 Subject: [PATCH 7/8] Add tick timeout when debouncing the events --- crates/cli/src/command/rpc.rs | 2 - .../src/searcher/grep/stoppable_searcher.rs | 1 - crates/maple_core/src/stdio_server/input.rs | 13 +++ crates/maple_core/src/stdio_server/service.rs | 91 +++++++++++-------- 4 files changed, 64 insertions(+), 43 deletions(-) diff --git a/crates/cli/src/command/rpc.rs b/crates/cli/src/command/rpc.rs index 91d4705ef..93941d4f2 100644 --- a/crates/cli/src/command/rpc.rs +++ b/crates/cli/src/command/rpc.rs @@ -4,7 +4,6 @@ use clap::Parser; use maple_core::stdio_server::ConfigError; use std::io::IsTerminal; use tracing_subscriber::filter::EnvFilter; -use tracing_subscriber::fmt::format::FmtSpan; /// Starts a RPC service using stdio. #[derive(Parser, Debug, Clone)] @@ -69,7 +68,6 @@ impl Rpc { } let subscriber = tracing_subscriber::FmtSubscriber::builder() - .with_span_events(FmtSpan::FULL) .with_max_level(max_level) .with_env_filter(env_filter) .with_line_number(true) diff --git a/crates/maple_core/src/searcher/grep/stoppable_searcher.rs b/crates/maple_core/src/searcher/grep/stoppable_searcher.rs index 8a90b57d6..6cb9c0c04 100644 --- a/crates/maple_core/src/searcher/grep/stoppable_searcher.rs +++ b/crates/maple_core/src/searcher/grep/stoppable_searcher.rs @@ -366,7 +366,6 @@ pub async fn search(query: String, matcher: Matcher, search_context: SearchConte let total_matched = search_info.total_matched.load(Ordering::SeqCst); let total_processed = search_info.total_processed.load(Ordering::SeqCst); - progressor.on_finished(display_lines, total_matched, total_processed); tracing::debug!( diff --git a/crates/maple_core/src/stdio_server/input.rs b/crates/maple_core/src/stdio_server/input.rs index 1f16dfa78..2e92724f8 100644 --- a/crates/maple_core/src/stdio_server/input.rs +++ b/crates/maple_core/src/stdio_server/input.rs @@ -47,6 +47,19 @@ pub enum ProviderEvent { Internal(InternalProviderEvent), } +impl ProviderEvent { + pub fn is_same_type(&self, other: &Self) -> bool { + match self { + Self::OnMove(_) => matches!(other, Self::OnMove(_)), + Self::OnTyped(_) => matches!(other, Self::OnTyped(_)), + Self::RemoteSink(_) => matches!(other, Self::RemoteSink(_)), + Self::Exit => matches!(other, Self::Exit), + Self::Key(_) => matches!(other, Self::Key(_)), + Self::Internal(_) => matches!(other, Self::Internal(_)), + } + } +} + #[derive(Debug, Clone)] pub enum InternalProviderEvent { Initialize, diff --git a/crates/maple_core/src/stdio_server/service.rs b/crates/maple_core/src/stdio_server/service.rs index 652c17cbc..74452f371 100644 --- a/crates/maple_core/src/stdio_server/service.rs +++ b/crates/maple_core/src/stdio_server/service.rs @@ -8,7 +8,7 @@ use crate::stdio_server::plugin::{ActionType, ClapPlugin, PluginId}; use crate::stdio_server::provider::{ClapProvider, Context, ProviderId}; use rpc::Params; use std::collections::hash_map::Entry; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; use std::ops::ControlFlow; use std::sync::atomic::{AtomicBool, Ordering}; @@ -50,15 +50,12 @@ impl DebounceTimer { fn should_emit(&mut self) -> bool { let now = std::time::Instant::now(); - if self.last_emitted.is_none() { + if self.last_emitted.is_none() + || now.duration_since(self.last_emitted.expect("Must be Some as checked")) + > self.debounce_period + { self.last_emitted.replace(now); return true; - } else { - let elapsed = now.duration_since(self.last_emitted.expect("Must be Some as checked")); - if elapsed > self.debounce_period { - self.last_emitted.replace(now); - return true; - } } false } @@ -89,38 +86,52 @@ impl ProviderSession { let mut on_move_timer = DebounceTimer::new(Duration::from_millis(200)); let mut on_typed_timer = DebounceTimer::new(Duration::from_millis(debounce_delay)); - let mut event_cache = Vec::new(); - - // TODO: this does not fully resolve the problem, find another solution. - // Text input from users could be overloaded in a short period of time, e.g., OnMove - // and OnTyped can be too frequent if user types too fast, in which case we observe - // the receiver side of unbounded channel may not be able to receive the events in time, - // leading to the annoying frozen UI on the vim side. The cause is probably because of - // the event processing logic on the receiver side are not running in separate tasks, - // the processing of incoming messages cannot keep up with the rate of message generation, - // rendering the receiver may hang for a while. One proper solution is to process each - // provider event in a separate task, that requires more effoets however, now we choose to - // debounce the stream to avoid overwhelming the system. - while let Some(event) = origin_provider_event_receiver.recv().await { - let should_emit = match &event { - ProviderEvent::OnMove(..) => on_move_timer.should_emit(), - ProviderEvent::OnTyped(..) => on_typed_timer.should_emit(), - _ => true, - }; - - // tracing::debug!(should_emit, "Recv origin event: {event:?}"); - - // Send event after debounce period - if should_emit { - if provider_is_busy.load(Ordering::SeqCst) { - tracing::debug!( - "=============== provider is busy, caching {event:?}, cache size: {}", - event_cache.len() - ); - event_cache.push(event); - } else if debounced_provider_event_sender.send(event.clone()).is_err() { - tracing::debug!("Sending debounced event: {event:?}"); - return; + let mut event_cache = VecDeque::with_capacity(2); + + let mut tick_timeout = { + let mut interval = tokio::time::interval(Duration::from_millis(100)); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); + interval + }; + + // NOTE: The processing logic within the provider must not contain any blocking operation, + // otherwise the blocking operation taking too long can block the runtime, leading a + // frozen UI, one symptom is that the receiver failed to receive the debounded messages + // in time. + + loop { + tokio::select! { + maybe_event = origin_provider_event_receiver.recv() => { + let Some(event) = maybe_event else { + continue; + }; + + // Params are unused at present, include the params when it's not the case + // in the future. + let should_emit = match &event { + ProviderEvent::OnMove(..) => on_move_timer.should_emit(), + ProviderEvent::OnTyped(..) => on_typed_timer.should_emit(), + _ => true, + }; + + // Send event after debounce period if the provider is not overloaded. + if should_emit { + if provider_is_busy.load(Ordering::SeqCst) { + if event_cache.iter().any(|e| event.is_same_type(e)) { + continue; + } + event_cache.push_back(event); + } else if debounced_provider_event_sender.send(event).is_err() { + return; + } + } + } + _ = tick_timeout.tick() => { + if let Some(event) = event_cache.pop_front() { + if debounced_provider_event_sender.send(event).is_err() { + return; + } + } } } } From 93e17234b9d858b3026d8de30f7ddf3141f28555 Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sun, 19 May 2024 15:29:31 +0800 Subject: [PATCH 8/8] Update docs --- CHANGELOG.md | 3 +++ crates/maple_core/src/stdio_server/service.rs | 17 ++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 474b8883b..92dc0df84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [unreleased] +- Fixes the regression of frozen UI caused by the blocking operations in OnMove implementation. #1081 +- Fixes the build for Android. #1079 + ## [0.53] 2024-5-2 - Introduce `remote_sink` in provider in order to implement the sink function on the Rust side, particularly useful for the providers with static list of source like lsp. diff --git a/crates/maple_core/src/stdio_server/service.rs b/crates/maple_core/src/stdio_server/service.rs index 74452f371..042709b89 100644 --- a/crates/maple_core/src/stdio_server/service.rs +++ b/crates/maple_core/src/stdio_server/service.rs @@ -94,20 +94,19 @@ impl ProviderSession { interval }; - // NOTE: The processing logic within the provider must not contain any blocking operation, - // otherwise the blocking operation taking too long can block the runtime, leading a - // frozen UI, one symptom is that the receiver failed to receive the debounded messages - // in time. + // NOTE: + // The processing logic within the provider must not contain any blocking operation, + // otherwise the whole async runtime may be blocked if it takes too long, leading a + // frozen UI because the debounded message receiver failed to receive messages in time + // in that case, ref #1080 for more details. loop { tokio::select! { maybe_event = origin_provider_event_receiver.recv() => { let Some(event) = maybe_event else { - continue; + return; }; - // Params are unused at present, include the params when it's not the case - // in the future. let should_emit = match &event { ProviderEvent::OnMove(..) => on_move_timer.should_emit(), ProviderEvent::OnTyped(..) => on_typed_timer.should_emit(), @@ -127,6 +126,10 @@ impl ProviderSession { } } _ = tick_timeout.tick() => { + if debounced_provider_event_sender.is_closed() { + return; + } + if let Some(event) = event_cache.pop_front() { if debounced_provider_event_sender.send(event).is_err() { return;