Use worktree qualified paths in agent file context + some code cleanup (#27943)
Release Notes: - N/A
This commit is contained in:
parent
142f9917d0
commit
b7b7f1ccdd
2 changed files with 53 additions and 99 deletions
|
@ -6,7 +6,7 @@ use anyhow::{Context as _, Result, anyhow};
|
||||||
use collections::{BTreeMap, HashMap, HashSet};
|
use collections::{BTreeMap, HashMap, HashSet};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use futures::{self, Future, FutureExt, future};
|
use futures::{self, Future, FutureExt, future};
|
||||||
use gpui::{App, AppContext as _, AsyncApp, Context, Entity, SharedString, Task, WeakEntity};
|
use gpui::{App, AppContext as _, Context, Entity, SharedString, Task, WeakEntity};
|
||||||
use language::{Buffer, File};
|
use language::{Buffer, File};
|
||||||
use project::{ProjectItem, ProjectPath, Worktree};
|
use project::{ProjectItem, ProjectPath, Worktree};
|
||||||
use rope::Rope;
|
use rope::Rope;
|
||||||
|
@ -95,8 +95,8 @@ impl ContextStore {
|
||||||
project.open_buffer(project_path.clone(), cx)
|
project.open_buffer(project_path.clone(), cx)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let buffer_entity = open_buffer_task.await?;
|
let buffer = open_buffer_task.await?;
|
||||||
let buffer_id = this.update(cx, |_, cx| buffer_entity.read(cx).remote_id())?;
|
let buffer_id = this.update(cx, |_, cx| buffer.read(cx).remote_id())?;
|
||||||
|
|
||||||
let already_included = this.update(cx, |this, _cx| {
|
let already_included = this.update(cx, |this, _cx| {
|
||||||
match this.will_include_buffer(buffer_id, &project_path.path) {
|
match this.will_include_buffer(buffer_id, &project_path.path) {
|
||||||
|
@ -115,16 +115,8 @@ impl ContextStore {
|
||||||
return anyhow::Ok(());
|
return anyhow::Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (buffer_info, text_task) = this.update(cx, |_, cx| {
|
let (buffer_info, text_task) =
|
||||||
let buffer = buffer_entity.read(cx);
|
this.update(cx, |_, cx| collect_buffer_info_and_text(buffer, None, cx))??;
|
||||||
collect_buffer_info_and_text(
|
|
||||||
project_path.path.clone(),
|
|
||||||
buffer_entity,
|
|
||||||
buffer,
|
|
||||||
None,
|
|
||||||
cx.to_async(),
|
|
||||||
)
|
|
||||||
})??;
|
|
||||||
|
|
||||||
let text = text_task.await;
|
let text = text_task.await;
|
||||||
|
|
||||||
|
@ -138,23 +130,12 @@ impl ContextStore {
|
||||||
|
|
||||||
pub fn add_file_from_buffer(
|
pub fn add_file_from_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer_entity: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let (buffer_info, text_task) = this.update(cx, |_, cx| {
|
let (buffer_info, text_task) =
|
||||||
let buffer = buffer_entity.read(cx);
|
this.update(cx, |_, cx| collect_buffer_info_and_text(buffer, None, cx))??;
|
||||||
let Some(file) = buffer.file() else {
|
|
||||||
return Err(anyhow!("Buffer has no path."));
|
|
||||||
};
|
|
||||||
collect_buffer_info_and_text(
|
|
||||||
file.path().clone(),
|
|
||||||
buffer_entity,
|
|
||||||
buffer,
|
|
||||||
None,
|
|
||||||
cx.to_async(),
|
|
||||||
)
|
|
||||||
})??;
|
|
||||||
|
|
||||||
let text = text_task.await;
|
let text = text_task.await;
|
||||||
|
|
||||||
|
@ -169,10 +150,8 @@ impl ContextStore {
|
||||||
fn insert_file(&mut self, context_buffer: ContextBuffer) {
|
fn insert_file(&mut self, context_buffer: ContextBuffer) {
|
||||||
let id = self.next_context_id.post_inc();
|
let id = self.next_context_id.post_inc();
|
||||||
self.files.insert(context_buffer.id, id);
|
self.files.insert(context_buffer.id, id);
|
||||||
self.context.push(AssistantContext::File(FileContext {
|
self.context
|
||||||
id,
|
.push(AssistantContext::File(FileContext { id, context_buffer }));
|
||||||
context_buffer: context_buffer,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_directory(
|
pub fn add_directory(
|
||||||
|
@ -233,22 +212,13 @@ impl ContextStore {
|
||||||
let mut buffer_infos = Vec::new();
|
let mut buffer_infos = Vec::new();
|
||||||
let mut text_tasks = Vec::new();
|
let mut text_tasks = Vec::new();
|
||||||
this.update(cx, |_, cx| {
|
this.update(cx, |_, cx| {
|
||||||
for (path, buffer_entity) in files.into_iter().zip(buffers) {
|
// Skip all binary files and other non-UTF8 files
|
||||||
// Skip all binary files and other non-UTF8 files
|
for buffer in buffers.into_iter().flatten() {
|
||||||
if let Ok(buffer_entity) = buffer_entity {
|
if let Some((buffer_info, text_task)) =
|
||||||
let buffer = buffer_entity.read(cx);
|
collect_buffer_info_and_text(buffer, None, cx).log_err()
|
||||||
if let Some((buffer_info, text_task)) = collect_buffer_info_and_text(
|
{
|
||||||
path,
|
buffer_infos.push(buffer_info);
|
||||||
buffer_entity,
|
text_tasks.push(text_task);
|
||||||
buffer,
|
|
||||||
None,
|
|
||||||
cx.to_async(),
|
|
||||||
)
|
|
||||||
.log_err()
|
|
||||||
{
|
|
||||||
buffer_infos.push(buffer_info);
|
|
||||||
text_tasks.push(text_task);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
|
@ -298,12 +268,8 @@ impl ContextStore {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<bool>> {
|
) -> Task<Result<bool>> {
|
||||||
let buffer_ref = buffer.read(cx);
|
let buffer_ref = buffer.read(cx);
|
||||||
let Some(file) = buffer_ref.file() else {
|
|
||||||
return Task::ready(Err(anyhow!("Buffer has no path.")));
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(project_path) = buffer_ref.project_path(cx) else {
|
let Some(project_path) = buffer_ref.project_path(cx) else {
|
||||||
return Task::ready(Err(anyhow!("Buffer has no project path.")));
|
return Task::ready(Err(anyhow!("buffer has no path")));
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(symbols_for_path) = self.symbols_by_path.get(&project_path) {
|
if let Some(symbols_for_path) = self.symbols_by_path.get(&project_path) {
|
||||||
|
@ -326,16 +292,11 @@ impl ContextStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (buffer_info, collect_content_task) = match collect_buffer_info_and_text(
|
let (buffer_info, collect_content_task) =
|
||||||
file.path().clone(),
|
match collect_buffer_info_and_text(buffer, Some(symbol_enclosing_range.clone()), cx) {
|
||||||
buffer,
|
Ok((buffer_info, collect_context_task)) => (buffer_info, collect_context_task),
|
||||||
buffer_ref,
|
Err(err) => return Task::ready(Err(err)),
|
||||||
Some(symbol_enclosing_range.clone()),
|
};
|
||||||
cx.to_async(),
|
|
||||||
) {
|
|
||||||
Ok((buffer_info, collect_context_task)) => (buffer_info, collect_context_task),
|
|
||||||
Err(err) => return Task::ready(Err(err)),
|
|
||||||
};
|
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let content = collect_content_task.await;
|
let content = collect_content_task.await;
|
||||||
|
@ -616,16 +577,16 @@ pub enum FileInclusion {
|
||||||
|
|
||||||
// ContextBuffer without text.
|
// ContextBuffer without text.
|
||||||
struct BufferInfo {
|
struct BufferInfo {
|
||||||
buffer_entity: Entity<Buffer>,
|
|
||||||
file: Arc<dyn File>,
|
|
||||||
id: BufferId,
|
id: BufferId,
|
||||||
|
buffer: Entity<Buffer>,
|
||||||
|
file: Arc<dyn File>,
|
||||||
version: clock::Global,
|
version: clock::Global,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_context_buffer(info: BufferInfo, text: SharedString) -> ContextBuffer {
|
fn make_context_buffer(info: BufferInfo, text: SharedString) -> ContextBuffer {
|
||||||
ContextBuffer {
|
ContextBuffer {
|
||||||
id: info.id,
|
id: info.id,
|
||||||
buffer: info.buffer_entity,
|
buffer: info.buffer,
|
||||||
file: info.file,
|
file: info.file,
|
||||||
version: info.version,
|
version: info.version,
|
||||||
text,
|
text,
|
||||||
|
@ -644,34 +605,37 @@ fn make_context_symbol(
|
||||||
id: ContextSymbolId { name, range, path },
|
id: ContextSymbolId { name, range, path },
|
||||||
buffer_version: info.version,
|
buffer_version: info.version,
|
||||||
enclosing_range,
|
enclosing_range,
|
||||||
buffer: info.buffer_entity,
|
buffer: info.buffer,
|
||||||
text,
|
text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_buffer_info_and_text(
|
fn collect_buffer_info_and_text(
|
||||||
path: Arc<Path>,
|
buffer: Entity<Buffer>,
|
||||||
buffer_entity: Entity<Buffer>,
|
|
||||||
buffer: &Buffer,
|
|
||||||
range: Option<Range<Anchor>>,
|
range: Option<Range<Anchor>>,
|
||||||
cx: AsyncApp,
|
cx: &App,
|
||||||
) -> Result<(BufferInfo, Task<SharedString>)> {
|
) -> Result<(BufferInfo, Task<SharedString>)> {
|
||||||
let buffer_info = BufferInfo {
|
let buffer_ref = buffer.read(cx);
|
||||||
id: buffer.remote_id(),
|
let file = buffer_ref.file().context("file context must have a path")?;
|
||||||
buffer_entity,
|
|
||||||
file: buffer
|
|
||||||
.file()
|
|
||||||
.context("buffer context must have a file")?
|
|
||||||
.clone(),
|
|
||||||
version: buffer.version(),
|
|
||||||
};
|
|
||||||
// Important to collect version at the same time as content so that staleness logic is correct.
|
// Important to collect version at the same time as content so that staleness logic is correct.
|
||||||
|
let version = buffer_ref.version();
|
||||||
let content = if let Some(range) = range {
|
let content = if let Some(range) = range {
|
||||||
buffer.text_for_range(range).collect::<Rope>()
|
buffer_ref.text_for_range(range).collect::<Rope>()
|
||||||
} else {
|
} else {
|
||||||
buffer.as_rope().clone()
|
buffer_ref.as_rope().clone()
|
||||||
};
|
};
|
||||||
let text_task = cx.background_spawn(async move { to_fenced_codeblock(&path, content) });
|
|
||||||
|
let buffer_info = BufferInfo {
|
||||||
|
buffer,
|
||||||
|
id: buffer_ref.remote_id(),
|
||||||
|
file: file.clone(),
|
||||||
|
version,
|
||||||
|
};
|
||||||
|
|
||||||
|
let full_path = file.full_path(cx);
|
||||||
|
let text_task = cx.background_spawn(async move { to_fenced_codeblock(&full_path, content) });
|
||||||
|
|
||||||
Ok((buffer_info, text_task))
|
Ok((buffer_info, text_task))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,16 +884,9 @@ fn refresh_context_buffer(
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<impl Future<Output = ContextBuffer> + use<>> {
|
) -> Option<impl Future<Output = ContextBuffer> + use<>> {
|
||||||
let buffer = context_buffer.buffer.read(cx);
|
let buffer = context_buffer.buffer.read(cx);
|
||||||
let path = buffer_path_log_err(buffer, cx)?;
|
|
||||||
if buffer.version.changed_since(&context_buffer.version) {
|
if buffer.version.changed_since(&context_buffer.version) {
|
||||||
let (buffer_info, text_task) = collect_buffer_info_and_text(
|
let (buffer_info, text_task) =
|
||||||
path,
|
collect_buffer_info_and_text(context_buffer.buffer.clone(), None, cx).log_err()?;
|
||||||
context_buffer.buffer.clone(),
|
|
||||||
buffer,
|
|
||||||
None,
|
|
||||||
cx.to_async(),
|
|
||||||
)
|
|
||||||
.log_err()?;
|
|
||||||
Some(text_task.map(move |text| make_context_buffer(buffer_info, text)))
|
Some(text_task.map(move |text| make_context_buffer(buffer_info, text)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -941,15 +898,12 @@ fn refresh_context_symbol(
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<impl Future<Output = ContextSymbol> + use<>> {
|
) -> Option<impl Future<Output = ContextSymbol> + use<>> {
|
||||||
let buffer = context_symbol.buffer.read(cx);
|
let buffer = context_symbol.buffer.read(cx);
|
||||||
let path = buffer_path_log_err(buffer, cx)?;
|
|
||||||
let project_path = buffer.project_path(cx)?;
|
let project_path = buffer.project_path(cx)?;
|
||||||
if buffer.version.changed_since(&context_symbol.buffer_version) {
|
if buffer.version.changed_since(&context_symbol.buffer_version) {
|
||||||
let (buffer_info, text_task) = collect_buffer_info_and_text(
|
let (buffer_info, text_task) = collect_buffer_info_and_text(
|
||||||
path,
|
|
||||||
context_symbol.buffer.clone(),
|
context_symbol.buffer.clone(),
|
||||||
buffer,
|
|
||||||
Some(context_symbol.enclosing_range.clone()),
|
Some(context_symbol.enclosing_range.clone()),
|
||||||
cx.to_async(),
|
cx,
|
||||||
)
|
)
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
let name = context_symbol.id.name.clone();
|
let name = context_symbol.id.name.clone();
|
||||||
|
|
|
@ -63,9 +63,9 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
Buffer, BufferEvent, Capability, CodeLabel, File as _, Language, LanguageName,
|
Buffer, BufferEvent, Capability, CodeLabel, Language, LanguageName, LanguageRegistry,
|
||||||
LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainList, Transaction,
|
PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainList, Transaction, Unclipped,
|
||||||
Unclipped, language_settings::InlayHintKind, proto::split_operations,
|
language_settings::InlayHintKind, proto::split_operations,
|
||||||
};
|
};
|
||||||
use lsp::{
|
use lsp::{
|
||||||
CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, LanguageServerId,
|
CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, LanguageServerId,
|
||||||
|
@ -4990,7 +4990,7 @@ impl ProjectItem for Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_path(&self, cx: &App) -> Option<ProjectPath> {
|
fn project_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||||
File::from_dyn(self.file()).map(|file| ProjectPath {
|
self.file().map(|file| ProjectPath {
|
||||||
worktree_id: file.worktree_id(cx),
|
worktree_id: file.worktree_id(cx),
|
||||||
path: file.path().clone(),
|
path: file.path().clone(),
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue