python.rs

 1use db::kvp::Dismissable;
 2use editor::Editor;
 3use gpui::{Context, EventEmitter, Subscription};
 4use ui::{
 5    Banner, Button, Clickable, Color, FluentBuilder as _, IconButton, IconName,
 6    InteractiveElement as _, IntoElement, Label, LabelCommon, LabelSize, ParentElement as _,
 7    Render, Styled as _, Window, div, h_flex, v_flex,
 8};
 9use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace};
10
11pub struct BasedPyrightBanner {
12    dismissed: bool,
13    have_basedpyright: bool,
14    _subscriptions: [Subscription; 1],
15}
16
17impl Dismissable for BasedPyrightBanner {
18    const KEY: &str = "basedpyright-banner";
19}
20
21impl BasedPyrightBanner {
22    pub fn new(workspace: &Workspace, cx: &mut Context<Self>) -> Self {
23        let subscription = cx.subscribe(workspace.project(), |this, _, event, _| {
24            if let project::Event::LanguageServerAdded(_, name, _) = event
25                && name == "basedpyright"
26            {
27                this.have_basedpyright = true;
28            }
29        });
30        let dismissed = Self::dismissed();
31        Self {
32            dismissed,
33            have_basedpyright: false,
34            _subscriptions: [subscription],
35        }
36    }
37}
38
39impl EventEmitter<ToolbarItemEvent> for BasedPyrightBanner {}
40
41impl Render for BasedPyrightBanner {
42    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
43        div()
44            .id("basedpyright-banner")
45            .when(!self.dismissed && self.have_basedpyright, |el| {
46                el.child(
47                    Banner::new()
48                        .severity(ui::Severity::Info)
49                        .child(
50                            h_flex()
51                                .gap_2()
52                                .child(v_flex()
53                                    .child("Basedpyright is now the only default language server for Python")
54                                    .child(Label::new("We have disabled PyRight and pylsp by default. They can be re-enabled in your settings.").size(LabelSize::XSmall).color(Color::Muted))
55                                )
56                                .child(
57                                    Button::new("learn-more", "Learn More")
58                                        .icon(IconName::ArrowUpRight)
59                                        .on_click(|_, _, cx| {
60                                            cx.open_url("https://zed.dev/docs/languages/python")
61                                        }),
62                                ),
63                        )
64                        .action_slot(IconButton::new("dismiss", IconName::Close).on_click(
65                            cx.listener(|this, _, _, cx| {
66                                this.dismissed = true;
67                                Self::set_dismissed(true, cx);
68                                cx.notify();
69                            }),
70                        ))
71                        .into_any_element(),
72                )
73            })
74    }
75}
76
77impl ToolbarItemView for BasedPyrightBanner {
78    fn set_active_pane_item(
79        &mut self,
80        active_pane_item: Option<&dyn workspace::ItemHandle>,
81        _window: &mut ui::Window,
82        cx: &mut Context<Self>,
83    ) -> ToolbarItemLocation {
84        if let Some(item) = active_pane_item
85            && let Some(editor) = item.act_as::<Editor>(cx)
86            && let Some(path) = editor.update(cx, |editor, cx| editor.target_file_abs_path(cx))
87            && let Some(file_name) = path.file_name()
88            && file_name.as_encoded_bytes().ends_with(".py".as_bytes())
89        {
90            return ToolbarItemLocation::Secondary;
91        }
92
93        ToolbarItemLocation::Hidden
94    }
95}