Skip to content

Commit 0cb595e

Browse files
transition to nucleo for fuzzy matching (#7814)
* transition to nucleo for fuzzy matching * drop flakey test case since the picker streams in results now any test that relies on the picker containing results is potentially flakely * use crates.io version of nucleo * Fix typo in commands.rs Co-authored-by: Skyler Hawthorne <[email protected]> --------- Co-authored-by: Skyler Hawthorne <[email protected]>
1 parent 40d7e6c commit 0cb595e

File tree

26 files changed

+757
-1052
lines changed

26 files changed

+757
-1052
lines changed

Cargo.lock

Lines changed: 116 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"helix-term",
66
"helix-tui",
77
"helix-lsp",
8+
"helix-event",
89
"helix-dap",
910
"helix-loader",
1011
"helix-vcs",
@@ -35,3 +36,4 @@ package.helix-term.opt-level = 2
3536

3637
[workspace.dependencies]
3738
tree-sitter = { version = "0.20", git = "https://github.com/tree-sitter/tree-sitter", rev = "ab09ae20d640711174b8da8a654f6b3dec93da1a" }
39+
nucleo = "0.2.0"

helix-core/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std"
4848
etcetera = "0.8"
4949
textwrap = "0.16.0"
5050

51+
nucleo.workspace = true
52+
parking_lot = "0.12"
53+
5154
[dev-dependencies]
5255
quickcheck = { version = "1", default-features = false }
5356
indoc = "2.0.3"

helix-core/src/fuzzy.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use std::ops::DerefMut;
2+
3+
use nucleo::pattern::{AtomKind, CaseMatching, Pattern};
4+
use nucleo::Config;
5+
use parking_lot::Mutex;
6+
7+
pub struct LazyMutex<T> {
8+
inner: Mutex<Option<T>>,
9+
init: fn() -> T,
10+
}
11+
12+
impl<T> LazyMutex<T> {
13+
pub const fn new(init: fn() -> T) -> Self {
14+
Self {
15+
inner: Mutex::new(None),
16+
init,
17+
}
18+
}
19+
20+
pub fn lock(&self) -> impl DerefMut<Target = T> + '_ {
21+
parking_lot::MutexGuard::map(self.inner.lock(), |val| val.get_or_insert_with(self.init))
22+
}
23+
}
24+
25+
pub static MATCHER: LazyMutex<nucleo::Matcher> = LazyMutex::new(nucleo::Matcher::default);
26+
27+
/// convenience function to easily fuzzy match
28+
/// on a (relatively small list of inputs). This is not recommended for building a full tui
29+
/// application that can match large numbers of matches as all matching is done on the current
30+
/// thread, effectively blocking the UI
31+
pub fn fuzzy_match<T: AsRef<str>>(
32+
pattern: &str,
33+
items: impl IntoIterator<Item = T>,
34+
path: bool,
35+
) -> Vec<(T, u32)> {
36+
let mut matcher = MATCHER.lock();
37+
matcher.config = Config::DEFAULT;
38+
if path {
39+
matcher.config.set_match_paths();
40+
}
41+
let pattern = Pattern::new(pattern, CaseMatching::Smart, AtomKind::Fuzzy);
42+
pattern.match_list(items, &mut matcher)
43+
}

helix-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod config;
77
pub mod diagnostic;
88
pub mod diff;
99
pub mod doc_formatter;
10+
pub mod fuzzy;
1011
pub mod graphemes;
1112
pub mod history;
1213
pub mod increment;

helix-event/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "helix-event"
3+
version = "0.6.0"
4+
authors = ["Blaž Hrastnik <[email protected]>"]
5+
edition = "2021"
6+
license = "MPL-2.0"
7+
categories = ["editor"]
8+
repository = "https://github.com/helix-editor/helix"
9+
homepage = "https://helix-editor.com"
10+
11+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12+
13+
[dependencies]
14+
tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot"] }
15+
parking_lot = { version = "0.12", features = ["send_guard"] }

helix-event/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//! `helix-event` contains systems that allow (often async) communication between
2+
//! different editor components without strongly coupling them. Currently this
3+
//! crate only contains some smaller facilities but the intend is to add more
4+
//! functionality in the future ( like a generic hook system)
5+
6+
pub use redraw::{lock_frame, redraw_requested, request_redraw, start_frame, RenderLockGuard};
7+
8+
mod redraw;

helix-event/src/redraw.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//! Signals that control when/if the editor redraws
2+
3+
use std::future::Future;
4+
5+
use parking_lot::{RwLock, RwLockReadGuard};
6+
use tokio::sync::Notify;
7+
8+
/// A `Notify` instance that can be used to (asynchronously) request
9+
/// the editor the render a new frame.
10+
static REDRAW_NOTIFY: Notify = Notify::const_new();
11+
12+
/// A `RwLock` that prevents the next frame from being
13+
/// drawn until an exclusive (write) lock can be acquired.
14+
/// This allows asynchsonous tasks to acquire `non-exclusive`
15+
/// locks (read) to prevent the next frame from being drawn
16+
/// until a certain computation has finished.
17+
static RENDER_LOCK: RwLock<()> = RwLock::new(());
18+
19+
pub type RenderLockGuard = RwLockReadGuard<'static, ()>;
20+
21+
/// Requests that the editor is redrawn. The redraws are debounced (currently to
22+
/// 30FPS) so this can be called many times without causing a ton of frames to
23+
/// be rendered.
24+
pub fn request_redraw() {
25+
REDRAW_NOTIFY.notify_one();
26+
}
27+
28+
/// Returns a future that will yield once a redraw has been asynchronously
29+
/// requested using [`request_redraw`].
30+
pub fn redraw_requested() -> impl Future<Output = ()> {
31+
REDRAW_NOTIFY.notified()
32+
}
33+
34+
/// Wait until all locks acquired with [`lock_frame`] have been released.
35+
/// This function is called before rendering and is intended to allow the frame
36+
/// to wait for async computations that should be included in the current frame.
37+
pub fn start_frame() {
38+
drop(RENDER_LOCK.write());
39+
// exhaust any leftover redraw notifications
40+
let notify = REDRAW_NOTIFY.notified();
41+
tokio::pin!(notify);
42+
notify.enable();
43+
}
44+
45+
/// Acquires the render lock which will prevent the next frame from being drawn
46+
/// until the returned guard is dropped.
47+
pub fn lock_frame() -> RenderLockGuard {
48+
RENDER_LOCK.read()
49+
}

helix-term/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ path = "src/main.rs"
2424

2525
[dependencies]
2626
helix-core = { version = "0.6", path = "../helix-core" }
27+
helix-event = { version = "0.6", path = "../helix-event" }
2728
helix-view = { version = "0.6", path = "../helix-view" }
2829
helix-lsp = { version = "0.6", path = "../helix-lsp" }
2930
helix-dap = { version = "0.6", path = "../helix-dap" }
@@ -49,7 +50,7 @@ chrono = { version = "0.4", default-features = false, features = ["clock"] }
4950
log = "0.4"
5051

5152
# File picker
52-
fuzzy-matcher = "0.3"
53+
nucleo.workspace = true
5354
ignore = "0.4"
5455
# markdown doc rendering
5556
pulldown-cmark = { version = "0.9", default-features = false }

0 commit comments

Comments
 (0)