Skip to content

Commit

Permalink
transition to nucleo for fuzzy matching (helix-editor#7814)
Browse files Browse the repository at this point in the history
* 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 <skyler@dead10ck.com>

---------

Co-authored-by: Skyler Hawthorne <skyler@dead10ck.com>
  • Loading branch information
2 people authored and dgkf committed Jan 30, 2024
1 parent 5ed8f12 commit d41df19
Show file tree
Hide file tree
Showing 26 changed files with 757 additions and 1,052 deletions.
126 changes: 116 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"helix-term",
"helix-tui",
"helix-lsp",
"helix-event",
"helix-dap",
"helix-loader",
"helix-vcs",
Expand Down Expand Up @@ -35,3 +36,4 @@ package.helix-term.opt-level = 2

[workspace.dependencies]
tree-sitter = { version = "0.20", git = "https://github.com/tree-sitter/tree-sitter", rev = "ab09ae20d640711174b8da8a654f6b3dec93da1a" }
nucleo = "0.2.0"
3 changes: 3 additions & 0 deletions helix-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std"
etcetera = "0.8"
textwrap = "0.16.0"

nucleo.workspace = true
parking_lot = "0.12"

[dev-dependencies]
quickcheck = { version = "1", default-features = false }
indoc = "2.0.3"
43 changes: 43 additions & 0 deletions helix-core/src/fuzzy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::ops::DerefMut;

use nucleo::pattern::{AtomKind, CaseMatching, Pattern};
use nucleo::Config;
use parking_lot::Mutex;

pub struct LazyMutex<T> {
inner: Mutex<Option<T>>,
init: fn() -> T,
}

impl<T> LazyMutex<T> {
pub const fn new(init: fn() -> T) -> Self {
Self {
inner: Mutex::new(None),
init,
}
}

pub fn lock(&self) -> impl DerefMut<Target = T> + '_ {
parking_lot::MutexGuard::map(self.inner.lock(), |val| val.get_or_insert_with(self.init))
}
}

pub static MATCHER: LazyMutex<nucleo::Matcher> = LazyMutex::new(nucleo::Matcher::default);

/// convenience function to easily fuzzy match
/// on a (relatively small list of inputs). This is not recommended for building a full tui
/// application that can match large numbers of matches as all matching is done on the current
/// thread, effectively blocking the UI
pub fn fuzzy_match<T: AsRef<str>>(
pattern: &str,
items: impl IntoIterator<Item = T>,
path: bool,
) -> Vec<(T, u32)> {
let mut matcher = MATCHER.lock();
matcher.config = Config::DEFAULT;
if path {
matcher.config.set_match_paths();
}
let pattern = Pattern::new(pattern, CaseMatching::Smart, AtomKind::Fuzzy);
pattern.match_list(items, &mut matcher)
}
1 change: 1 addition & 0 deletions helix-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod config;
pub mod diagnostic;
pub mod diff;
pub mod doc_formatter;
pub mod fuzzy;
pub mod graphemes;
pub mod history;
pub mod increment;
Expand Down
15 changes: 15 additions & 0 deletions helix-event/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "helix-event"
version = "0.6.0"
authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
edition = "2021"
license = "MPL-2.0"
categories = ["editor"]
repository = "https://github.com/helix-editor/helix"
homepage = "https://helix-editor.com"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot"] }
parking_lot = { version = "0.12", features = ["send_guard"] }
8 changes: 8 additions & 0 deletions helix-event/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! `helix-event` contains systems that allow (often async) communication between
//! different editor components without strongly coupling them. Currently this
//! crate only contains some smaller facilities but the intend is to add more
//! functionality in the future ( like a generic hook system)

pub use redraw::{lock_frame, redraw_requested, request_redraw, start_frame, RenderLockGuard};

mod redraw;
49 changes: 49 additions & 0 deletions helix-event/src/redraw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Signals that control when/if the editor redraws

use std::future::Future;

use parking_lot::{RwLock, RwLockReadGuard};
use tokio::sync::Notify;

/// A `Notify` instance that can be used to (asynchronously) request
/// the editor the render a new frame.
static REDRAW_NOTIFY: Notify = Notify::const_new();

/// A `RwLock` that prevents the next frame from being
/// drawn until an exclusive (write) lock can be acquired.
/// This allows asynchsonous tasks to acquire `non-exclusive`
/// locks (read) to prevent the next frame from being drawn
/// until a certain computation has finished.
static RENDER_LOCK: RwLock<()> = RwLock::new(());

pub type RenderLockGuard = RwLockReadGuard<'static, ()>;

/// Requests that the editor is redrawn. The redraws are debounced (currently to
/// 30FPS) so this can be called many times without causing a ton of frames to
/// be rendered.
pub fn request_redraw() {
REDRAW_NOTIFY.notify_one();
}

/// Returns a future that will yield once a redraw has been asynchronously
/// requested using [`request_redraw`].
pub fn redraw_requested() -> impl Future<Output = ()> {
REDRAW_NOTIFY.notified()
}

/// Wait until all locks acquired with [`lock_frame`] have been released.
/// This function is called before rendering and is intended to allow the frame
/// to wait for async computations that should be included in the current frame.
pub fn start_frame() {
drop(RENDER_LOCK.write());
// exhaust any leftover redraw notifications
let notify = REDRAW_NOTIFY.notified();
tokio::pin!(notify);
notify.enable();
}

/// Acquires the render lock which will prevent the next frame from being drawn
/// until the returned guard is dropped.
pub fn lock_frame() -> RenderLockGuard {
RENDER_LOCK.read()
}
3 changes: 2 additions & 1 deletion helix-term/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ path = "src/main.rs"

[dependencies]
helix-core = { version = "0.6", path = "../helix-core" }
helix-event = { version = "0.6", path = "../helix-event" }
helix-view = { version = "0.6", path = "../helix-view" }
helix-lsp = { version = "0.6", path = "../helix-lsp" }
helix-dap = { version = "0.6", path = "../helix-dap" }
Expand All @@ -49,7 +50,7 @@ chrono = { version = "0.4", default-features = false, features = ["clock"] }
log = "0.4"

# File picker
fuzzy-matcher = "0.3"
nucleo.workspace = true
ignore = "0.4"
# markdown doc rendering
pulldown-cmark = { version = "0.9", default-features = false }
Expand Down
Loading

0 comments on commit d41df19

Please sign in to comment.