Remove 2 suffix for fs, db, semantic_index, prettier
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
324ac96977
commit
5ddd298b4d
75 changed files with 455 additions and 12973 deletions
|
@ -11,16 +11,13 @@ doctest = false
|
|||
[dependencies]
|
||||
ai = { path = "../ai" }
|
||||
collections = { path = "../collections" }
|
||||
gpui = { path = "../gpui" }
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
language = { path = "../language" }
|
||||
project = { path = "../project" }
|
||||
workspace = { path = "../workspace" }
|
||||
util = { path = "../util" }
|
||||
picker = { path = "../picker" }
|
||||
theme = { path = "../theme" }
|
||||
editor = { path = "../editor" }
|
||||
rpc = { path = "../rpc" }
|
||||
settings = { path = "../settings" }
|
||||
settings = { package = "settings2", path = "../settings2" }
|
||||
anyhow.workspace = true
|
||||
postage.workspace = true
|
||||
futures.workspace = true
|
||||
|
@ -44,12 +41,12 @@ ndarray = { version = "0.15.0" }
|
|||
[dev-dependencies]
|
||||
ai = { path = "../ai", features = ["test-support"] }
|
||||
collections = { path = "../collections", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
rpc = { path = "../rpc", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
settings = { path = "../settings", features = ["test-support"]}
|
||||
settings = { package = "settings2", path = "../settings2", features = ["test-support"]}
|
||||
rust-embed = { version = "8.0", features = ["include-exclude"] }
|
||||
client = { path = "../client" }
|
||||
node_runtime = { path = "../node_runtime"}
|
||||
|
|
|
@ -6,7 +6,7 @@ use ai::embedding::Embedding;
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::HashMap;
|
||||
use futures::channel::oneshot;
|
||||
use gpui::executor;
|
||||
use gpui::BackgroundExecutor;
|
||||
use ndarray::{Array1, Array2};
|
||||
use ordered_float::OrderedFloat;
|
||||
use project::Fs;
|
||||
|
@ -48,7 +48,7 @@ impl VectorDatabase {
|
|||
pub async fn new(
|
||||
fs: Arc<dyn Fs>,
|
||||
path: Arc<Path>,
|
||||
executor: Arc<executor::Background>,
|
||||
executor: BackgroundExecutor,
|
||||
) -> Result<Self> {
|
||||
if let Some(db_directory) = path.parent() {
|
||||
fs.create_dir(db_directory).await?;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{parsing::Span, JobHandle};
|
||||
use ai::embedding::EmbeddingProvider;
|
||||
use gpui::executor::Background;
|
||||
use gpui::BackgroundExecutor;
|
||||
use parking_lot::Mutex;
|
||||
use smol::channel;
|
||||
use std::{mem, ops::Range, path::Path, sync::Arc, time::SystemTime};
|
||||
|
@ -37,7 +37,7 @@ impl PartialEq for FileToEmbed {
|
|||
pub struct EmbeddingQueue {
|
||||
embedding_provider: Arc<dyn EmbeddingProvider>,
|
||||
pending_batch: Vec<FileFragmentToEmbed>,
|
||||
executor: Arc<Background>,
|
||||
executor: BackgroundExecutor,
|
||||
pending_batch_token_count: usize,
|
||||
finished_files_tx: channel::Sender<FileToEmbed>,
|
||||
finished_files_rx: channel::Receiver<FileToEmbed>,
|
||||
|
@ -50,7 +50,10 @@ pub struct FileFragmentToEmbed {
|
|||
}
|
||||
|
||||
impl EmbeddingQueue {
|
||||
pub fn new(embedding_provider: Arc<dyn EmbeddingProvider>, executor: Arc<Background>) -> Self {
|
||||
pub fn new(
|
||||
embedding_provider: Arc<dyn EmbeddingProvider>,
|
||||
executor: BackgroundExecutor,
|
||||
) -> Self {
|
||||
let (finished_files_tx, finished_files_rx) = channel::unbounded();
|
||||
Self {
|
||||
embedding_provider,
|
||||
|
|
|
@ -9,12 +9,15 @@ mod semantic_index_tests;
|
|||
use crate::semantic_index_settings::SemanticIndexSettings;
|
||||
use ai::embedding::{Embedding, EmbeddingProvider};
|
||||
use ai::providers::open_ai::OpenAIEmbeddingProvider;
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{BTreeMap, HashMap, HashSet};
|
||||
use db::VectorDatabase;
|
||||
use embedding_queue::{EmbeddingQueue, FileToEmbed};
|
||||
use futures::{future, FutureExt, StreamExt};
|
||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle};
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, BorrowWindow, Context, Model, ModelContext, Task, ViewContext,
|
||||
WeakModel,
|
||||
};
|
||||
use language::{Anchor, Bias, Buffer, Language, LanguageRegistry};
|
||||
use lazy_static::lazy_static;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
@ -22,6 +25,7 @@ use parking_lot::Mutex;
|
|||
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
|
||||
use postage::watch;
|
||||
use project::{Fs, PathChange, Project, ProjectEntryId, Worktree, WorktreeId};
|
||||
use settings::Settings;
|
||||
use smol::channel;
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
|
@ -35,7 +39,7 @@ use std::{
|
|||
};
|
||||
use util::paths::PathMatcher;
|
||||
use util::{channel::RELEASE_CHANNEL_NAME, http::HttpClient, paths::EMBEDDINGS_DIR, ResultExt};
|
||||
use workspace::WorkspaceCreated;
|
||||
use workspace::Workspace;
|
||||
|
||||
const SEMANTIC_INDEX_VERSION: usize = 11;
|
||||
const BACKGROUND_INDEXING_DELAY: Duration = Duration::from_secs(5 * 60);
|
||||
|
@ -51,54 +55,54 @@ pub fn init(
|
|||
language_registry: Arc<LanguageRegistry>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
settings::register::<SemanticIndexSettings>(cx);
|
||||
SemanticIndexSettings::register(cx);
|
||||
|
||||
let db_file_path = EMBEDDINGS_DIR
|
||||
.join(Path::new(RELEASE_CHANNEL_NAME.as_str()))
|
||||
.join("embeddings_db");
|
||||
|
||||
cx.subscribe_global::<WorkspaceCreated, _>({
|
||||
move |event, cx| {
|
||||
cx.observe_new_views(
|
||||
|workspace: &mut Workspace, cx: &mut ViewContext<Workspace>| {
|
||||
let Some(semantic_index) = SemanticIndex::global(cx) else {
|
||||
return;
|
||||
};
|
||||
let workspace = &event.0;
|
||||
if let Some(workspace) = workspace.upgrade(cx) {
|
||||
let project = workspace.read(cx).project().clone();
|
||||
if project.read(cx).is_local() {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let project = workspace.project().clone();
|
||||
|
||||
if project.read(cx).is_local() {
|
||||
cx.app_mut()
|
||||
.spawn(|mut cx| async move {
|
||||
let previously_indexed = semantic_index
|
||||
.update(&mut cx, |index, cx| {
|
||||
index.project_previously_indexed(&project, cx)
|
||||
})
|
||||
})?
|
||||
.await?;
|
||||
if previously_indexed {
|
||||
semantic_index
|
||||
.update(&mut cx, |index, cx| index.index_project(project, cx))
|
||||
.update(&mut cx, |index, cx| index.index_project(project, cx))?
|
||||
.await?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
||||
cx.spawn(move |mut cx| async move {
|
||||
cx.spawn(move |cx| async move {
|
||||
let semantic_index = SemanticIndex::new(
|
||||
fs,
|
||||
db_file_path,
|
||||
Arc::new(OpenAIEmbeddingProvider::new(http_client, cx.background())),
|
||||
Arc::new(OpenAIEmbeddingProvider::new(
|
||||
http_client,
|
||||
cx.background_executor().clone(),
|
||||
)),
|
||||
language_registry,
|
||||
cx.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.set_global(semantic_index.clone());
|
||||
});
|
||||
cx.update(|cx| cx.set_global(semantic_index.clone()))?;
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
|
@ -124,7 +128,7 @@ pub struct SemanticIndex {
|
|||
parsing_files_tx: channel::Sender<(Arc<HashMap<SpanDigest, Embedding>>, PendingFile)>,
|
||||
_embedding_task: Task<()>,
|
||||
_parsing_files_tasks: Vec<Task<()>>,
|
||||
projects: HashMap<WeakModelHandle<Project>, ProjectState>,
|
||||
projects: HashMap<WeakModel<Project>, ProjectState>,
|
||||
}
|
||||
|
||||
struct ProjectState {
|
||||
|
@ -229,12 +233,12 @@ impl ProjectState {
|
|||
pending_file_count_tx,
|
||||
pending_index: 0,
|
||||
_subscription: subscription,
|
||||
_observe_pending_file_count: cx.spawn_weak({
|
||||
_observe_pending_file_count: cx.spawn({
|
||||
let mut pending_file_count_rx = pending_file_count_rx.clone();
|
||||
|this, mut cx| async move {
|
||||
while let Some(_) = pending_file_count_rx.next().await {
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |_, cx| cx.notify());
|
||||
if this.update(&mut cx, |_, cx| cx.notify()).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,21 +268,21 @@ pub struct PendingFile {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct SearchResult {
|
||||
pub buffer: ModelHandle<Buffer>,
|
||||
pub buffer: Model<Buffer>,
|
||||
pub range: Range<Anchor>,
|
||||
pub similarity: OrderedFloat<f32>,
|
||||
}
|
||||
|
||||
impl SemanticIndex {
|
||||
pub fn global(cx: &mut AppContext) -> Option<ModelHandle<SemanticIndex>> {
|
||||
if cx.has_global::<ModelHandle<Self>>() {
|
||||
Some(cx.global::<ModelHandle<SemanticIndex>>().clone())
|
||||
pub fn global(cx: &mut AppContext) -> Option<Model<SemanticIndex>> {
|
||||
if cx.has_global::<Model<Self>>() {
|
||||
Some(cx.global::<Model<SemanticIndex>>().clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn authenticate(&mut self, cx: &AppContext) -> bool {
|
||||
pub fn authenticate(&mut self, cx: &mut AppContext) -> bool {
|
||||
if !self.embedding_provider.has_credentials() {
|
||||
self.embedding_provider.retrieve_credentials(cx);
|
||||
} else {
|
||||
|
@ -293,10 +297,10 @@ impl SemanticIndex {
|
|||
}
|
||||
|
||||
pub fn enabled(cx: &AppContext) -> bool {
|
||||
settings::get::<SemanticIndexSettings>(cx).enabled
|
||||
SemanticIndexSettings::get_global(cx).enabled
|
||||
}
|
||||
|
||||
pub fn status(&self, project: &ModelHandle<Project>) -> SemanticIndexStatus {
|
||||
pub fn status(&self, project: &Model<Project>) -> SemanticIndexStatus {
|
||||
if !self.is_authenticated() {
|
||||
return SemanticIndexStatus::NotAuthenticated;
|
||||
}
|
||||
|
@ -326,21 +330,22 @@ impl SemanticIndex {
|
|||
embedding_provider: Arc<dyn EmbeddingProvider>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<ModelHandle<Self>> {
|
||||
) -> Result<Model<Self>> {
|
||||
let t0 = Instant::now();
|
||||
let database_path = Arc::from(database_path);
|
||||
let db = VectorDatabase::new(fs.clone(), database_path, cx.background()).await?;
|
||||
let db = VectorDatabase::new(fs.clone(), database_path, cx.background_executor().clone())
|
||||
.await?;
|
||||
|
||||
log::trace!(
|
||||
"db initialization took {:?} milliseconds",
|
||||
t0.elapsed().as_millis()
|
||||
);
|
||||
|
||||
Ok(cx.add_model(|cx| {
|
||||
cx.new_model(|cx| {
|
||||
let t0 = Instant::now();
|
||||
let embedding_queue =
|
||||
EmbeddingQueue::new(embedding_provider.clone(), cx.background().clone());
|
||||
let _embedding_task = cx.background().spawn({
|
||||
EmbeddingQueue::new(embedding_provider.clone(), cx.background_executor().clone());
|
||||
let _embedding_task = cx.background_executor().spawn({
|
||||
let embedded_files = embedding_queue.finished_files();
|
||||
let db = db.clone();
|
||||
async move {
|
||||
|
@ -357,13 +362,13 @@ impl SemanticIndex {
|
|||
channel::unbounded::<(Arc<HashMap<SpanDigest, Embedding>>, PendingFile)>();
|
||||
let embedding_queue = Arc::new(Mutex::new(embedding_queue));
|
||||
let mut _parsing_files_tasks = Vec::new();
|
||||
for _ in 0..cx.background().num_cpus() {
|
||||
for _ in 0..cx.background_executor().num_cpus() {
|
||||
let fs = fs.clone();
|
||||
let mut parsing_files_rx = parsing_files_rx.clone();
|
||||
let embedding_provider = embedding_provider.clone();
|
||||
let embedding_queue = embedding_queue.clone();
|
||||
let background = cx.background().clone();
|
||||
_parsing_files_tasks.push(cx.background().spawn(async move {
|
||||
let background = cx.background_executor().clone();
|
||||
_parsing_files_tasks.push(cx.background_executor().spawn(async move {
|
||||
let mut retriever = CodeContextRetriever::new(embedding_provider.clone());
|
||||
loop {
|
||||
let mut timer = background.timer(EMBEDDING_QUEUE_FLUSH_TIMEOUT).fuse();
|
||||
|
@ -405,7 +410,7 @@ impl SemanticIndex {
|
|||
_parsing_files_tasks,
|
||||
projects: Default::default(),
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
async fn parse_file(
|
||||
|
@ -449,12 +454,12 @@ impl SemanticIndex {
|
|||
|
||||
pub fn project_previously_indexed(
|
||||
&mut self,
|
||||
project: &ModelHandle<Project>,
|
||||
project: &Model<Project>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<bool>> {
|
||||
let worktrees_indexed_previously = project
|
||||
.read(cx)
|
||||
.worktrees(cx)
|
||||
.worktrees()
|
||||
.map(|worktree| {
|
||||
self.db
|
||||
.worktree_previously_indexed(&worktree.read(cx).abs_path())
|
||||
|
@ -473,7 +478,7 @@ impl SemanticIndex {
|
|||
|
||||
fn project_entries_changed(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
project: Model<Project>,
|
||||
worktree_id: WorktreeId,
|
||||
changes: Arc<[(Arc<Path>, ProjectEntryId, PathChange)]>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
|
@ -495,22 +500,25 @@ impl SemanticIndex {
|
|||
};
|
||||
worktree_state.paths_changed(changes, worktree);
|
||||
if let WorktreeState::Registered(_) = worktree_state {
|
||||
cx.spawn_weak(|this, mut cx| async move {
|
||||
cx.background().timer(BACKGROUND_INDEXING_DELAY).await;
|
||||
if let Some((this, project)) = this.upgrade(&cx).zip(project.upgrade(&cx)) {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
cx.background_executor()
|
||||
.timer(BACKGROUND_INDEXING_DELAY)
|
||||
.await;
|
||||
if let Some((this, project)) = this.upgrade().zip(project.upgrade()) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.index_project(project, cx).detach_and_log_err(cx)
|
||||
});
|
||||
})?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn register_worktree(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
worktree: ModelHandle<Worktree>,
|
||||
project: Model<Project>,
|
||||
worktree: Model<Worktree>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let project = project.downgrade();
|
||||
|
@ -536,16 +544,18 @@ impl SemanticIndex {
|
|||
scan_complete.await;
|
||||
let db_id = db.find_or_create_worktree(worktree_abs_path).await?;
|
||||
let mut file_mtimes = db.get_file_mtimes(db_id).await?;
|
||||
let worktree = if let Some(project) = project.upgrade(&cx) {
|
||||
let worktree = if let Some(project) = project.upgrade() {
|
||||
project
|
||||
.read_with(&cx, |project, cx| project.worktree_for_id(worktree_id, cx))
|
||||
.ok_or_else(|| anyhow!("worktree not found"))?
|
||||
.ok()
|
||||
.flatten()
|
||||
.context("worktree not found")?
|
||||
} else {
|
||||
return anyhow::Ok(());
|
||||
};
|
||||
let worktree = worktree.read_with(&cx, |worktree, _| worktree.snapshot());
|
||||
let worktree = worktree.read_with(&cx, |worktree, _| worktree.snapshot())?;
|
||||
let mut changed_paths = cx
|
||||
.background()
|
||||
.background_executor()
|
||||
.spawn(async move {
|
||||
let mut changed_paths = BTreeMap::new();
|
||||
for file in worktree.files(false, 0) {
|
||||
|
@ -607,10 +617,8 @@ impl SemanticIndex {
|
|||
let project_state = this
|
||||
.projects
|
||||
.get_mut(&project)
|
||||
.ok_or_else(|| anyhow!("project not registered"))?;
|
||||
let project = project
|
||||
.upgrade(cx)
|
||||
.ok_or_else(|| anyhow!("project was dropped"))?;
|
||||
.context("project not registered")?;
|
||||
let project = project.upgrade().context("project was dropped")?;
|
||||
|
||||
if let Some(WorktreeState::Registering(state)) =
|
||||
project_state.worktrees.remove(&worktree_id)
|
||||
|
@ -627,7 +635,7 @@ impl SemanticIndex {
|
|||
this.index_project(project, cx).detach_and_log_err(cx);
|
||||
|
||||
anyhow::Ok(())
|
||||
})?;
|
||||
})??;
|
||||
|
||||
anyhow::Ok(())
|
||||
};
|
||||
|
@ -639,6 +647,7 @@ impl SemanticIndex {
|
|||
project_state.worktrees.remove(&worktree_id);
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
*done_tx.borrow_mut() = Some(());
|
||||
|
@ -654,11 +663,7 @@ impl SemanticIndex {
|
|||
);
|
||||
}
|
||||
|
||||
fn project_worktrees_changed(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
fn project_worktrees_changed(&mut self, project: Model<Project>, cx: &mut ModelContext<Self>) {
|
||||
let project_state = if let Some(project_state) = self.projects.get_mut(&project.downgrade())
|
||||
{
|
||||
project_state
|
||||
|
@ -668,7 +673,7 @@ impl SemanticIndex {
|
|||
|
||||
let mut worktrees = project
|
||||
.read(cx)
|
||||
.worktrees(cx)
|
||||
.worktrees()
|
||||
.filter(|worktree| worktree.read(cx).is_local())
|
||||
.collect::<Vec<_>>();
|
||||
let worktree_ids = worktrees
|
||||
|
@ -691,10 +696,7 @@ impl SemanticIndex {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pending_file_count(
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
) -> Option<watch::Receiver<usize>> {
|
||||
pub fn pending_file_count(&self, project: &Model<Project>) -> Option<watch::Receiver<usize>> {
|
||||
Some(
|
||||
self.projects
|
||||
.get(&project.downgrade())?
|
||||
|
@ -705,7 +707,7 @@ impl SemanticIndex {
|
|||
|
||||
pub fn search_project(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
project: Model<Project>,
|
||||
query: String,
|
||||
limit: usize,
|
||||
includes: Vec<PathMatcher>,
|
||||
|
@ -727,7 +729,7 @@ impl SemanticIndex {
|
|||
.embed_batch(vec![query])
|
||||
.await?
|
||||
.pop()
|
||||
.ok_or_else(|| anyhow!("could not embed query"))?;
|
||||
.context("could not embed query")?;
|
||||
log::trace!("Embedding Search Query: {:?}ms", t0.elapsed().as_millis());
|
||||
|
||||
let search_start = Instant::now();
|
||||
|
@ -740,10 +742,10 @@ impl SemanticIndex {
|
|||
&excludes,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
})?;
|
||||
let file_results = this.update(&mut cx, |this, cx| {
|
||||
this.search_files(project, query, limit, includes, excludes, cx)
|
||||
});
|
||||
})?;
|
||||
let (modified_buffer_results, file_results) =
|
||||
futures::join!(modified_buffer_results, file_results);
|
||||
|
||||
|
@ -768,7 +770,7 @@ impl SemanticIndex {
|
|||
|
||||
pub fn search_files(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
project: Model<Project>,
|
||||
query: Embedding,
|
||||
limit: usize,
|
||||
includes: Vec<PathMatcher>,
|
||||
|
@ -778,14 +780,18 @@ impl SemanticIndex {
|
|||
let db_path = self.db.path().clone();
|
||||
let fs = self.fs.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let database =
|
||||
VectorDatabase::new(fs.clone(), db_path.clone(), cx.background()).await?;
|
||||
let database = VectorDatabase::new(
|
||||
fs.clone(),
|
||||
db_path.clone(),
|
||||
cx.background_executor().clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let worktree_db_ids = this.read_with(&cx, |this, _| {
|
||||
let project_state = this
|
||||
.projects
|
||||
.get(&project.downgrade())
|
||||
.ok_or_else(|| anyhow!("project was not indexed"))?;
|
||||
.context("project was not indexed")?;
|
||||
let worktree_db_ids = project_state
|
||||
.worktrees
|
||||
.values()
|
||||
|
@ -798,13 +804,13 @@ impl SemanticIndex {
|
|||
})
|
||||
.collect::<Vec<i64>>();
|
||||
anyhow::Ok(worktree_db_ids)
|
||||
})?;
|
||||
})??;
|
||||
|
||||
let file_ids = database
|
||||
.retrieve_included_file_ids(&worktree_db_ids, &includes, &excludes)
|
||||
.await?;
|
||||
|
||||
let batch_n = cx.background().num_cpus();
|
||||
let batch_n = cx.background_executor().num_cpus();
|
||||
let ids_len = file_ids.clone().len();
|
||||
let minimum_batch_size = 50;
|
||||
|
||||
|
@ -824,9 +830,10 @@ impl SemanticIndex {
|
|||
let fs = fs.clone();
|
||||
let db_path = db_path.clone();
|
||||
let query = query.clone();
|
||||
if let Some(db) = VectorDatabase::new(fs, db_path.clone(), cx.background())
|
||||
.await
|
||||
.log_err()
|
||||
if let Some(db) =
|
||||
VectorDatabase::new(fs, db_path.clone(), cx.background_executor().clone())
|
||||
.await
|
||||
.log_err()
|
||||
{
|
||||
batch_results.push(async move {
|
||||
db.top_k_search(&query, limit, batch.as_slice()).await
|
||||
|
@ -864,6 +871,7 @@ impl SemanticIndex {
|
|||
let mut ranges = Vec::new();
|
||||
let weak_project = project.downgrade();
|
||||
project.update(&mut cx, |project, cx| {
|
||||
let this = this.upgrade().context("index was dropped")?;
|
||||
for (worktree_db_id, file_path, byte_range) in spans {
|
||||
let project_state =
|
||||
if let Some(state) = this.read(cx).projects.get(&weak_project) {
|
||||
|
@ -878,7 +886,7 @@ impl SemanticIndex {
|
|||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
})??;
|
||||
|
||||
let buffers = futures::future::join_all(tasks).await;
|
||||
Ok(buffers
|
||||
|
@ -887,11 +895,13 @@ impl SemanticIndex {
|
|||
.zip(scores)
|
||||
.filter_map(|((buffer, range), similarity)| {
|
||||
let buffer = buffer.log_err()?;
|
||||
let range = buffer.read_with(&cx, |buffer, _| {
|
||||
let start = buffer.clip_offset(range.start, Bias::Left);
|
||||
let end = buffer.clip_offset(range.end, Bias::Right);
|
||||
buffer.anchor_before(start)..buffer.anchor_after(end)
|
||||
});
|
||||
let range = buffer
|
||||
.read_with(&cx, |buffer, _| {
|
||||
let start = buffer.clip_offset(range.start, Bias::Left);
|
||||
let end = buffer.clip_offset(range.end, Bias::Right);
|
||||
buffer.anchor_before(start)..buffer.anchor_after(end)
|
||||
})
|
||||
.log_err()?;
|
||||
Some(SearchResult {
|
||||
buffer,
|
||||
range,
|
||||
|
@ -904,7 +914,7 @@ impl SemanticIndex {
|
|||
|
||||
fn search_modified_buffers(
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
project: &Model<Project>,
|
||||
query: Embedding,
|
||||
limit: usize,
|
||||
includes: &[PathMatcher],
|
||||
|
@ -913,7 +923,7 @@ impl SemanticIndex {
|
|||
) -> Task<Result<Vec<SearchResult>>> {
|
||||
let modified_buffers = project
|
||||
.read(cx)
|
||||
.opened_buffers(cx)
|
||||
.opened_buffers()
|
||||
.into_iter()
|
||||
.filter_map(|buffer_handle| {
|
||||
let buffer = buffer_handle.read(cx);
|
||||
|
@ -941,8 +951,8 @@ impl SemanticIndex {
|
|||
let embedding_provider = self.embedding_provider.clone();
|
||||
let fs = self.fs.clone();
|
||||
let db_path = self.db.path().clone();
|
||||
let background = cx.background().clone();
|
||||
cx.background().spawn(async move {
|
||||
let background = cx.background_executor().clone();
|
||||
cx.background_executor().spawn(async move {
|
||||
let db = VectorDatabase::new(fs, db_path.clone(), background).await?;
|
||||
let mut results = Vec::<SearchResult>::new();
|
||||
|
||||
|
@ -996,7 +1006,7 @@ impl SemanticIndex {
|
|||
|
||||
pub fn index_project(
|
||||
&mut self,
|
||||
project: ModelHandle<Project>,
|
||||
project: Model<Project>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
if !self.is_authenticated() {
|
||||
|
@ -1038,7 +1048,7 @@ impl SemanticIndex {
|
|||
let project_state = this
|
||||
.projects
|
||||
.get_mut(&project.downgrade())
|
||||
.ok_or_else(|| anyhow!("project was dropped"))?;
|
||||
.context("project was dropped")?;
|
||||
let pending_file_count_tx = &project_state.pending_file_count_tx;
|
||||
|
||||
project_state
|
||||
|
@ -1080,9 +1090,9 @@ impl SemanticIndex {
|
|||
});
|
||||
|
||||
anyhow::Ok(())
|
||||
})?;
|
||||
})??;
|
||||
|
||||
cx.background()
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
for (worktree_db_id, path) in files_to_delete {
|
||||
db.delete_file(worktree_db_id, path).await.log_err();
|
||||
|
@ -1138,11 +1148,11 @@ impl SemanticIndex {
|
|||
let project_state = this
|
||||
.projects
|
||||
.get_mut(&project.downgrade())
|
||||
.ok_or_else(|| anyhow!("project was dropped"))?;
|
||||
.context("project was dropped")?;
|
||||
project_state.pending_index -= 1;
|
||||
cx.notify();
|
||||
anyhow::Ok(())
|
||||
})?;
|
||||
})??;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
@ -1150,15 +1160,15 @@ impl SemanticIndex {
|
|||
|
||||
fn wait_for_worktree_registration(
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
project: &Model<Project>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let project = project.downgrade();
|
||||
cx.spawn_weak(|this, cx| async move {
|
||||
cx.spawn(|this, cx| async move {
|
||||
loop {
|
||||
let mut pending_worktrees = Vec::new();
|
||||
this.upgrade(&cx)
|
||||
.ok_or_else(|| anyhow!("semantic index dropped"))?
|
||||
this.upgrade()
|
||||
.context("semantic index dropped")?
|
||||
.read_with(&cx, |this, _| {
|
||||
if let Some(project) = this.projects.get(&project) {
|
||||
for worktree in project.worktrees.values() {
|
||||
|
@ -1167,7 +1177,7 @@ impl SemanticIndex {
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})?;
|
||||
|
||||
if pending_worktrees.is_empty() {
|
||||
break;
|
||||
|
@ -1230,17 +1240,13 @@ impl SemanticIndex {
|
|||
} else {
|
||||
embeddings.next()
|
||||
};
|
||||
let embedding = embedding.ok_or_else(|| anyhow!("failed to embed spans"))?;
|
||||
let embedding = embedding.context("failed to embed spans")?;
|
||||
span.embedding = Some(embedding);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for SemanticIndex {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl Drop for JobHandle {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = Arc::get_mut(&mut self.tx) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
use settings::Settings;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct SemanticIndexSettings {
|
||||
|
@ -13,7 +13,7 @@ pub struct SemanticIndexSettingsContent {
|
|||
pub enabled: Option<bool>,
|
||||
}
|
||||
|
||||
impl Setting for SemanticIndexSettings {
|
||||
impl Settings for SemanticIndexSettings {
|
||||
const KEY: Option<&'static str> = Some("semantic_index");
|
||||
|
||||
type FileContent = SemanticIndexSettingsContent;
|
||||
|
@ -21,7 +21,7 @@ impl Setting for SemanticIndexSettings {
|
|||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &gpui::AppContext,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@ use crate::{
|
|||
};
|
||||
use ai::test::FakeEmbeddingProvider;
|
||||
|
||||
use gpui::{executor::Deterministic, Task, TestAppContext};
|
||||
use gpui::{Task, TestAppContext};
|
||||
use language::{Language, LanguageConfig, LanguageRegistry, ToOffset};
|
||||
use parking_lot::Mutex;
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::{project_settings::ProjectSettings, FakeFs, Fs, Project};
|
||||
use rand::{rngs::StdRng, Rng};
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use settings::{Settings, SettingsStore};
|
||||
use std::{path::Path, sync::Arc, time::SystemTime};
|
||||
use unindent::Unindent;
|
||||
use util::{paths::PathMatcher, RandomCharIter};
|
||||
|
@ -26,10 +26,10 @@ fn init_logger() {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_semantic_index(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
|
||||
async fn test_semantic_index(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let fs = FakeFs::new(cx.background_executor.clone());
|
||||
fs.insert_tree(
|
||||
"/the-root",
|
||||
json!({
|
||||
|
@ -91,9 +91,10 @@ async fn test_semantic_index(deterministic: Arc<Deterministic>, cx: &mut TestApp
|
|||
});
|
||||
let pending_file_count =
|
||||
semantic_index.read_with(cx, |index, _| index.pending_file_count(&project).unwrap());
|
||||
deterministic.run_until_parked();
|
||||
cx.background_executor.run_until_parked();
|
||||
assert_eq!(*pending_file_count.borrow(), 3);
|
||||
deterministic.advance_clock(EMBEDDING_QUEUE_FLUSH_TIMEOUT);
|
||||
cx.background_executor
|
||||
.advance_clock(EMBEDDING_QUEUE_FLUSH_TIMEOUT);
|
||||
assert_eq!(*pending_file_count.borrow(), 0);
|
||||
|
||||
let search_results = search_results.await.unwrap();
|
||||
|
@ -170,13 +171,15 @@ async fn test_semantic_index(deterministic: Arc<Deterministic>, cx: &mut TestApp
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
deterministic.advance_clock(EMBEDDING_QUEUE_FLUSH_TIMEOUT);
|
||||
cx.background_executor
|
||||
.advance_clock(EMBEDDING_QUEUE_FLUSH_TIMEOUT);
|
||||
|
||||
let prev_embedding_count = embedding_provider.embedding_count();
|
||||
let index = semantic_index.update(cx, |store, cx| store.index_project(project.clone(), cx));
|
||||
deterministic.run_until_parked();
|
||||
cx.background_executor.run_until_parked();
|
||||
assert_eq!(*pending_file_count.borrow(), 1);
|
||||
deterministic.advance_clock(EMBEDDING_QUEUE_FLUSH_TIMEOUT);
|
||||
cx.background_executor
|
||||
.advance_clock(EMBEDDING_QUEUE_FLUSH_TIMEOUT);
|
||||
assert_eq!(*pending_file_count.borrow(), 0);
|
||||
index.await.unwrap();
|
||||
|
||||
|
@ -220,13 +223,13 @@ async fn test_embedding_batching(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
|
||||
let embedding_provider = Arc::new(FakeEmbeddingProvider::default());
|
||||
|
||||
let mut queue = EmbeddingQueue::new(embedding_provider.clone(), cx.background());
|
||||
let mut queue = EmbeddingQueue::new(embedding_provider.clone(), cx.background_executor.clone());
|
||||
for file in &files {
|
||||
queue.push(file.clone());
|
||||
}
|
||||
queue.flush();
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
cx.background_executor.run_until_parked();
|
||||
let finished_files = queue.finished_files();
|
||||
let mut embedded_files: Vec<_> = files
|
||||
.iter()
|
||||
|
@ -1686,8 +1689,9 @@ fn test_subtract_ranges() {
|
|||
|
||||
fn init_test(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
settings::register::<SemanticIndexSettings>(cx);
|
||||
settings::register::<ProjectSettings>(cx);
|
||||
let settings_store = SettingsStore::test(cx);
|
||||
cx.set_global(settings_store);
|
||||
SemanticIndexSettings::register(cx);
|
||||
ProjectSettings::register(cx);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue