active_toolchain.rs

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