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