tool_selector.rs

  1use std::sync::Arc;
  2
  3use assistant_tool::{ToolSource, ToolWorkingSet};
  4use gpui::Entity;
  5use scripting_tool::ScriptingTool;
  6use ui::{prelude::*, ContextMenu, PopoverMenu, Tooltip};
  7
  8pub struct ToolSelector {
  9    tools: Arc<ToolWorkingSet>,
 10}
 11
 12impl ToolSelector {
 13    pub fn new(tools: Arc<ToolWorkingSet>, _cx: &mut Context<Self>) -> Self {
 14        Self { tools }
 15    }
 16
 17    fn build_context_menu(
 18        &self,
 19        window: &mut Window,
 20        cx: &mut Context<Self>,
 21    ) -> Entity<ContextMenu> {
 22        let tool_set = self.tools.clone();
 23        ContextMenu::build_persistent(window, cx, move |mut menu, _window, cx| {
 24            let icon_position = IconPosition::End;
 25            let tools_by_source = tool_set.tools_by_source(cx);
 26
 27            let all_tools_enabled = tool_set.are_all_tools_enabled();
 28            menu = menu.toggleable_entry("All Tools", all_tools_enabled, icon_position, None, {
 29                let tools = tool_set.clone();
 30                move |_window, cx| {
 31                    if all_tools_enabled {
 32                        tools.disable_all_tools(cx);
 33                    } else {
 34                        tools.enable_all_tools();
 35                    }
 36                }
 37            });
 38
 39            for (source, tools) in tools_by_source {
 40                let mut tools = tools
 41                    .into_iter()
 42                    .map(|tool| {
 43                        let source = tool.source();
 44                        let name = tool.name().into();
 45                        let is_enabled = tool_set.is_enabled(&source, &name);
 46
 47                        (source, name, is_enabled)
 48                    })
 49                    .collect::<Vec<_>>();
 50
 51                if ToolSource::Native == source {
 52                    tools.push((
 53                        ToolSource::Native,
 54                        ScriptingTool::NAME.into(),
 55                        tool_set.is_scripting_tool_enabled(),
 56                    ));
 57                    tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b));
 58                }
 59
 60                menu = match &source {
 61                    ToolSource::Native => menu.separator().header("Zed Tools"),
 62                    ToolSource::ContextServer { id } => {
 63                        let all_tools_from_source_enabled =
 64                            tool_set.are_all_tools_from_source_enabled(&source);
 65
 66                        menu.separator().header(id).toggleable_entry(
 67                            "All Tools",
 68                            all_tools_from_source_enabled,
 69                            icon_position,
 70                            None,
 71                            {
 72                                let tools = tool_set.clone();
 73                                let source = source.clone();
 74                                move |_window, cx| {
 75                                    if all_tools_from_source_enabled {
 76                                        tools.disable_source(source.clone(), cx);
 77                                    } else {
 78                                        tools.enable_source(&source);
 79                                    }
 80                                }
 81                            },
 82                        )
 83                    }
 84                };
 85
 86                for (source, name, is_enabled) in tools {
 87                    menu = menu.toggleable_entry(name.clone(), is_enabled, icon_position, None, {
 88                        let tools = tool_set.clone();
 89                        move |_window, _cx| {
 90                            if name.as_ref() == ScriptingTool::NAME {
 91                                if is_enabled {
 92                                    tools.disable_scripting_tool();
 93                                } else {
 94                                    tools.enable_scripting_tool();
 95                                }
 96                            } else {
 97                                if is_enabled {
 98                                    tools.disable(source.clone(), &[name.clone()]);
 99                                } else {
100                                    tools.enable(source.clone(), &[name.clone()]);
101                                }
102                            }
103                        }
104                    });
105                }
106            }
107
108            menu
109        })
110    }
111}
112
113impl Render for ToolSelector {
114    fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
115        let this = cx.entity().clone();
116        PopoverMenu::new("tool-selector")
117            .menu(move |window, cx| {
118                Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
119            })
120            .trigger_with_tooltip(
121                IconButton::new("tool-selector-button", IconName::SettingsAlt)
122                    .icon_size(IconSize::Small)
123                    .icon_color(Color::Muted),
124                Tooltip::text("Customize Tools"),
125            )
126            .anchor(gpui::Corner::BottomLeft)
127    }
128}