@@ -234,6 +234,8 @@ impl MentionSet {
mention_uri.name().into(),
IconName::Image.path().into(),
mention_uri.tooltip_text(),
+ Some(mention_uri.clone()),
+ Some(workspace.downgrade()),
Some(image),
editor.clone(),
window,
@@ -247,6 +249,8 @@ impl MentionSet {
crease_text,
mention_uri.icon_path(cx),
mention_uri.tooltip_text(),
+ Some(mention_uri.clone()),
+ Some(workspace.downgrade()),
None,
editor.clone(),
window,
@@ -699,6 +703,8 @@ pub(crate) async fn insert_images_as_context(
MentionUri::PastedImage.name().into(),
IconName::Image.path().into(),
None,
+ None,
+ None,
Some(Task::ready(Ok(image.clone())).shared()),
editor.clone(),
window,
@@ -810,6 +816,8 @@ pub(crate) fn insert_crease_for_mention(
crease_label: SharedString,
crease_icon: SharedString,
crease_tooltip: Option<SharedString>,
+ mention_uri: Option<MentionUri>,
+ workspace: Option<WeakEntity<Workspace>>,
image: Option<Shared<Task<Result<Arc<Image>, String>>>>,
editor: Entity<Editor>,
window: &mut Window,
@@ -830,6 +838,8 @@ pub(crate) fn insert_crease_for_mention(
crease_label.clone(),
crease_icon.clone(),
crease_tooltip,
+ mention_uri.clone(),
+ workspace.clone(),
start..end,
rx,
image,
@@ -1029,6 +1039,8 @@ fn render_mention_fold_button(
label: SharedString,
icon: SharedString,
tooltip: Option<SharedString>,
+ mention_uri: Option<MentionUri>,
+ workspace: Option<WeakEntity<Workspace>>,
range: Range<Anchor>,
mut loading_finished: postage::barrier::Receiver,
image_task: Option<Shared<Task<Result<Arc<Image>, String>>>>,
@@ -1049,6 +1061,8 @@ fn render_mention_fold_button(
label,
icon,
tooltip,
+ mention_uri: mention_uri.clone(),
+ workspace: workspace.clone(),
range,
editor,
loading: Some(loading),
@@ -1063,6 +1077,8 @@ struct LoadingContext {
label: SharedString,
icon: SharedString,
tooltip: Option<SharedString>,
+ mention_uri: Option<MentionUri>,
+ workspace: Option<WeakEntity<Workspace>>,
range: Range<Anchor>,
editor: WeakEntity<Editor>,
loading: Option<Task<()>>,
@@ -1079,6 +1095,8 @@ impl Render for LoadingContext {
let id = ElementId::from(("loading_context", self.id));
MentionCrease::new(id, self.icon.clone(), self.label.clone())
+ .mention_uri(self.mention_uri.clone())
+ .workspace(self.workspace.clone())
.is_toggled(is_in_text_selection)
.is_loading(self.loading.is_some())
.when_some(self.tooltip.clone(), |this, tooltip_text| {
@@ -722,6 +722,8 @@ impl MessageEditor {
crease_text.into(),
mention_uri.icon_path(cx),
mention_uri.tooltip_text(),
+ Some(mention_uri.clone()),
+ Some(self.workspace.clone()),
None,
self.editor.clone(),
window,
@@ -833,6 +835,8 @@ impl MessageEditor {
mention_uri.name().into(),
mention_uri.icon_path(cx),
mention_uri.tooltip_text(),
+ Some(mention_uri.clone()),
+ Some(self.workspace.clone()),
None,
self.editor.clone(),
window,
@@ -1014,6 +1018,8 @@ impl MessageEditor {
mention_uri.name().into(),
mention_uri.icon_path(cx),
mention_uri.tooltip_text(),
+ Some(mention_uri.clone()),
+ Some(self.workspace.clone()),
None,
self.editor.clone(),
window,
@@ -1370,6 +1376,8 @@ impl MessageEditor {
mention_uri.name().into(),
mention_uri.icon_path(cx),
mention_uri.tooltip_text(),
+ Some(mention_uri.clone()),
+ Some(self.workspace.clone()),
None,
self.editor.clone(),
window,
@@ -1,15 +1,25 @@
-use std::time::Duration;
+use std::{ops::RangeInclusive, path::PathBuf, time::Duration};
-use gpui::{Animation, AnimationExt, AnyView, IntoElement, Window, pulsating_between};
+use acp_thread::MentionUri;
+use agent_client_protocol as acp;
+use editor::{Editor, SelectionEffects, scroll::Autoscroll};
+use gpui::{
+ Animation, AnimationExt, AnyView, Context, IntoElement, WeakEntity, Window, pulsating_between,
+};
+use prompt_store::PromptId;
+use rope::Point;
use settings::Settings;
use theme::ThemeSettings;
use ui::{ButtonLike, TintColor, Tooltip, prelude::*};
+use workspace::{OpenOptions, Workspace};
#[derive(IntoElement)]
pub struct MentionCrease {
id: ElementId,
icon: SharedString,
label: SharedString,
+ mention_uri: Option<MentionUri>,
+ workspace: Option<WeakEntity<Workspace>>,
is_toggled: bool,
is_loading: bool,
tooltip: Option<SharedString>,
@@ -26,6 +36,8 @@ impl MentionCrease {
id: id.into(),
icon: icon.into(),
label: label.into(),
+ mention_uri: None,
+ workspace: None,
is_toggled: false,
is_loading: false,
tooltip: None,
@@ -33,6 +45,16 @@ impl MentionCrease {
}
}
+ pub fn mention_uri(mut self, mention_uri: Option<MentionUri>) -> Self {
+ self.mention_uri = mention_uri;
+ self
+ }
+
+ pub fn workspace(mut self, workspace: Option<WeakEntity<Workspace>>) -> Self {
+ self.workspace = workspace;
+ self
+ }
+
pub fn is_toggled(mut self, is_toggled: bool) -> Self {
self.is_toggled = is_toggled;
self
@@ -76,6 +98,14 @@ impl RenderOnce for MentionCrease {
.height(button_height)
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
.toggle_state(self.is_toggled)
+ .when_some(
+ self.mention_uri.clone().zip(self.workspace.clone()),
+ |this, (mention_uri, workspace)| {
+ this.on_click(move |_event, window, cx| {
+ open_mention_uri(mention_uri.clone(), &workspace, window, cx);
+ })
+ },
+ )
.child(
h_flex()
.pb_px()
@@ -114,3 +144,168 @@ impl RenderOnce for MentionCrease {
})
}
}
+
+fn open_mention_uri(
+ mention_uri: MentionUri,
+ workspace: &WeakEntity<Workspace>,
+ window: &mut Window,
+ cx: &mut App,
+) {
+ let Some(workspace) = workspace.upgrade() else {
+ return;
+ };
+
+ workspace.update(cx, |workspace, cx| match mention_uri {
+ MentionUri::File { abs_path } => {
+ open_file(workspace, abs_path, None, window, cx);
+ }
+ MentionUri::Symbol {
+ abs_path,
+ line_range,
+ ..
+ }
+ | MentionUri::Selection {
+ abs_path: Some(abs_path),
+ line_range,
+ } => {
+ open_file(workspace, abs_path, Some(line_range), window, cx);
+ }
+ MentionUri::Directory { abs_path } => {
+ reveal_in_project_panel(workspace, abs_path, cx);
+ }
+ MentionUri::Thread { id, name } => {
+ open_thread(workspace, id, name, window, cx);
+ }
+ MentionUri::TextThread { .. } => {}
+ MentionUri::Rule { id, .. } => {
+ open_rule(workspace, id, window, cx);
+ }
+ MentionUri::Fetch { url } => {
+ cx.open_url(url.as_str());
+ }
+ MentionUri::PastedImage
+ | MentionUri::Selection { abs_path: None, .. }
+ | MentionUri::Diagnostics { .. }
+ | MentionUri::TerminalSelection { .. }
+ | MentionUri::GitDiff { .. } => {}
+ });
+}
+
+fn open_file(
+ workspace: &mut Workspace,
+ abs_path: PathBuf,
+ line_range: Option<RangeInclusive<u32>>,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ let project = workspace.project();
+
+ if let Some(project_path) =
+ project.update(cx, |project, cx| project.find_project_path(&abs_path, cx))
+ {
+ let item = workspace.open_path(project_path, None, true, window, cx);
+ if let Some(line_range) = line_range {
+ window
+ .spawn(cx, async move |cx| {
+ let Some(editor) = item.await?.downcast::<Editor>() else {
+ return Ok(());
+ };
+ editor
+ .update_in(cx, |editor, window, cx| {
+ let range = Point::new(*line_range.start(), 0)
+ ..Point::new(*line_range.start(), 0);
+ editor.change_selections(
+ SelectionEffects::scroll(Autoscroll::center()),
+ window,
+ cx,
+ |selections| selections.select_ranges(vec![range]),
+ );
+ })
+ .ok();
+ anyhow::Ok(())
+ })
+ .detach_and_log_err(cx);
+ } else {
+ item.detach_and_log_err(cx);
+ }
+ } else if abs_path.exists() {
+ workspace
+ .open_abs_path(
+ abs_path,
+ OpenOptions {
+ focus: Some(true),
+ ..Default::default()
+ },
+ window,
+ cx,
+ )
+ .detach_and_log_err(cx);
+ }
+}
+
+fn reveal_in_project_panel(
+ workspace: &mut Workspace,
+ abs_path: PathBuf,
+ cx: &mut Context<Workspace>,
+) {
+ let project = workspace.project();
+ let Some(entry_id) = project.update(cx, |project, cx| {
+ let path = project.find_project_path(&abs_path, cx)?;
+ project.entry_for_path(&path, cx).map(|entry| entry.id)
+ }) else {
+ return;
+ };
+
+ project.update(cx, |_, cx| {
+ cx.emit(project::Event::RevealInProjectPanel(entry_id));
+ });
+}
+
+fn open_thread(
+ workspace: &mut Workspace,
+ id: acp::SessionId,
+ name: String,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ use crate::AgentPanel;
+ use acp_thread::AgentSessionInfo;
+
+ let Some(panel) = workspace.panel::<AgentPanel>(cx) else {
+ return;
+ };
+
+ panel.update(cx, |panel, cx| {
+ panel.load_agent_thread(
+ AgentSessionInfo {
+ session_id: id,
+ cwd: None,
+ title: Some(name.into()),
+ updated_at: None,
+ meta: None,
+ },
+ window,
+ cx,
+ )
+ });
+}
+
+fn open_rule(
+ _workspace: &mut Workspace,
+ id: PromptId,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ use zed_actions::assistant::OpenRulesLibrary;
+
+ let PromptId::User { uuid } = id else {
+ return;
+ };
+
+ window.dispatch_action(
+ Box::new(OpenRulesLibrary {
+ prompt_to_select: Some(uuid.0),
+ }),
+ cx,
+ );
+}