1use editor::Editor;
2use gpui::{
3 div, AsyncWindowContext, IntoElement, ParentElement, Render, Subscription, Task, View,
4 ViewContext, WeakModel, WeakView,
5};
6use language::{Buffer, BufferEvent, LanguageName, Toolchain};
7use project::WorktreeId;
8use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, Tooltip};
9use workspace::{item::ItemHandle, StatusItemView, Workspace};
10
11use crate::ToolchainSelector;
12
13pub struct ActiveToolchain {
14 active_toolchain: Option<Toolchain>,
15 workspace: WeakView<Workspace>,
16 active_buffer: Option<(WorktreeId, WeakModel<Buffer>, Subscription)>,
17 _update_toolchain_task: Task<Option<()>>,
18}
19
20impl ActiveToolchain {
21 pub fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
22 Self {
23 active_toolchain: None,
24 active_buffer: None,
25 workspace: workspace.weak_handle(),
26
27 _update_toolchain_task: Self::spawn_tracker_task(cx),
28 }
29 }
30 fn spawn_tracker_task(cx: &mut ViewContext<Self>) -> Task<Option<()>> {
31 cx.spawn(|this, mut cx| async move {
32 let active_file = this
33 .update(&mut cx, |this, _| {
34 this.active_buffer
35 .as_ref()
36 .map(|(_, buffer, _)| buffer.clone())
37 })
38 .ok()
39 .flatten()?;
40 let workspace = this
41 .update(&mut cx, |this, _| this.workspace.clone())
42 .ok()?;
43 let language_name = active_file
44 .update(&mut cx, |this, _| Some(this.language()?.name()))
45 .ok()
46 .flatten()?;
47
48 let worktree_id = active_file
49 .update(&mut cx, |this, cx| Some(this.file()?.worktree_id(cx)))
50 .ok()
51 .flatten()?;
52 let toolchain =
53 Self::active_toolchain(workspace, worktree_id, language_name, cx.clone()).await?;
54 let _ = this.update(&mut cx, |this, cx| {
55 this.active_toolchain = Some(toolchain);
56
57 cx.notify();
58 });
59 Some(())
60 })
61 }
62
63 fn update_lister(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
64 let editor = editor.read(cx);
65 if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
66 if let Some(worktree_id) = buffer.read(cx).file().map(|file| file.worktree_id(cx)) {
67 let subscription = cx.subscribe(&buffer, |this, _, event: &BufferEvent, cx| {
68 if matches!(event, BufferEvent::LanguageChanged) {
69 this._update_toolchain_task = Self::spawn_tracker_task(cx);
70 }
71 });
72 self.active_buffer = Some((worktree_id, buffer.downgrade(), subscription));
73 self._update_toolchain_task = Self::spawn_tracker_task(cx);
74 }
75 }
76
77 cx.notify();
78 }
79
80 fn active_toolchain(
81 workspace: WeakView<Workspace>,
82 worktree_id: WorktreeId,
83 language_name: LanguageName,
84 cx: AsyncWindowContext,
85 ) -> Task<Option<Toolchain>> {
86 cx.spawn(move |mut cx| async move {
87 let workspace_id = workspace
88 .update(&mut cx, |this, _| this.database_id())
89 .ok()
90 .flatten()?;
91 let selected_toolchain = workspace
92 .update(&mut cx, |this, cx| {
93 this.project()
94 .read(cx)
95 .active_toolchain(worktree_id, language_name.clone(), cx)
96 })
97 .ok()?
98 .await;
99 if let Some(toolchain) = selected_toolchain {
100 Some(toolchain)
101 } else {
102 let project = workspace
103 .update(&mut cx, |this, _| this.project().clone())
104 .ok()?;
105 let toolchains = cx
106 .update(|cx| {
107 project
108 .read(cx)
109 .available_toolchains(worktree_id, language_name, cx)
110 })
111 .ok()?
112 .await?;
113 if let Some(toolchain) = toolchains.toolchains.first() {
114 // Since we don't have a selected toolchain, pick one for user here.
115 workspace::WORKSPACE_DB
116 .set_toolchain(workspace_id, worktree_id, toolchain.clone())
117 .await
118 .ok()?;
119 project
120 .update(&mut cx, |this, cx| {
121 this.activate_toolchain(worktree_id, toolchain.clone(), cx)
122 })
123 .ok()?
124 .await;
125 }
126
127 toolchains.toolchains.first().cloned()
128 }
129 })
130 }
131}
132
133impl Render for ActiveToolchain {
134 fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
135 div().when_some(self.active_toolchain.as_ref(), |el, active_toolchain| {
136 el.child(
137 Button::new("change-toolchain", active_toolchain.name.clone())
138 .label_size(LabelSize::Small)
139 .on_click(cx.listener(|this, _, cx| {
140 if let Some(workspace) = this.workspace.upgrade() {
141 workspace.update(cx, |workspace, cx| {
142 ToolchainSelector::toggle(workspace, cx)
143 });
144 }
145 }))
146 .tooltip(|cx| Tooltip::text("Select Toolchain", cx)),
147 )
148 })
149 }
150}
151
152impl StatusItemView for ActiveToolchain {
153 fn set_active_pane_item(
154 &mut self,
155 active_pane_item: Option<&dyn ItemHandle>,
156 cx: &mut ViewContext<Self>,
157 ) {
158 if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
159 self.active_toolchain.take();
160 self.update_lister(editor, cx);
161 }
162 cx.notify();
163 }
164}