Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T03:57:48.549Z 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

toolchain_store.rs

780 lines · 27.2 KB · rust
1use std::{path::PathBuf, str::FromStr, sync::Arc};
2
3use anyhow::{Context as _, Result, bail};
4
5use async_trait::async_trait;
6use collections::{BTreeMap, IndexSet};
7
8use gpui::{
9    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,
10};
11use language::{
12    LanguageName, LanguageRegistry, LanguageToolchainStore, ManifestDelegate, Toolchain,
13    ToolchainList, ToolchainScope,
14};
15use rpc::{
16    AnyProtoClient, TypedEnvelope,
17    proto::{
18        self, ResolveToolchainResponse,
19        resolve_toolchain_response::Response as ResolveResponsePayload,
20    },
21};
22use settings::WorktreeId;
23use task::Shell;
24use util::{ResultExt as _, rel_path::RelPath};
25
26use crate::{
27    ProjectEnvironment, ProjectPath,
28    manifest_tree::{ManifestQueryDelegate, ManifestTree},
29    worktree_store::WorktreeStore,
30};
31
32pub struct ToolchainStore {
33    mode: ToolchainStoreInner,
34    user_toolchains: BTreeMap<ToolchainScope, IndexSet<Toolchain>>,
35    worktree_store: Entity<WorktreeStore>,
36    _sub: Subscription,
37}
38
39enum ToolchainStoreInner {
40    Local(Entity<LocalToolchainStore>),
41    Remote(Entity<RemoteToolchainStore>),
42}
43
44pub struct Toolchains {
45    /// Auto-detected toolchains.
46    pub toolchains: ToolchainList,
47    /// Path of the project root at which we ran the automatic toolchain detection.
48    pub root_path: Arc<RelPath>,
49    pub user_toolchains: BTreeMap<ToolchainScope, IndexSet<Toolchain>>,
50}
51impl EventEmitter<ToolchainStoreEvent> for ToolchainStore {}
52impl ToolchainStore {
53    pub fn init(client: &AnyProtoClient) {
54        client.add_entity_request_handler(Self::handle_activate_toolchain);
55        client.add_entity_request_handler(Self::handle_list_toolchains);
56        client.add_entity_request_handler(Self::handle_active_toolchain);
57        client.add_entity_request_handler(Self::handle_resolve_toolchain);
58    }
59
60    pub fn local(
61        languages: Arc<LanguageRegistry>,
62        worktree_store: Entity<WorktreeStore>,
63        project_environment: Entity<ProjectEnvironment>,
64        manifest_tree: Entity<ManifestTree>,
65        cx: &mut Context<Self>,
66    ) -> Self {
67        let entity = cx.new(|_| LocalToolchainStore {
68            languages,
69            worktree_store: worktree_store.clone(),
70            project_environment,
71            active_toolchains: Default::default(),
72            manifest_tree,
73        });
74        let _sub = cx.subscribe(&entity, |_, _, e: &ToolchainStoreEvent, cx| {
75            cx.emit(e.clone())
76        });
77        Self {
78            mode: ToolchainStoreInner::Local(entity),
79            worktree_store,
80            user_toolchains: Default::default(),
81            _sub,
82        }
83    }
84
85    pub(super) fn remote(
86        project_id: u64,
87        worktree_store: Entity<WorktreeStore>,
88        client: AnyProtoClient,
89        cx: &mut Context<Self>,
90    ) -> Self {
91        let entity = cx.new(|_| RemoteToolchainStore { client, project_id });
92        let _sub = cx.subscribe(&entity, |_, _, e: &ToolchainStoreEvent, cx| {
93            cx.emit(e.clone())
94        });
95        Self {
96            mode: ToolchainStoreInner::Remote(entity),
97            user_toolchains: Default::default(),
98            worktree_store,
99            _sub,
100        }
101    }
102    pub(crate) fn activate_toolchain(
103        &self,
104        path: ProjectPath,
105        toolchain: Toolchain,
106        cx: &mut App,
107    ) -> Task<Option<()>> {
108        match &self.mode {
109            ToolchainStoreInner::Local(local) => {
110                local.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
111            }
112            ToolchainStoreInner::Remote(remote) => {
113                remote.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
114            }
115        }
116    }
117
118    pub(crate) fn user_toolchains(&self) -> BTreeMap<ToolchainScope, IndexSet<Toolchain>> {
119        self.user_toolchains.clone()
120    }
121    pub(crate) fn add_toolchain(
122        &mut self,
123        toolchain: Toolchain,
124        scope: ToolchainScope,
125        cx: &mut Context<Self>,
126    ) {
127        let did_insert = self
128            .user_toolchains
129            .entry(scope)
130            .or_default()
131            .insert(toolchain);
132        if did_insert {
133            cx.emit(ToolchainStoreEvent::CustomToolchainsModified);
134        }
135    }
136
137    pub(crate) fn remove_toolchain(
138        &mut self,
139        toolchain: Toolchain,
140        scope: ToolchainScope,
141        cx: &mut Context<Self>,
142    ) {
143        let mut did_remove = false;
144        self.user_toolchains
145            .entry(scope)
146            .and_modify(|toolchains| did_remove = toolchains.shift_remove(&toolchain));
147        if did_remove {
148            cx.emit(ToolchainStoreEvent::CustomToolchainsModified);
149        }
150    }
151
152    pub(crate) fn resolve_toolchain(
153        &self,
154        abs_path: PathBuf,
155        language_name: LanguageName,
156        cx: &mut Context<Self>,
157    ) -> Task<Result<Toolchain>> {
158        debug_assert!(abs_path.is_absolute());
159        match &self.mode {
160            ToolchainStoreInner::Local(local) => local.update(cx, |this, cx| {
161                this.resolve_toolchain(abs_path, language_name, cx)
162            }),
163            ToolchainStoreInner::Remote(remote) => remote.update(cx, |this, cx| {
164                this.resolve_toolchain(abs_path, language_name, cx)
165            }),
166        }
167    }
168    pub(crate) fn list_toolchains(
169        &self,
170        path: ProjectPath,
171        language_name: LanguageName,
172        cx: &mut Context<Self>,
173    ) -> Task<Option<Toolchains>> {
174        let Some(worktree) = self
175            .worktree_store
176            .read(cx)
177            .worktree_for_id(path.worktree_id, cx)
178        else {
179            return Task::ready(None);
180        };
181        let target_root_path = worktree.read_with(cx, |this, _| this.abs_path());
182
183        let user_toolchains = self
184            .user_toolchains
185            .iter()
186            .filter(|(scope, _)| {
187                if let ToolchainScope::Subproject(subproject_root_path, relative_path) = scope {
188                    target_root_path == *subproject_root_path
189                        && relative_path.starts_with(&path.path)
190                } else {
191                    true
192                }
193            })
194            .map(|(scope, toolchains)| {
195                (
196                    scope.clone(),
197                    toolchains
198                        .iter()
199                        .filter(|toolchain| toolchain.language_name == language_name)
200                        .cloned()
201                        .collect::<IndexSet<_>>(),
202                )
203            })
204            .collect::<BTreeMap<_, _>>();
205        let task = match &self.mode {
206            ToolchainStoreInner::Local(local) => {
207                local.update(cx, |this, cx| this.list_toolchains(path, language_name, cx))
208            }
209            ToolchainStoreInner::Remote(remote) => {
210                remote.read(cx).list_toolchains(path, language_name, cx)
211            }
212        };
213        cx.spawn(async move |_, _| {
214            let (mut toolchains, root_path) = task.await?;
215            toolchains.toolchains.retain(|toolchain| {
216                !user_toolchains
217                    .values()
218                    .any(|toolchains| toolchains.contains(toolchain))
219            });
220
221            Some(Toolchains {
222                toolchains,
223                root_path,
224                user_toolchains,
225            })
226        })
227    }
228
229    pub(crate) fn active_toolchain(
230        &self,
231        path: ProjectPath,
232        language_name: LanguageName,
233        cx: &App,
234    ) -> Task<Option<Toolchain>> {
235        match &self.mode {
236            ToolchainStoreInner::Local(local) => Task::ready(local.read(cx).active_toolchain(
237                path.worktree_id,
238                &path.path,
239                language_name,
240            )),
241            ToolchainStoreInner::Remote(remote) => {
242                remote.read(cx).active_toolchain(path, language_name, cx)
243            }
244        }
245    }
246    async fn handle_activate_toolchain(
247        this: Entity<Self>,
248        envelope: TypedEnvelope<proto::ActivateToolchain>,
249        mut cx: AsyncApp,
250    ) -> Result<proto::Ack> {
251        this.update(&mut cx, |this, cx| {
252            let language_name = LanguageName::from_proto(envelope.payload.language_name);
253            let Some(toolchain) = envelope.payload.toolchain else {
254                bail!("Missing `toolchain` in payload");
255            };
256            let toolchain = Toolchain {
257                name: toolchain.name.into(),
258                // todo(windows)
259                // Do we need to convert path to native string?
260                path: toolchain.path.into(),
261                as_json: serde_json::Value::from_str(&toolchain.raw_json)?,
262                language_name,
263            };
264            let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
265            let path = if let Some(path) = envelope.payload.path {
266                RelPath::from_unix_str(&path)?.into()
267            } else {
268                RelPath::empty_arc()
269            };
270            Ok(this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx))
271        })?
272        .await;
273        Ok(proto::Ack {})
274    }
275    async fn handle_active_toolchain(
276        this: Entity<Self>,
277        envelope: TypedEnvelope<proto::ActiveToolchain>,
278        mut cx: AsyncApp,
279    ) -> Result<proto::ActiveToolchainResponse> {
280        let path = RelPath::from_unix_str(envelope.payload.path.as_deref().unwrap_or(""))?;
281        let toolchain = this
282            .update(&mut cx, |this, cx| {
283                let language_name = LanguageName::from_proto(envelope.payload.language_name);
284                let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
285                this.active_toolchain(
286                    ProjectPath {
287                        worktree_id,
288                        path: Arc::from(path),
289                    },
290                    language_name,
291                    cx,
292                )
293            })
294            .await;
295
296        Ok(proto::ActiveToolchainResponse {
297            toolchain: toolchain.map(|toolchain| {
298                let path = PathBuf::from(toolchain.path.to_string());
299                proto::Toolchain {
300                    name: toolchain.name.into(),
301                    path: path.to_string_lossy().into_owned(),
302                    raw_json: toolchain.as_json.to_string(),
303                }
304            }),
305        })
306    }
307
308    async fn handle_list_toolchains(
309        this: Entity<Self>,
310        envelope: TypedEnvelope<proto::ListToolchains>,
311        mut cx: AsyncApp,
312    ) -> Result<proto::ListToolchainsResponse> {
313        let toolchains = this
314            .update(&mut cx, |this, cx| {
315                let language_name = LanguageName::from_proto(envelope.payload.language_name);
316                let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
317                let path =
318                    RelPath::from_unix_str(envelope.payload.path.as_deref().unwrap_or(""))?.into();
319                anyhow::Ok(this.list_toolchains(
320                    ProjectPath { worktree_id, path },
321                    language_name,
322                    cx,
323                ))
324            })?
325            .await;
326        let has_values = toolchains.is_some();
327        let groups = if let Some(Toolchains { toolchains, .. }) = &toolchains {
328            toolchains
329                .groups
330                .iter()
331                .filter_map(|group| {
332                    Some(proto::ToolchainGroup {
333                        start_index: u64::try_from(group.0).ok()?,
334                        name: String::from(group.1.as_ref()),
335                    })
336                })
337                .collect()
338        } else {
339            vec![]
340        };
341        let (toolchains, relative_path) = if let Some(Toolchains {
342            toolchains,
343            root_path: relative_path,
344            ..
345        }) = toolchains
346        {
347            let toolchains = toolchains
348                .toolchains
349                .into_iter()
350                .map(|toolchain| {
351                    let path = PathBuf::from(toolchain.path.to_string());
352                    proto::Toolchain {
353                        name: toolchain.name.to_string(),
354                        path: path.to_string_lossy().into_owned(),
355                        raw_json: toolchain.as_json.to_string(),
356                    }
357                })
358                .collect::<Vec<_>>();
359            (toolchains, relative_path)
360        } else {
361            (vec![], Arc::from(RelPath::empty()))
362        };
363
364        Ok(proto::ListToolchainsResponse {
365            has_values,
366            toolchains,
367            groups,
368            relative_worktree_path: Some(relative_path.as_unix_str().to_owned()),
369        })
370    }
371
372    async fn handle_resolve_toolchain(
373        this: Entity<Self>,
374        envelope: TypedEnvelope<proto::ResolveToolchain>,
375        mut cx: AsyncApp,
376    ) -> Result<proto::ResolveToolchainResponse> {
377        let toolchain = this
378            .update(&mut cx, |this, cx| {
379                let language_name = LanguageName::from_proto(envelope.payload.language_name);
380                let path = PathBuf::from(envelope.payload.abs_path);
381                this.resolve_toolchain(path, language_name, cx)
382            })
383            .await;
384        let response = match toolchain {
385            Ok(toolchain) => {
386                let toolchain = proto::Toolchain {
387                    name: toolchain.name.to_string(),
388                    path: toolchain.path.to_string(),
389                    raw_json: toolchain.as_json.to_string(),
390                };
391                ResolveResponsePayload::Toolchain(toolchain)
392            }
393            Err(e) => ResolveResponsePayload::Error(e.to_string()),
394        };
395        Ok(ResolveToolchainResponse {
396            response: Some(response),
397        })
398    }
399
400    pub fn as_language_toolchain_store(&self) -> Arc<dyn LanguageToolchainStore> {
401        match &self.mode {
402            ToolchainStoreInner::Local(local) => Arc::new(LocalStore(local.downgrade())),
403            ToolchainStoreInner::Remote(remote) => Arc::new(RemoteStore(remote.downgrade())),
404        }
405    }
406    pub fn as_local_store(&self) -> Option<&Entity<LocalToolchainStore>> {
407        match &self.mode {
408            ToolchainStoreInner::Local(local) => Some(local),
409            ToolchainStoreInner::Remote(_) => None,
410        }
411    }
412}
413
414pub struct LocalToolchainStore {
415    languages: Arc<LanguageRegistry>,
416    worktree_store: Entity<WorktreeStore>,
417    project_environment: Entity<ProjectEnvironment>,
418    active_toolchains: BTreeMap<(WorktreeId, LanguageName), BTreeMap<Arc<RelPath>, Toolchain>>,
419    manifest_tree: Entity<ManifestTree>,
420}
421
422#[async_trait(?Send)]
423impl language::LocalLanguageToolchainStore for LocalStore {
424    fn active_toolchain(
425        self: Arc<Self>,
426        worktree_id: WorktreeId,
427        path: &Arc<RelPath>,
428        language_name: LanguageName,
429        cx: &mut AsyncApp,
430    ) -> Option<Toolchain> {
431        self.0
432            .update(cx, |this, _| {
433                this.active_toolchain(worktree_id, path, language_name)
434            })
435            .ok()?
436    }
437}
438
439#[async_trait(?Send)]
440impl language::LanguageToolchainStore for RemoteStore {
441    async fn active_toolchain(
442        self: Arc<Self>,
443        worktree_id: WorktreeId,
444        path: Arc<RelPath>,
445        language_name: LanguageName,
446        cx: &mut AsyncApp,
447    ) -> Option<Toolchain> {
448        self.0
449            .update(cx, |this, cx| {
450                this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx)
451            })
452            .ok()?
453            .await
454    }
455}
456
457pub struct EmptyToolchainStore;
458impl language::LocalLanguageToolchainStore for EmptyToolchainStore {
459    fn active_toolchain(
460        self: Arc<Self>,
461        _: WorktreeId,
462        _: &Arc<RelPath>,
463        _: LanguageName,
464        _: &mut AsyncApp,
465    ) -> Option<Toolchain> {
466        None
467    }
468}
469pub(crate) struct LocalStore(WeakEntity<LocalToolchainStore>);
470struct RemoteStore(WeakEntity<RemoteToolchainStore>);
471
472#[derive(Clone)]
473pub enum ToolchainStoreEvent {
474    ToolchainActivated,
475    CustomToolchainsModified,
476}
477
478impl EventEmitter<ToolchainStoreEvent> for LocalToolchainStore {}
479
480impl LocalToolchainStore {
481    pub(crate) fn activate_toolchain(
482        &self,
483        path: ProjectPath,
484        toolchain: Toolchain,
485        cx: &mut Context<Self>,
486    ) -> Task<Option<()>> {
487        cx.spawn(async move |this, cx| {
488            this.update(cx, |this, cx| {
489                this.active_toolchains
490                    .entry((path.worktree_id, toolchain.language_name.clone()))
491                    .or_default()
492                    .insert(path.path, toolchain.clone());
493                cx.emit(ToolchainStoreEvent::ToolchainActivated);
494            })
495            .ok();
496            Some(())
497        })
498    }
499    pub(crate) fn list_toolchains(
500        &mut self,
501        path: ProjectPath,
502        language_name: LanguageName,
503        cx: &mut Context<Self>,
504    ) -> Task<Option<(ToolchainList, Arc<RelPath>)>> {
505        let registry = self.languages.clone();
506
507        let manifest_tree = self.manifest_tree.downgrade();
508
509        let environment = self.project_environment.clone();
510        cx.spawn(async move |this, cx| {
511            let language = cx
512                .background_spawn(registry.language_for_name(language_name.as_ref()))
513                .await
514                .ok()?;
515            let toolchains = language.toolchain_lister()?;
516            let manifest_name = toolchains.meta().manifest_name;
517            let (snapshot, worktree) = this
518                .update(cx, |this, cx| {
519                    this.worktree_store
520                        .read(cx)
521                        .worktree_for_id(path.worktree_id, cx)
522                        .map(|worktree| (worktree.read(cx).snapshot(), worktree))
523                })
524                .ok()
525                .flatten()?;
526            let worktree_id = snapshot.id();
527            let worktree_root = snapshot.abs_path().to_path_buf();
528            let delegate =
529                Arc::from(ManifestQueryDelegate::new(snapshot)) as Arc<dyn ManifestDelegate>;
530            let relative_path = manifest_tree
531                .update(cx, |this, cx| {
532                    this.root_for_path(&path, &manifest_name, &delegate, cx)
533                })
534                .ok()?
535                .unwrap_or_else(|| ProjectPath {
536                    path: Arc::from(RelPath::empty()),
537                    worktree_id,
538                });
539            let abs_path = worktree.update(cx, |this, _| this.absolutize(&relative_path.path));
540
541            let project_env = environment
542                .update(cx, |environment, cx| {
543                    environment.local_directory_environment(
544                        &Shell::System,
545                        abs_path.as_path().into(),
546                        cx,
547                    )
548                })
549                .await;
550
551            cx.background_spawn(async move {
552                Some((
553                    toolchains
554                        .list(worktree_root, relative_path.path.clone(), project_env)
555                        .await,
556                    relative_path.path,
557                ))
558            })
559            .await
560        })
561    }
562    pub(crate) fn active_toolchain(
563        &self,
564        worktree_id: WorktreeId,
565        relative_path: &Arc<RelPath>,
566        language_name: LanguageName,
567    ) -> Option<Toolchain> {
568        let ancestors = relative_path.ancestors();
569
570        self.active_toolchains
571            .get(&(worktree_id, language_name))
572            .and_then(|paths| {
573                ancestors
574                    .into_iter()
575                    .find_map(|root_path| paths.get(root_path))
576            })
577            .cloned()
578    }
579
580    fn resolve_toolchain(
581        &self,
582        path: PathBuf,
583        language_name: LanguageName,
584        cx: &mut Context<Self>,
585    ) -> Task<Result<Toolchain>> {
586        let registry = self.languages.clone();
587        let environment = self.project_environment.clone();
588        cx.spawn(async move |_, cx| {
589            let language = cx
590                .background_spawn(registry.language_for_name(&language_name.0))
591                .await
592                .with_context(|| format!("Language {} not found", language_name.0))?;
593            let toolchain_lister = language.toolchain_lister().with_context(|| {
594                format!("Language {} does not support toolchains", language_name.0)
595            })?;
596
597            let project_env = environment
598                .update(cx, |environment, cx| {
599                    environment.local_directory_environment(
600                        &Shell::System,
601                        path.as_path().into(),
602                        cx,
603                    )
604                })
605                .await;
606            cx.background_spawn(async move { toolchain_lister.resolve(path, project_env).await })
607                .await
608        })
609    }
610}
611
612impl EventEmitter<ToolchainStoreEvent> for RemoteToolchainStore {}
613struct RemoteToolchainStore {
614    client: AnyProtoClient,
615    project_id: u64,
616}
617
618impl RemoteToolchainStore {
619    pub(crate) fn activate_toolchain(
620        &self,
621        project_path: ProjectPath,
622        toolchain: Toolchain,
623        cx: &mut Context<Self>,
624    ) -> Task<Option<()>> {
625        let project_id = self.project_id;
626        let client = self.client.clone();
627        cx.spawn(async move |this, cx| {
628            let did_activate = cx
629                .background_spawn(async move {
630                    let path = PathBuf::from(toolchain.path.to_string());
631                    let _ = client
632                        .request(proto::ActivateToolchain {
633                            project_id,
634                            worktree_id: project_path.worktree_id.to_proto(),
635                            language_name: toolchain.language_name.into(),
636                            toolchain: Some(proto::Toolchain {
637                                name: toolchain.name.into(),
638                                path: path.to_string_lossy().into_owned(),
639                                raw_json: toolchain.as_json.to_string(),
640                            }),
641                            path: Some(project_path.path.as_unix_str().to_owned()),
642                        })
643                        .await
644                        .log_err()?;
645                    Some(())
646                })
647                .await;
648            did_activate.and_then(|_| {
649                this.update(cx, |_, cx| {
650                    cx.emit(ToolchainStoreEvent::ToolchainActivated);
651                })
652                .ok()
653            })
654        })
655    }
656
657    pub(crate) fn list_toolchains(
658        &self,
659        path: ProjectPath,
660        language_name: LanguageName,
661        cx: &App,
662    ) -> Task<Option<(ToolchainList, Arc<RelPath>)>> {
663        let project_id = self.project_id;
664        let client = self.client.clone();
665        cx.background_spawn(async move {
666            let response = client
667                .request(proto::ListToolchains {
668                    project_id,
669                    worktree_id: path.worktree_id.to_proto(),
670                    language_name: language_name.clone().into(),
671                    path: Some(path.path.as_unix_str().to_owned()),
672                })
673                .await
674                .log_err()?;
675            if !response.has_values {
676                return None;
677            }
678            let toolchains = response
679                .toolchains
680                .into_iter()
681                .filter_map(|toolchain| {
682                    Some(Toolchain {
683                        language_name: language_name.clone(),
684                        name: toolchain.name.into(),
685                        path: toolchain.path.into(),
686                        as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
687                    })
688                })
689                .collect();
690            let groups = response
691                .groups
692                .into_iter()
693                .filter_map(|group| {
694                    Some((usize::try_from(group.start_index).ok()?, group.name.into()))
695                })
696                .collect();
697            let relative_path = RelPath::from_unix_str(
698                response
699                    .relative_worktree_path
700                    .as_deref()
701                    .unwrap_or_default(),
702            )
703            .log_err()?;
704            Some((
705                ToolchainList {
706                    toolchains,
707                    default: None,
708                    groups,
709                },
710                relative_path.into(),
711            ))
712        })
713    }
714    pub(crate) fn active_toolchain(
715        &self,
716        path: ProjectPath,
717        language_name: LanguageName,
718        cx: &App,
719    ) -> Task<Option<Toolchain>> {
720        let project_id = self.project_id;
721        let client = self.client.clone();
722        cx.background_spawn(async move {
723            let response = client
724                .request(proto::ActiveToolchain {
725                    project_id,
726                    worktree_id: path.worktree_id.to_proto(),
727                    language_name: language_name.clone().into(),
728                    path: Some(path.path.as_unix_str().to_owned()),
729                })
730                .await
731                .log_err()?;
732
733            response.toolchain.and_then(|toolchain| {
734                Some(Toolchain {
735                    language_name: language_name.clone(),
736                    name: toolchain.name.into(),
737                    path: toolchain.path.into(),
738                    as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
739                })
740            })
741        })
742    }
743
744    fn resolve_toolchain(
745        &self,
746        abs_path: PathBuf,
747        language_name: LanguageName,
748        cx: &mut Context<Self>,
749    ) -> Task<Result<Toolchain>> {
750        let project_id = self.project_id;
751        let client = self.client.clone();
752        cx.background_spawn(async move {
753            let response: proto::ResolveToolchainResponse = client
754                .request(proto::ResolveToolchain {
755                    project_id,
756                    language_name: language_name.clone().into(),
757                    abs_path: abs_path.to_string_lossy().into_owned(),
758                })
759                .await?;
760
761            let response = response
762                .response
763                .context("Failed to resolve toolchain via RPC")?;
764            use proto::resolve_toolchain_response::Response;
765            match response {
766                Response::Toolchain(toolchain) => Ok(Toolchain {
767                    language_name: language_name.clone(),
768                    name: toolchain.name.into(),
769                    path: toolchain.path.into(),
770                    as_json: serde_json::Value::from_str(&toolchain.raw_json)
771                        .context("Deserializing ResolveToolchain LSP response")?,
772                }),
773                Response::Error(error) => {
774                    anyhow::bail!("{error}");
775                }
776            }
777        })
778    }
779}
780
Served at tenant.openagents/omega Member data and write actions are omitted.