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}