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