tool_selector.rs

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