Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:34:39.165Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

active_toolchain.rs

275 lines · 10.4 KB · rust
1use std::sync::Arc;
2
3use editor::Editor;
4use gpui::{
5    App, AsyncWindowContext, Context, Entity, IntoElement, ParentElement, Render, Styled,
6    Subscription, Task, WeakEntity, Window, div,
7};
8use language::{Buffer, BufferEvent, LanguageName, Toolchain, ToolchainScope};
9use project::{Project, ProjectPath, Toolchains, WorktreeId, toolchain_store::ToolchainStoreEvent};
10use ui::{Button, ButtonCommon, Clickable, LabelSize, SharedString, Tooltip};
11use util::{maybe, rel_path::RelPath};
12use workspace::{HideStatusItem, StatusItemView, Workspace, item::ItemHandle};
13
14use crate::ToolchainSelector;
15
16pub struct ActiveToolchain {
17    active_toolchain: Option<Toolchain>,
18    term: SharedString,
19    workspace: WeakEntity<Workspace>,
20    active_buffer: Option<(WorktreeId, WeakEntity<Buffer>, Subscription)>,
21    _update_toolchain_task: Task<Option<()>>,
22}
23
24impl ActiveToolchain {
25    pub fn new(workspace: &Workspace, window: &mut Window, cx: &mut Context<Self>) -> Self {
26        if let Some(store) = workspace.project().read(cx).toolchain_store() {
27            cx.subscribe_in(
28                &store,
29                window,
30                |this, _, _: &ToolchainStoreEvent, window, cx| {
31                    let editor = this
32                        .workspace
33                        .update(cx, |workspace, cx| {
34                            workspace
35                                .active_item(cx)
36                                .and_then(|item| item.downcast::<Editor>())
37                        })
38                        .ok()
39                        .flatten();
40                    if let Some(editor) = editor {
41                        this.update_lister(editor, window, cx);
42                    }
43                },
44            )
45            .detach();
46        }
47        Self {
48            active_toolchain: None,
49            active_buffer: None,
50            term: SharedString::new_static("Toolchain"),
51            workspace: workspace.weak_handle(),
52
53            _update_toolchain_task: Self::spawn_tracker_task(window, cx),
54        }
55    }
56    fn spawn_tracker_task(window: &mut Window, cx: &mut Context<Self>) -> Task<Option<()>> {
57        cx.spawn_in(window, async move |this, cx| {
58            let did_set_toolchain = maybe!(async {
59                let active_file = this
60                    .read_with(cx, |this, _| {
61                        this.active_buffer
62                            .as_ref()
63                            .map(|(_, buffer, _)| buffer.clone())
64                    })
65                    .ok()
66                    .flatten()?;
67                let workspace = this.read_with(cx, |this, _| this.workspace.clone()).ok()?;
68                let language_name = active_file
69                    .read_with(cx, |this, _| Some(this.language()?.name()))
70                    .ok()
71                    .flatten()?;
72                let meta = workspace
73                    .update(cx, |workspace, cx| {
74                        let languages = workspace.project().read(cx).languages();
75                        Project::toolchain_metadata(languages.clone(), language_name.clone())
76                    })
77                    .ok()?
78                    .await?;
79                let _ = this.update(cx, |this, cx| {
80                    this.term = meta.term;
81                    cx.notify();
82                });
83                let (worktree_id, path) = active_file
84                    .update(cx, |this, cx| {
85                        this.file().and_then(|file| {
86                            Some((file.worktree_id(cx), file.path().parent()?.into()))
87                        })
88                    })
89                    .ok()
90                    .flatten()?;
91                let toolchain =
92                    Self::active_toolchain(workspace, worktree_id, path, language_name, cx).await?;
93                this.update(cx, |this, cx| {
94                    this.active_toolchain = Some(toolchain);
95
96                    cx.notify();
97                })
98                .ok()
99            })
100            .await
101            .is_some();
102            if !did_set_toolchain {
103                this.update(cx, |this, cx| {
104                    this.active_toolchain = None;
105                    cx.notify();
106                })
107                .ok();
108            }
109            did_set_toolchain.then_some(())
110        })
111    }
112
113    fn update_lister(
114        &mut self,
115        editor: Entity<Editor>,
116        window: &mut Window,
117        cx: &mut Context<Self>,
118    ) {
119        let editor = editor.read(cx);
120        if let Some(buffer) = editor.active_buffer(cx)
121            && let Some(worktree_id) = buffer.read(cx).file().map(|file| file.worktree_id(cx))
122        {
123            let subscription = cx.subscribe_in(
124                &buffer,
125                window,
126                |this, _, event: &BufferEvent, window, cx| {
127                    if matches!(event, BufferEvent::LanguageChanged(_)) {
128                        this._update_toolchain_task = Self::spawn_tracker_task(window, cx);
129                    }
130                },
131            );
132            self.active_buffer = Some((worktree_id, buffer.downgrade(), subscription));
133            self._update_toolchain_task = Self::spawn_tracker_task(window, cx);
134        }
135
136        cx.notify();
137    }
138
139    fn active_toolchain(
140        workspace: WeakEntity<Workspace>,
141        worktree_id: WorktreeId,
142        relative_path: Arc<RelPath>,
143        language_name: LanguageName,
144        cx: &mut AsyncWindowContext,
145    ) -> Task<Option<Toolchain>> {
146        cx.spawn(async move |cx| {
147            let workspace_id = workspace
148                .read_with(cx, |this, _| this.database_id())
149                .ok()
150                .flatten()?;
151            let selected_toolchain = workspace
152                .update(cx, |this, cx| {
153                    this.project().read(cx).active_toolchain(
154                        ProjectPath {
155                            worktree_id,
156                            path: relative_path.clone(),
157                        },
158                        language_name.clone(),
159                        cx,
160                    )
161                })
162                .ok()?
163                .await;
164            if let Some(toolchain) = selected_toolchain {
165                Some(toolchain)
166            } else {
167                let project = workspace
168                    .read_with(cx, |this, _| this.project().clone())
169                    .ok()?;
170                let Toolchains {
171                    toolchains,
172                    root_path: relative_path,
173                    user_toolchains,
174                } = cx
175                    .update(|_, cx| {
176                        project.read(cx).available_toolchains(
177                            ProjectPath {
178                                worktree_id,
179                                path: relative_path.clone(),
180                            },
181                            language_name,
182                            cx,
183                        )
184                    })
185                    .ok()?
186                    .await?;
187                // Since we don't have a selected toolchain, pick one for user here.
188                let default_choice = user_toolchains
189                    .iter()
190                    .find_map(|(scope, toolchains)| {
191                        if scope == &ToolchainScope::Global {
192                            // Ignore global toolchains when making a default choice. They're unlikely to be the right choice.
193                            None
194                        } else {
195                            toolchains.first()
196                        }
197                    })
198                    .or_else(|| toolchains.toolchains.first())
199                    .cloned();
200                if let Some(toolchain) = &default_choice {
201                    let worktree_root_path = project.read_with(cx, |this, cx| {
202                        this.worktree_for_id(worktree_id, cx)
203                            .map(|worktree| worktree.read(cx).abs_path())
204                    })?;
205                    let db = cx.update(|_, cx| workspace::WorkspaceDb::global(cx)).ok()?;
206                    db.set_toolchain(
207                        workspace_id,
208                        worktree_root_path,
209                        relative_path.clone(),
210                        toolchain.clone(),
211                    )
212                    .await
213                    .ok()?;
214                    project
215                        .update(cx, |this, cx| {
216                            this.activate_toolchain(
217                                ProjectPath {
218                                    worktree_id,
219                                    path: relative_path,
220                                },
221                                toolchain.clone(),
222                                cx,
223                            )
224                        })
225                        .await;
226                }
227
228                default_choice
229            }
230        })
231    }
232}
233
234impl Render for ActiveToolchain {
235    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
236        let Some(active_toolchain) = self.active_toolchain.as_ref() else {
237            return div().hidden();
238        };
239
240        div().child(
241            Button::new("change-toolchain", active_toolchain.name.clone())
242                .label_size(LabelSize::Small)
243                .tab_index(0isize)
244                .on_click(cx.listener(|this, _, window, cx| {
245                    if let Some(workspace) = this.workspace.upgrade() {
246                        workspace.update(cx, |workspace, cx| {
247                            ToolchainSelector::toggle(workspace, window, cx)
248                        });
249                    }
250                }))
251                .tooltip(Tooltip::text(format!("Select {}", &self.term))),
252        )
253    }
254}
255
256impl StatusItemView for ActiveToolchain {
257    fn set_active_pane_item(
258        &mut self,
259        active_pane_item: Option<&dyn ItemHandle>,
260        window: &mut Window,
261        cx: &mut Context<Self>,
262    ) {
263        if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
264            self.update_lister(editor, window, cx);
265        }
266        cx.notify();
267    }
268
269    fn hide_setting(&self, _: &App) -> Option<HideStatusItem> {
270        // The toolchain selector only appears when the active buffer has a
271        // language with toolchain support.
272        None
273    }
274}
275
Served at tenant.openagents/omega Member data and write actions are omitted.