@@ -364,7 +364,7 @@ impl MessageEditor {
let task = match mention_uri.clone() {
MentionUri::Fetch { url } => self.confirm_mention_for_fetch(url, cx),
- MentionUri::Directory { abs_path } => self.confirm_mention_for_directory(abs_path, cx),
+ MentionUri::Directory { .. } => Task::ready(Ok(Mention::UriOnly)),
MentionUri::Thread { id, .. } => self.confirm_mention_for_thread(id, cx),
MentionUri::TextThread { path, .. } => self.confirm_mention_for_text_thread(path, cx),
MentionUri::File { abs_path } => self.confirm_mention_for_file(abs_path, cx),
@@ -468,97 +468,6 @@ impl MessageEditor {
})
}
- fn confirm_mention_for_directory(
- &mut self,
- abs_path: PathBuf,
- cx: &mut Context<Self>,
- ) -> Task<Result<Mention>> {
- fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec<(Arc<Path>, PathBuf)> {
- let mut files = Vec::new();
-
- for entry in worktree.child_entries(path) {
- if entry.is_dir() {
- files.extend(collect_files_in_path(worktree, &entry.path));
- } else if entry.is_file() {
- files.push((entry.path.clone(), worktree.full_path(&entry.path)));
- }
- }
-
- files
- }
-
- let Some(project_path) = self
- .project
- .read(cx)
- .project_path_for_absolute_path(&abs_path, cx)
- else {
- return Task::ready(Err(anyhow!("project path not found")));
- };
- let Some(entry) = self.project.read(cx).entry_for_path(&project_path, cx) else {
- return Task::ready(Err(anyhow!("project entry not found")));
- };
- let directory_path = entry.path.clone();
- let worktree_id = project_path.worktree_id;
- let Some(worktree) = self.project.read(cx).worktree_for_id(worktree_id, cx) else {
- return Task::ready(Err(anyhow!("worktree not found")));
- };
- let project = self.project.clone();
- cx.spawn(async move |_, cx| {
- let file_paths = worktree.read_with(cx, |worktree, _cx| {
- collect_files_in_path(worktree, &directory_path)
- })?;
- let descendants_future = cx.update(|cx| {
- join_all(file_paths.into_iter().map(|(worktree_path, full_path)| {
- let rel_path = worktree_path
- .strip_prefix(&directory_path)
- .log_err()
- .map_or_else(|| worktree_path.clone(), |rel_path| rel_path.into());
-
- let open_task = project.update(cx, |project, cx| {
- project.buffer_store().update(cx, |buffer_store, cx| {
- let project_path = ProjectPath {
- worktree_id,
- path: worktree_path,
- };
- buffer_store.open_buffer(project_path, cx)
- })
- });
-
- cx.spawn(async move |cx| {
- let buffer = open_task.await.log_err()?;
- let buffer_content = outline::get_buffer_content_or_outline(
- buffer.clone(),
- Some(&full_path),
- &cx,
- )
- .await
- .ok()?;
-
- Some((rel_path, full_path, buffer_content.text, buffer))
- })
- }))
- })?;
-
- let contents = cx
- .background_spawn(async move {
- let (contents, tracked_buffers) = descendants_future
- .await
- .into_iter()
- .flatten()
- .map(|(rel_path, full_path, rope, buffer)| {
- ((rel_path, full_path, rope), buffer)
- })
- .unzip();
- Mention::Text {
- content: render_directory_contents(contents),
- tracked_buffers,
- }
- })
- .await;
- anyhow::Ok(contents)
- })
- }
-
fn confirm_mention_for_fetch(
&mut self,
url: url::Url,
@@ -776,6 +685,7 @@ impl MessageEditor {
pub fn contents(
&self,
+ full_mention_content: bool,
cx: &mut Context<Self>,
) -> Task<Result<(Vec<acp::ContentBlock>, Vec<Entity<Buffer>>)>> {
// Check for unsupported slash commands before spawning async task
@@ -787,9 +697,12 @@ impl MessageEditor {
return Task::ready(Err(err));
}
- let contents = self
- .mention_set
- .contents(&self.prompt_capabilities.borrow(), cx);
+ let contents = self.mention_set.contents(
+ &self.prompt_capabilities.borrow(),
+ full_mention_content,
+ self.project.clone(),
+ cx,
+ );
let editor = self.editor.clone();
cx.spawn(async move |_, cx| {
@@ -1263,6 +1176,96 @@ impl MessageEditor {
}
}
+fn full_mention_for_directory(
+ project: &Entity<Project>,
+ abs_path: &Path,
+ cx: &mut App,
+) -> Task<Result<Mention>> {
+ fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec<(Arc<Path>, PathBuf)> {
+ let mut files = Vec::new();
+
+ for entry in worktree.child_entries(path) {
+ if entry.is_dir() {
+ files.extend(collect_files_in_path(worktree, &entry.path));
+ } else if entry.is_file() {
+ files.push((entry.path.clone(), worktree.full_path(&entry.path)));
+ }
+ }
+
+ files
+ }
+
+ let Some(project_path) = project
+ .read(cx)
+ .project_path_for_absolute_path(&abs_path, cx)
+ else {
+ return Task::ready(Err(anyhow!("project path not found")));
+ };
+ let Some(entry) = project.read(cx).entry_for_path(&project_path, cx) else {
+ return Task::ready(Err(anyhow!("project entry not found")));
+ };
+ let directory_path = entry.path.clone();
+ let worktree_id = project_path.worktree_id;
+ let Some(worktree) = project.read(cx).worktree_for_id(worktree_id, cx) else {
+ return Task::ready(Err(anyhow!("worktree not found")));
+ };
+ let project = project.clone();
+ cx.spawn(async move |cx| {
+ let file_paths = worktree.read_with(cx, |worktree, _cx| {
+ collect_files_in_path(worktree, &directory_path)
+ })?;
+ let descendants_future = cx.update(|cx| {
+ join_all(file_paths.into_iter().map(|(worktree_path, full_path)| {
+ let rel_path = worktree_path
+ .strip_prefix(&directory_path)
+ .log_err()
+ .map_or_else(|| worktree_path.clone(), |rel_path| rel_path.into());
+
+ let open_task = project.update(cx, |project, cx| {
+ project.buffer_store().update(cx, |buffer_store, cx| {
+ let project_path = ProjectPath {
+ worktree_id,
+ path: worktree_path,
+ };
+ buffer_store.open_buffer(project_path, cx)
+ })
+ });
+
+ cx.spawn(async move |cx| {
+ let buffer = open_task.await.log_err()?;
+ let buffer_content = outline::get_buffer_content_or_outline(
+ buffer.clone(),
+ Some(&full_path),
+ &cx,
+ )
+ .await
+ .ok()?;
+
+ Some((rel_path, full_path, buffer_content.text, buffer))
+ })
+ }))
+ })?;
+
+ let contents = cx
+ .background_spawn(async move {
+ let (contents, tracked_buffers) = descendants_future
+ .await
+ .into_iter()
+ .flatten()
+ .map(|(rel_path, full_path, rope, buffer)| {
+ ((rel_path, full_path, rope), buffer)
+ })
+ .unzip();
+ Mention::Text {
+ content: render_directory_contents(contents),
+ tracked_buffers,
+ }
+ })
+ .await;
+ anyhow::Ok(contents)
+ })
+}
+
fn render_directory_contents(entries: Vec<(Arc<Path>, PathBuf, String)>) -> String {
let mut output = String::new();
for (_relative_path, full_path, content) in entries {
@@ -1514,6 +1517,8 @@ impl MentionSet {
fn contents(
&self,
prompt_capabilities: &acp::PromptCapabilities,
+ full_mention_content: bool,
+ project: Entity<Project>,
cx: &mut App,
) -> Task<Result<HashMap<CreaseId, (MentionUri, Mention)>>> {
if !prompt_capabilities.embedded_context {
@@ -1527,13 +1532,19 @@ impl MentionSet {
}
let mentions = self.mentions.clone();
- cx.spawn(async move |_cx| {
+ cx.spawn(async move |cx| {
let mut contents = HashMap::default();
for (crease_id, (mention_uri, task)) in mentions {
- contents.insert(
- crease_id,
- (mention_uri, task.await.map_err(|e| anyhow!("{e}"))?),
- );
+ let content = if full_mention_content
+ && let MentionUri::Directory { abs_path } = &mention_uri
+ {
+ cx.update(|cx| full_mention_for_directory(&project, abs_path, cx))?
+ .await?
+ } else {
+ task.await.map_err(|e| anyhow!("{e}"))?
+ };
+
+ contents.insert(crease_id, (mention_uri, content));
}
Ok(contents)
})
@@ -1694,7 +1705,7 @@ mod tests {
});
let (content, _) = message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx))
+ .update(cx, |message_editor, cx| message_editor.contents(false, cx))
.await
.unwrap();
@@ -1757,7 +1768,7 @@ mod tests {
});
let contents_result = message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx))
+ .update(cx, |message_editor, cx| message_editor.contents(false, cx))
.await;
// Should fail because available_commands is empty (no commands supported)
@@ -1780,7 +1791,7 @@ mod tests {
});
let contents_result = message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx))
+ .update(cx, |message_editor, cx| message_editor.contents(false, cx))
.await;
assert!(contents_result.is_err());
@@ -1795,7 +1806,7 @@ mod tests {
});
let contents_result = message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx))
+ .update(cx, |message_editor, cx| message_editor.contents(false, cx))
.await;
// Should succeed because /help is in available_commands
@@ -1807,7 +1818,7 @@ mod tests {
});
let (content, _) = message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx))
+ .update(cx, |message_editor, cx| message_editor.contents(false, cx))
.await
.unwrap();
@@ -1825,7 +1836,7 @@ mod tests {
// The @ mention functionality should not be affected
let (content, _) = message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx))
+ .update(cx, |message_editor, cx| message_editor.contents(false, cx))
.await
.unwrap();
@@ -2271,9 +2282,12 @@ mod tests {
let contents = message_editor
.update(&mut cx, |message_editor, cx| {
- message_editor
- .mention_set()
- .contents(&all_prompt_capabilities, cx)
+ message_editor.mention_set().contents(
+ &all_prompt_capabilities,
+ false,
+ project.clone(),
+ cx,
+ )
})
.await
.unwrap()
@@ -2290,9 +2304,12 @@ mod tests {
let contents = message_editor
.update(&mut cx, |message_editor, cx| {
- message_editor
- .mention_set()
- .contents(&acp::PromptCapabilities::default(), cx)
+ message_editor.mention_set().contents(
+ &acp::PromptCapabilities::default(),
+ false,
+ project.clone(),
+ cx,
+ )
})
.await
.unwrap()
@@ -2341,9 +2358,12 @@ mod tests {
let contents = message_editor
.update(&mut cx, |message_editor, cx| {
- message_editor
- .mention_set()
- .contents(&all_prompt_capabilities, cx)
+ message_editor.mention_set().contents(
+ &all_prompt_capabilities,
+ false,
+ project.clone(),
+ cx,
+ )
})
.await
.unwrap()
@@ -2451,9 +2471,12 @@ mod tests {
let contents = message_editor
.update(&mut cx, |message_editor, cx| {
- message_editor
- .mention_set()
- .contents(&all_prompt_capabilities, cx)
+ message_editor.mention_set().contents(
+ &all_prompt_capabilities,
+ false,
+ project.clone(),
+ cx,
+ )
})
.await
.unwrap()
@@ -2501,9 +2524,12 @@ mod tests {
// Getting the message contents fails
message_editor
.update(&mut cx, |message_editor, cx| {
- message_editor
- .mention_set()
- .contents(&all_prompt_capabilities, cx)
+ message_editor.mention_set().contents(
+ &all_prompt_capabilities,
+ false,
+ project.clone(),
+ cx,
+ )
})
.await
.expect_err("Should fail to load x.png");
@@ -2548,9 +2574,12 @@ mod tests {
// Now getting the contents succeeds, because the invalid mention was removed
let contents = message_editor
.update(&mut cx, |message_editor, cx| {
- message_editor
- .mention_set()
- .contents(&all_prompt_capabilities, cx)
+ message_editor.mention_set().contents(
+ &all_prompt_capabilities,
+ false,
+ project.clone(),
+ cx,
+ )
})
.await
.unwrap();
@@ -1038,10 +1038,7 @@ impl AcpThreadView {
return;
}
- let contents = self
- .message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx));
- self.send_impl(contents, window, cx)
+ self.send_impl(self.message_editor.clone(), window, cx)
}
fn stop_current_and_send_new_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -1051,15 +1048,11 @@ impl AcpThreadView {
let cancelled = thread.update(cx, |thread, cx| thread.cancel(cx));
- let contents = self
- .message_editor
- .update(cx, |message_editor, cx| message_editor.contents(cx));
-
cx.spawn_in(window, async move |this, cx| {
cancelled.await;
this.update_in(cx, |this, window, cx| {
- this.send_impl(contents, window, cx);
+ this.send_impl(this.message_editor.clone(), window, cx);
})
.ok();
})
@@ -1068,10 +1061,23 @@ impl AcpThreadView {
fn send_impl(
&mut self,
- contents: Task<Result<(Vec<acp::ContentBlock>, Vec<Entity<Buffer>>)>>,
+ message_editor: Entity<MessageEditor>,
window: &mut Window,
cx: &mut Context<Self>,
) {
+ let full_mention_content = self.as_native_thread(cx).is_some_and(|thread| {
+ // Include full contents when using minimal profile
+ let thread = thread.read(cx);
+ AgentSettings::get_global(cx)
+ .profiles
+ .get(thread.profile())
+ .is_some_and(|profile| profile.tools.is_empty())
+ });
+
+ let contents = message_editor.update(cx, |message_editor, cx| {
+ message_editor.contents(full_mention_content, cx)
+ });
+
let agent_telemetry_id = self.agent.telemetry_id();
self.thread_error.take();
@@ -1200,10 +1206,8 @@ impl AcpThreadView {
thread
.update(cx, |thread, cx| thread.rewind(user_message_id, cx))?
.await?;
- let contents =
- message_editor.update(cx, |message_editor, cx| message_editor.contents(cx))?;
this.update_in(cx, |this, window, cx| {
- this.send_impl(contents, window, cx);
+ this.send_impl(message_editor, window, cx);
})?;
anyhow::Ok(())
})