Detailed changes
@@ -7960,6 +7960,7 @@ dependencies = [
"log",
"matrixmultiply",
"ndarray",
+ "picker",
"project",
"rand 0.8.5",
"rusqlite",
@@ -7968,6 +7969,7 @@ dependencies = [
"sha-1 0.10.1",
"smol",
"tempdir",
+ "theme",
"tree-sitter",
"tree-sitter-rust",
"unindent",
@@ -14,6 +14,8 @@ language = { path = "../language" }
project = { path = "../project" }
workspace = { path = "../workspace" }
util = { path = "../util" }
+picker = { path = "../picker" }
+theme = { path = "../theme" }
anyhow.workspace = true
futures.workspace = true
smol.workspace = true
@@ -0,0 +1,107 @@
+use std::sync::Arc;
+
+use gpui::{
+ actions, elements::*, AnyElement, AppContext, ModelHandle, MouseState, Task, ViewContext,
+ WeakViewHandle,
+};
+use picker::{Picker, PickerDelegate, PickerEvent};
+use project::Project;
+use util::ResultExt;
+use workspace::Workspace;
+
+use crate::{SearchResult, VectorStore};
+
+actions!(semantic_search, [Toggle]);
+
+pub type SemanticSearch = Picker<SemanticSearchDelegate>;
+
+pub struct SemanticSearchDelegate {
+ workspace: WeakViewHandle<Workspace>,
+ project: ModelHandle<Project>,
+ vector_store: ModelHandle<VectorStore>,
+ selected_match_index: usize,
+ matches: Vec<SearchResult>,
+}
+
+impl SemanticSearchDelegate {
+ // This is currently searching on every keystroke,
+ // This is wildly overkill, and has the potential to get expensive
+ // We will need to update this to throttle searching
+ pub fn new(
+ workspace: WeakViewHandle<Workspace>,
+ project: ModelHandle<Project>,
+ vector_store: ModelHandle<VectorStore>,
+ ) -> Self {
+ Self {
+ workspace,
+ project,
+ vector_store,
+ selected_match_index: 0,
+ matches: vec![],
+ }
+ }
+}
+
+impl PickerDelegate for SemanticSearchDelegate {
+ fn placeholder_text(&self) -> Arc<str> {
+ "Search repository in natural language...".into()
+ }
+
+ fn confirm(&mut self, cx: &mut ViewContext<SemanticSearch>) {
+ todo!()
+ }
+
+ fn dismissed(&mut self, _cx: &mut ViewContext<SemanticSearch>) {}
+
+ fn match_count(&self) -> usize {
+ self.matches.len()
+ }
+
+ fn selected_index(&self) -> usize {
+ self.selected_match_index
+ }
+
+ fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<SemanticSearch>) {
+ self.selected_match_index = ix;
+ }
+
+ fn update_matches(&mut self, query: String, cx: &mut ViewContext<SemanticSearch>) -> Task<()> {
+ let task = self
+ .vector_store
+ .update(cx, |store, cx| store.search(query.to_string(), 10, cx));
+
+ cx.spawn(|this, mut cx| async move {
+ let results = task.await.log_err();
+ this.update(&mut cx, |this, cx| {
+ if let Some(results) = results {
+ let delegate = this.delegate_mut();
+ delegate.matches = results;
+ }
+ });
+ })
+ }
+
+ fn render_match(
+ &self,
+ ix: usize,
+ mouse_state: &mut MouseState,
+ selected: bool,
+ cx: &AppContext,
+ ) -> AnyElement<Picker<Self>> {
+ let theme = theme::current(cx);
+ let style = &theme.picker.item;
+ let current_style = style.style_for(mouse_state, selected);
+
+ let search_result = &self.matches[ix];
+
+ let mut path = search_result.file_path.to_string_lossy();
+ let name = search_result.name.clone();
+
+ Flex::column()
+ .with_child(Text::new(name, current_style.label.text.clone()).with_soft_wrap(false))
+ .with_child(Label::new(path.to_string(), style.default.label.clone()))
+ .contained()
+ .with_style(current_style.container)
+ .into_any()
+ }
+}
@@ -1,5 +1,6 @@
mod db;
mod embedding;
+mod modal;
mod search;
#[cfg(test)]
@@ -10,6 +11,7 @@ use db::{FileSha1, VectorDatabase, VECTOR_DB_URL};
use embedding::{EmbeddingProvider, OpenAIEmbeddings};
use gpui::{actions, AppContext, Entity, ModelContext, ModelHandle, Task, ViewContext};
use language::{Language, LanguageRegistry};
+use modal::{SemanticSearch, SemanticSearchDelegate, Toggle};
use project::{Fs, Project};
use smol::channel;
use std::{cmp::Ordering, collections::HashMap, path::PathBuf, sync::Arc};
@@ -17,8 +19,6 @@ use tree_sitter::{Parser, QueryCursor};
use util::{http::HttpClient, ResultExt, TryFutureExt};
use workspace::{Workspace, WorkspaceCreated};
-actions!(semantic_search, [TestSearch]);
-
#[derive(Debug)]
pub struct Document {
pub offset: usize,
@@ -60,24 +60,40 @@ pub fn init(
.detach();
cx.add_action({
- let vector_store = vector_store.clone();
- move |workspace: &mut Workspace, _: &TestSearch, cx: &mut ViewContext<Workspace>| {
- let t0 = std::time::Instant::now();
- let task = vector_store.update(cx, |store, cx| {
- store.search("compute embeddings for all of the symbols in the codebase and write them to a database".to_string(), 10, cx)
- });
-
- cx.spawn(|this, cx| async move {
- let results = task.await?;
- let duration = t0.elapsed();
-
- println!("search took {:?}", duration);
- println!("results {:?}", results);
-
- anyhow::Ok(())
- }).detach()
+ move |workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>| {
+ let vector_store = vector_store.clone();
+ workspace.toggle_modal(cx, |workspace, cx| {
+ let project = workspace.project().clone();
+ let workspace = cx.weak_handle();
+ cx.add_view(|cx| {
+ SemanticSearch::new(
+ SemanticSearchDelegate::new(workspace, project, vector_store),
+ cx,
+ )
+ })
+ })
}
});
+ SemanticSearch::init(cx);
+ // cx.add_action({
+ // let vector_store = vector_store.clone();
+ // move |workspace: &mut Workspace, _: &TestSearch, cx: &mut ViewContext<Workspace>| {
+ // let t0 = std::time::Instant::now();
+ // let task = vector_store.update(cx, |store, cx| {
+ // store.search("compute embeddings for all of the symbols in the codebase and write them to a database".to_string(), 10, cx)
+ // });
+
+ // cx.spawn(|this, cx| async move {
+ // let results = task.await?;
+ // let duration = t0.elapsed();
+
+ // println!("search took {:?}", duration);
+ // println!("results {:?}", results);
+
+ // anyhow::Ok(())
+ // }).detach()
+ // }
+ // });
}
#[derive(Debug)]
@@ -87,7 +103,7 @@ pub struct IndexedFile {
documents: Vec<Document>,
}
-struct VectorStore {
+pub struct VectorStore {
fs: Arc<dyn Fs>,
database_url: Arc<str>,
embedding_provider: Arc<dyn EmbeddingProvider>,