active_toolchain.rs

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