Detailed changes
@@ -138,7 +138,7 @@
"find": "buffer_search::Deploy",
"ctrl-f": "buffer_search::Deploy",
"ctrl-h": "buffer_search::DeployReplace",
- "ctrl->": "assistant::QuoteSelection",
+ "ctrl->": "agent::QuoteSelection",
"ctrl-<": "assistant::InsertIntoEditor",
"ctrl-alt-e": "editor::SelectEnclosingSymbol",
"ctrl-shift-backspace": "editor::GoToPreviousChange",
@@ -241,7 +241,7 @@
"ctrl-shift-i": "agent::ToggleOptionsMenu",
"ctrl-alt-shift-n": "agent::ToggleNewThreadMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
- "ctrl->": "assistant::QuoteSelection",
+ "ctrl->": "agent::QuoteSelection",
"ctrl-alt-e": "agent::RemoveAllContext",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-enter": "agent::ContinueThread",
@@ -162,7 +162,7 @@
"cmd-alt-f": "buffer_search::DeployReplace",
"cmd-alt-l": ["buffer_search::Deploy", { "selection_search_enabled": true }],
"cmd-e": ["buffer_search::Deploy", { "focus": false }],
- "cmd->": "assistant::QuoteSelection",
+ "cmd->": "agent::QuoteSelection",
"cmd-<": "assistant::InsertIntoEditor",
"cmd-alt-e": "editor::SelectEnclosingSymbol",
"alt-enter": "editor::OpenSelectionsInMultibuffer"
@@ -281,7 +281,7 @@
"cmd-shift-i": "agent::ToggleOptionsMenu",
"cmd-alt-shift-n": "agent::ToggleNewThreadMenu",
"shift-alt-escape": "agent::ExpandMessageEditor",
- "cmd->": "assistant::QuoteSelection",
+ "cmd->": "agent::QuoteSelection",
"cmd-alt-e": "agent::RemoveAllContext",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-ctrl-b": "agent::ToggleBurnMode",
@@ -17,8 +17,8 @@
"bindings": {
"ctrl-i": "agent::ToggleFocus",
"ctrl-shift-i": "agent::ToggleFocus",
- "ctrl-shift-l": "assistant::QuoteSelection", // In cursor uses "Ask" mode
- "ctrl-l": "assistant::QuoteSelection", // In cursor uses "Agent" mode
+ "ctrl-shift-l": "agent::QuoteSelection", // In cursor uses "Ask" mode
+ "ctrl-l": "agent::QuoteSelection", // In cursor uses "Agent" mode
"ctrl-k": "assistant::InlineAssist",
"ctrl-shift-k": "assistant::InsertIntoEditor"
}
@@ -17,8 +17,8 @@
"bindings": {
"cmd-i": "agent::ToggleFocus",
"cmd-shift-i": "agent::ToggleFocus",
- "cmd-shift-l": "assistant::QuoteSelection", // In cursor uses "Ask" mode
- "cmd-l": "assistant::QuoteSelection", // In cursor uses "Agent" mode
+ "cmd-shift-l": "agent::QuoteSelection", // In cursor uses "Ask" mode
+ "cmd-l": "agent::QuoteSelection", // In cursor uses "Agent" mode
"cmd-k": "assistant::InlineAssist",
"cmd-shift-k": "assistant::InsertIntoEditor"
}
@@ -108,62 +108,7 @@ impl ContextPickerCompletionProvider {
confirm: Some(Arc::new(|_, _, _| true)),
}),
ContextPickerEntry::Action(action) => {
- let (new_text, on_action) = match action {
- ContextPickerAction::AddSelections => {
- const PLACEHOLDER: &str = "selection ";
- let selections = selection_ranges(workspace, cx)
- .into_iter()
- .enumerate()
- .map(|(ix, (buffer, range))| {
- (
- buffer,
- range,
- (PLACEHOLDER.len() * ix)..(PLACEHOLDER.len() * (ix + 1) - 1),
- )
- })
- .collect::<Vec<_>>();
-
- let new_text: String = PLACEHOLDER.repeat(selections.len());
-
- let callback = Arc::new({
- let source_range = source_range.clone();
- move |_, window: &mut Window, cx: &mut App| {
- let selections = selections.clone();
- let message_editor = message_editor.clone();
- let source_range = source_range.clone();
- window.defer(cx, move |window, cx| {
- message_editor
- .update(cx, |message_editor, cx| {
- message_editor.confirm_mention_for_selection(
- source_range,
- selections,
- window,
- cx,
- )
- })
- .ok();
- });
- false
- }
- });
-
- (new_text, callback)
- }
- };
-
- Some(Completion {
- replace_range: source_range,
- new_text,
- label: CodeLabel::plain(action.label().to_string(), None),
- icon_path: Some(action.icon().path().into()),
- documentation: None,
- source: project::CompletionSource::Custom,
- insert_text_mode: None,
- // This ensures that when a user accepts this completion, the
- // completion menu will still be shown after "@category " is
- // inserted
- confirm: Some(on_action),
- })
+ Self::completion_for_action(action, source_range, message_editor, workspace, cx)
}
}
}
@@ -359,6 +304,71 @@ impl ContextPickerCompletionProvider {
})
}
+ pub(crate) fn completion_for_action(
+ action: ContextPickerAction,
+ source_range: Range<Anchor>,
+ message_editor: WeakEntity<MessageEditor>,
+ workspace: &Entity<Workspace>,
+ cx: &mut App,
+ ) -> Option<Completion> {
+ let (new_text, on_action) = match action {
+ ContextPickerAction::AddSelections => {
+ const PLACEHOLDER: &str = "selection ";
+ let selections = selection_ranges(workspace, cx)
+ .into_iter()
+ .enumerate()
+ .map(|(ix, (buffer, range))| {
+ (
+ buffer,
+ range,
+ (PLACEHOLDER.len() * ix)..(PLACEHOLDER.len() * (ix + 1) - 1),
+ )
+ })
+ .collect::<Vec<_>>();
+
+ let new_text: String = PLACEHOLDER.repeat(selections.len());
+
+ let callback = Arc::new({
+ let source_range = source_range.clone();
+ move |_, window: &mut Window, cx: &mut App| {
+ let selections = selections.clone();
+ let message_editor = message_editor.clone();
+ let source_range = source_range.clone();
+ window.defer(cx, move |window, cx| {
+ message_editor
+ .update(cx, |message_editor, cx| {
+ message_editor.confirm_mention_for_selection(
+ source_range,
+ selections,
+ window,
+ cx,
+ )
+ })
+ .ok();
+ });
+ false
+ }
+ });
+
+ (new_text, callback)
+ }
+ };
+
+ Some(Completion {
+ replace_range: source_range,
+ new_text,
+ label: CodeLabel::plain(action.label().to_string(), None),
+ icon_path: Some(action.icon().path().into()),
+ documentation: None,
+ source: project::CompletionSource::Custom,
+ insert_text_mode: None,
+ // This ensures that when a user accepts this completion, the
+ // completion menu will still be shown after "@category " is
+ // inserted
+ confirm: Some(on_action),
+ })
+ }
+
fn search(
&self,
mode: Option<ContextPickerMode>,
@@ -1,6 +1,6 @@
use crate::{
acp::completion_provider::ContextPickerCompletionProvider,
- context_picker::fetch_context_picker::fetch_url_content,
+ context_picker::{ContextPickerAction, fetch_context_picker::fetch_url_content},
};
use acp_thread::{MentionUri, selection_name};
use agent_client_protocol as acp;
@@ -27,7 +27,7 @@ use gpui::{
};
use language::{Buffer, Language};
use language_model::LanguageModelImage;
-use project::{Project, ProjectPath, Worktree};
+use project::{CompletionIntent, Project, ProjectItem, ProjectPath, Worktree};
use prompt_store::PromptStore;
use rope::Point;
use settings::Settings;
@@ -561,21 +561,24 @@ impl MessageEditor {
let range = snapshot.anchor_after(offset + range_to_fold.start)
..snapshot.anchor_after(offset + range_to_fold.end);
- let path = buffer
- .read(cx)
- .file()
- .map_or(PathBuf::from("untitled"), |file| file.path().to_path_buf());
+ // TODO support selections from buffers with no path
+ let Some(project_path) = buffer.read(cx).project_path(cx) else {
+ continue;
+ };
+ let Some(abs_path) = self.project.read(cx).absolute_path(&project_path, cx) else {
+ continue;
+ };
let snapshot = buffer.read(cx).snapshot();
let point_range = selection_range.to_point(&snapshot);
let line_range = point_range.start.row..point_range.end.row;
let uri = MentionUri::Selection {
- path: path.clone(),
+ path: abs_path.clone(),
line_range: line_range.clone(),
};
let crease = crate::context_picker::crease_for_mention(
- selection_name(&path, &line_range).into(),
+ selection_name(&abs_path, &line_range).into(),
uri.icon_path(cx),
range,
self.editor.downgrade(),
@@ -587,8 +590,7 @@ impl MessageEditor {
crease_ids.first().copied().unwrap()
});
- self.mention_set
- .insert_uri(crease_id, MentionUri::Selection { path, line_range });
+ self.mention_set.insert_uri(crease_id, uri);
}
}
@@ -948,6 +950,38 @@ impl MessageEditor {
.detach();
}
+ pub fn insert_selections(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ let buffer = self.editor.read(cx).buffer().clone();
+ let Some(buffer) = buffer.read(cx).as_singleton() else {
+ return;
+ };
+ let anchor = buffer.update(cx, |buffer, _cx| buffer.anchor_before(buffer.len()));
+ let Some(workspace) = self.workspace.upgrade() else {
+ return;
+ };
+ let Some(completion) = ContextPickerCompletionProvider::completion_for_action(
+ ContextPickerAction::AddSelections,
+ anchor..anchor,
+ cx.weak_entity(),
+ &workspace,
+ cx,
+ ) else {
+ return;
+ };
+ self.editor.update(cx, |message_editor, cx| {
+ message_editor.edit(
+ [(
+ multi_buffer::Anchor::max()..multi_buffer::Anchor::max(),
+ completion.new_text,
+ )],
+ cx,
+ );
+ });
+ if let Some(confirm) = completion.confirm {
+ confirm(CompletionIntent::Complete, window, cx);
+ }
+ }
+
pub fn set_read_only(&mut self, read_only: bool, cx: &mut Context<Self>) {
self.editor.update(cx, |message_editor, cx| {
message_editor.set_read_only(read_only);
@@ -4097,6 +4097,12 @@ impl AcpThreadView {
})
}
+ pub(crate) fn insert_selections(&self, window: &mut Window, cx: &mut Context<Self>) {
+ self.message_editor.update(cx, |message_editor, cx| {
+ message_editor.insert_selections(window, cx);
+ })
+ }
+
fn render_thread_retry_status_callout(
&self,
_window: &mut Window,
@@ -903,6 +903,16 @@ impl AgentPanel {
}
}
+ fn active_thread_view(&self) -> Option<&Entity<AcpThreadView>> {
+ match &self.active_view {
+ ActiveView::ExternalAgentThread { thread_view } => Some(thread_view),
+ ActiveView::Thread { .. }
+ | ActiveView::TextThread { .. }
+ | ActiveView::History
+ | ActiveView::Configuration => None,
+ }
+ }
+
fn new_thread(&mut self, action: &NewThread, window: &mut Window, cx: &mut Context<Self>) {
if cx.has_flag::<GeminiAndNativeFeatureFlag>() {
return self.new_agent_thread(AgentType::NativeAgent, window, cx);
@@ -3882,7 +3892,11 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate {
// Wait to create a new context until the workspace is no longer
// being updated.
cx.defer_in(window, move |panel, window, cx| {
- if let Some(message_editor) = panel.active_message_editor() {
+ if let Some(thread_view) = panel.active_thread_view() {
+ thread_view.update(cx, |thread_view, cx| {
+ thread_view.insert_selections(window, cx);
+ });
+ } else if let Some(message_editor) = panel.active_message_editor() {
message_editor.update(cx, |message_editor, cx| {
message_editor.context_store().update(cx, |store, cx| {
let buffer = buffer.read(cx);
@@ -128,6 +128,12 @@ actions!(
]
);
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Action)]
+#[action(namespace = agent)]
+#[action(deprecated_aliases = ["assistant::QuoteSelection"])]
+/// Quotes the current selection in the agent panel's message editor.
+pub struct QuoteSelection;
+
/// Creates a new conversation thread, optionally based on an existing thread.
#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
#[action(namespace = agent)]
@@ -1,4 +1,5 @@
use crate::{
+ QuoteSelection,
language_model_selector::{LanguageModelSelector, language_model_selector},
ui::BurnModeTooltip,
};
@@ -89,8 +90,6 @@ actions!(
CycleMessageRole,
/// Inserts the selected text into the active editor.
InsertIntoEditor,
- /// Quotes the current selection in the assistant conversation.
- QuoteSelection,
/// Splits the conversation at the current cursor position.
Split,
]
@@ -16,7 +16,7 @@ To begin, type a message in a `You` block.
As you type, the remaining tokens count for the selected model is updated.
-Inserting text from an editor is as simple as highlighting the text and running `assistant: quote selection` ({#kb assistant::QuoteSelection}); Zed will wrap it in a fenced code block if it is code.
+Inserting text from an editor is as simple as highlighting the text and running `agent: quote selection` ({#kb agent::QuoteSelection}); Zed will wrap it in a fenced code block if it is code.

@@ -148,7 +148,7 @@ Usage: `/terminal [<number>]`
The `/selection` command inserts the selected text in the editor into the context. This is useful for referencing specific parts of your code.
-This is equivalent to the `assistant: quote selection` command ({#kb assistant::QuoteSelection}).
+This is equivalent to the `agent: quote selection` command ({#kb agent::QuoteSelection}).
Usage: `/selection`