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 tools_by_source = self.tools.tools_by_source(cx);
24
25            for (source, tools) in tools_by_source {
26                let mut tools = tools
27                    .into_iter()
28                    .map(|tool| {
29                        let source = tool.source();
30                        let name = tool.name().into();
31                        let is_enabled = self.tools.is_enabled(&source, &name);
32
33                        (source, name, is_enabled)
34                    })
35                    .collect::<Vec<_>>();
36
37                if ToolSource::Native == source {
38                    tools.push((
39                        ToolSource::Native,
40                        ScriptingTool::NAME.into(),
41                        self.tools.is_scripting_tool_enabled(),
42                    ));
43                    tools.sort_by(|(_, name_a, _), (_, name_b, _)| name_a.cmp(name_b));
44                }
45
46                menu = match source {
47                    ToolSource::Native => menu.header("Zed"),
48                    ToolSource::ContextServer { id } => menu.separator().header(id),
49                };
50
51                for (source, name, is_enabled) in tools {
52                    menu =
53                        menu.toggleable_entry(name.clone(), is_enabled, IconPosition::End, None, {
54                            let tools = self.tools.clone();
55                            move |_window, _cx| {
56                                if name.as_ref() == ScriptingTool::NAME {
57                                    if is_enabled {
58                                        tools.disable_scripting_tool();
59                                    } else {
60                                        tools.enable_scripting_tool();
61                                    }
62                                } else {
63                                    if is_enabled {
64                                        tools.disable(source.clone(), &[name.clone()]);
65                                    } else {
66                                        tools.enable(source.clone(), &[name.clone()]);
67                                    }
68                                }
69                            }
70                        });
71                }
72            }
73
74            menu
75        })
76    }
77}
78
79impl Render for ToolSelector {
80    fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
81        let this = cx.entity().clone();
82        PopoverMenu::new("tool-selector")
83            .menu(move |window, cx| {
84                Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
85            })
86            .trigger_with_tooltip(
87                IconButton::new("tool-selector-button", IconName::SettingsAlt)
88                    .shape(IconButtonShape::Square)
89                    .icon_size(IconSize::Small)
90                    .icon_color(Color::Muted),
91                Tooltip::text("Customize Tools"),
92            )
93            .anchor(gpui::Corner::BottomLeft)
94    }
95}