diff --git a/Cargo.lock b/Cargo.lock index 63459cbe8d..46fb54b550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -745,7 +745,9 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" dependencies = [ + "lazy_static", "memchr", + "regex-automata", ] [[package]] @@ -776,6 +778,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + [[package]] name = "bytemuck" version = "1.5.1" @@ -1653,6 +1661,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "encoding_rs_io" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" +dependencies = [ + "encoding_rs", +] + [[package]] name = "entities" version = "1.0.1" @@ -1782,6 +1799,7 @@ dependencies = [ "editor", "gpui", "postage", + "project", "regex", "smol", "theme", @@ -2248,6 +2266,90 @@ dependencies = [ "syn", ] +[[package]] +name = "grep" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51cb840c560b45a2ffd8abf00190382789d3f596663d5ffeb2e05931c20e8657" +dependencies = [ + "grep-cli", + "grep-matcher", + "grep-printer", + "grep-regex", + "grep-searcher", +] + +[[package]] +name = "grep-cli" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dd110c34bb4460d0de5062413b773e385cbf8a85a63fc535590110a09e79e8a" +dependencies = [ + "atty", + "bstr", + "globset", + "lazy_static", + "log", + "regex", + "same-file", + "termcolor", + "winapi-util", +] + +[[package]] +name = "grep-matcher" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d27563c33062cd33003b166ade2bb4fd82db1fd6a86db764dfdad132d46c1cc" +dependencies = [ + "memchr", +] + +[[package]] +name = "grep-printer" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c271a24daedf5675b61a275a1d0af06e03312ab7856d15433ae6cde044dc72" +dependencies = [ + "base64 0.13.0", + "bstr", + "grep-matcher", + "grep-searcher", + "serde", + "serde_json", + "termcolor", +] + +[[package]] +name = "grep-regex" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121553c9768c363839b92fc2d7cdbbad44a3b70e8d6e7b1b72b05c977527bd06" +dependencies = [ + "aho-corasick", + "bstr", + "grep-matcher", + "log", + "regex", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "grep-searcher" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdbde90ba52adc240d2deef7b6ad1f99f53142d074b771fe9b7bede6c4c23d" +dependencies = [ + "bstr", + "bytecount", + "encoding_rs", + "encoding_rs_io", + "grep-matcher", + "log", + "memmap2 0.3.1", +] + [[package]] name = "group" version = "0.10.0" @@ -2911,6 +3013,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.3" @@ -3563,6 +3674,7 @@ dependencies = [ "futures", "fuzzy", "gpui", + "grep", "ignore", "language", "lazy_static", @@ -3866,6 +3978,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.25" diff --git a/crates/find/Cargo.toml b/crates/find/Cargo.toml index acab695d12..39570334d6 100644 --- a/crates/find/Cargo.toml +++ b/crates/find/Cargo.toml @@ -10,6 +10,7 @@ path = "src/find.rs" collections = { path = "../collections" } editor = { path = "../editor" } gpui = { path = "../gpui" } +project = { path = "../project" } theme = { path = "../theme" } workspace = { path = "../workspace" } aho-corasick = "0.7" diff --git a/crates/find/src/find.rs b/crates/find/src/find.rs index 76beed1f2e..4be4216c37 100644 --- a/crates/find/src/find.rs +++ b/crates/find/src/find.rs @@ -1,3 +1,5 @@ +mod project_find; + use aho_corasick::AhoCorasickBuilder; use anyhow::Result; use collections::HashMap; diff --git a/crates/find/src/project_find.rs b/crates/find/src/project_find.rs new file mode 100644 index 0000000000..8bc2777a6f --- /dev/null +++ b/crates/find/src/project_find.rs @@ -0,0 +1,46 @@ +use crate::SearchMode; +use editor::MultiBuffer; +use gpui::{Entity, ModelContext, ModelHandle, Task}; +use project::Project; + +struct ProjectFind { + last_search: SearchParams, + project: ModelHandle, + excerpts: ModelHandle, + pending_search: Task>, +} + +#[derive(Default)] +struct SearchParams { + query: String, + regex: bool, + whole_word: bool, + case_sensitive: bool, +} + +struct ProjectFindView { + model: ModelHandle, +} + +impl Entity for ProjectFind { + type Event = (); +} + +impl ProjectFind { + fn new(project: ModelHandle, cx: &mut ModelContext) -> Self { + let replica_id = project.read(cx).replica_id(); + Self { + project, + last_search: Default::default(), + excerpts: cx.add_model(|_| MultiBuffer::new(replica_id)), + pending_search: Task::ready(None), + } + } + + fn search(&mut self, params: SearchParams, cx: &mut ModelContext) { + self.pending_search = cx.spawn_weak(|this, cx| async move { + // + None + }); + } +} diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index f72ba133c3..ec1669ec72 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -29,6 +29,7 @@ util = { path = "../util" } anyhow = "1.0.38" async-trait = "0.1" futures = "0.3" +grep = "0.2" ignore = "0.4" lazy_static = "1.4.0" libc = "0.2" diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 236b02cd6c..0c3ff7d7cf 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -7,7 +7,7 @@ use anyhow::{anyhow, Context, Result}; use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use clock::ReplicaId; use collections::{hash_map, HashMap, HashSet}; -use futures::{future::Shared, Future, FutureExt}; +use futures::{future::Shared, Future, FutureExt, StreamExt}; use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet}; use gpui::{ AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, @@ -16,7 +16,7 @@ use gpui::{ use language::{ range_from_lsp, Anchor, AnchorRangeExt, Bias, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, Operation, PointUtf16, - ToLspPosition, ToOffset, ToPointUtf16, Transaction, + Rope, ToLspPosition, ToOffset, ToPointUtf16, Transaction, }; use lsp::{DiagnosticSeverity, DocumentHighlightKind, LanguageServer}; use lsp_command::*; @@ -2042,6 +2042,111 @@ impl Project { ) } + pub fn search(&self, query: &str, cx: &mut ModelContext) { + if self.is_local() { + enum SearchItem { + Path(PathBuf), + Buffer((WeakModelHandle, Rope)), + } + + let (queue_tx, queue_rx) = smol::channel::bounded(1024); + + // Submit all worktree paths to the queue. + let snapshots = self + .strong_worktrees(cx) + .filter_map(|tree| { + let tree = tree.read(cx).as_local()?; + Some((tree.abs_path().clone(), tree.snapshot())) + }) + .collect::>(); + cx.background() + .spawn({ + let queue_tx = queue_tx.clone(); + async move { + for (snapshot_abs_path, snapshot) in snapshots { + for file in snapshot.files(false, 0) { + if queue_tx + .send(SearchItem::Path(snapshot_abs_path.join(&file.path))) + .await + .is_err() + { + return; + } + } + } + } + }) + .detach(); + + // Submit all the currently-open buffers that are dirty to the queue. + let buffers = self + .open_buffers + .values() + .filter_map(|buffer| { + if let OpenBuffer::Loaded(buffer) = buffer { + Some(buffer.clone()) + } else { + None + } + }) + .collect::>(); + cx.spawn_weak(|_, cx| async move { + for buffer in buffers.into_iter().filter_map(|buffer| buffer.upgrade(&cx)) { + let text = buffer.read_with(&cx, |buffer, _| { + if buffer.is_dirty() { + Some(buffer.as_rope().clone()) + } else { + None + } + }); + + if let Some(text) = text { + if queue_tx + .send(SearchItem::Buffer((buffer.downgrade(), text))) + .await + .is_err() + { + return; + } + } + } + }) + .detach(); + + let background = cx.background().clone(); + cx.background() + .spawn(async move { + let workers = background.num_cpus(); + background + .scoped(|scope| { + for _ in 0..workers { + let mut paths_rx = queue_rx.clone(); + scope.spawn(async move { + while let Some(item) = paths_rx.next().await { + match item { + SearchItem::Path(_) => todo!(), + SearchItem::Buffer(_) => todo!(), + } + } + }); + } + }) + .await; + }) + .detach(); + // let multiline = query.contains('\n'); + // let searcher = grep::searcher::SearcherBuilder::new() + // .multi_line(multiline) + // .build(); + // searcher.search_path( + // "hey".to_string(), + // "/hello/world", + // grep::searcher::sinks::Lossy(|row, mat| {}), + // ); + } else { + } + } + fn request_lsp( &self, buffer_handle: ModelHandle,