1use std::sync::Arc;
2
3use anyhow::Result;
4use context_server::ContextServerCommand;
5use extension::{
6 ContextServerConfiguration, Extension, ExtensionContextServerProxy, ExtensionHostProxy,
7 ProjectDelegate,
8};
9use gpui::{App, AsyncApp, Entity, Task};
10
11use crate::worktree_store::WorktreeStore;
12
13use super::registry::{self, ContextServerDescriptorRegistry};
14
15pub fn init(cx: &mut App) {
16 let proxy = ExtensionHostProxy::default_global(cx);
17 proxy.register_context_server_proxy(ContextServerDescriptorRegistryProxy {
18 context_server_factory_registry: ContextServerDescriptorRegistry::default_global(cx),
19 });
20}
21
22struct ExtensionProject {
23 worktree_ids: Vec<u64>,
24}
25
26impl ProjectDelegate for ExtensionProject {
27 fn worktree_ids(&self) -> Vec<u64> {
28 self.worktree_ids.clone()
29 }
30}
31
32struct ContextServerDescriptor {
33 id: Arc<str>,
34 extension: Arc<dyn Extension>,
35}
36
37fn extension_project(
38 worktree_store: Entity<WorktreeStore>,
39 cx: &mut AsyncApp,
40) -> Result<Arc<ExtensionProject>> {
41 worktree_store.update(cx, |worktree_store, cx| {
42 Arc::new(ExtensionProject {
43 worktree_ids: worktree_store
44 .visible_worktrees(cx)
45 .map(|worktree| worktree.read(cx).id().to_proto())
46 .collect(),
47 })
48 })
49}
50
51impl registry::ContextServerDescriptor for ContextServerDescriptor {
52 fn command(
53 &self,
54 worktree_store: Entity<WorktreeStore>,
55 cx: &AsyncApp,
56 ) -> Task<Result<ContextServerCommand>> {
57 let id = self.id.clone();
58 let extension = self.extension.clone();
59 cx.spawn(async move |cx| {
60 let extension_project = extension_project(worktree_store, cx)?;
61 let mut command = extension
62 .context_server_command(id.clone(), extension_project.clone())
63 .await?;
64 command.command = extension
65 .path_from_extension(command.command.as_ref())
66 .to_string_lossy()
67 .to_string();
68
69 log::info!("loaded command for context server {id}: {command:?}");
70
71 Ok(ContextServerCommand {
72 path: command.command,
73 args: command.args,
74 env: Some(command.env.into_iter().collect()),
75 })
76 })
77 }
78
79 fn configuration(
80 &self,
81 worktree_store: Entity<WorktreeStore>,
82 cx: &AsyncApp,
83 ) -> Task<Result<Option<ContextServerConfiguration>>> {
84 let id = self.id.clone();
85 let extension = self.extension.clone();
86 cx.spawn(async move |cx| {
87 let extension_project = extension_project(worktree_store, cx)?;
88 let configuration = extension
89 .context_server_configuration(id.clone(), extension_project)
90 .await?;
91
92 log::debug!("loaded configuration for context server {id}: {configuration:?}");
93
94 Ok(configuration)
95 })
96 }
97}
98
99struct ContextServerDescriptorRegistryProxy {
100 context_server_factory_registry: Entity<ContextServerDescriptorRegistry>,
101}
102
103impl ExtensionContextServerProxy for ContextServerDescriptorRegistryProxy {
104 fn register_context_server(&self, extension: Arc<dyn Extension>, id: Arc<str>, cx: &mut App) {
105 self.context_server_factory_registry
106 .update(cx, |registry, _| {
107 registry.register_context_server_descriptor(
108 id.clone(),
109 Arc::new(ContextServerDescriptor { id, extension })
110 as Arc<dyn registry::ContextServerDescriptor>,
111 )
112 });
113 }
114
115 fn unregister_context_server(&self, server_id: Arc<str>, cx: &mut App) {
116 self.context_server_factory_registry
117 .update(cx, |registry, _| {
118 registry.unregister_context_server_descriptor_by_id(&server_id)
119 });
120 }
121}