Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:00:59.729Z 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

rust_analyzer_ext.rs

273 lines · 10.0 KB · rust
1use ::serde::{Deserialize, Serialize};
2use anyhow::Context as _;
3use gpui::{App, AsyncApp, Entity, Task, WeakEntity};
4use language::{Buffer, ServerHealth};
5use lsp::{LanguageServer, LanguageServerId, LanguageServerName};
6use rpc::proto;
7
8use crate::{LspStore, LspStoreEvent, Project, ProjectPath, lsp_store};
9
10pub const RUST_ANALYZER_NAME: LanguageServerName = LanguageServerName::new_static("rust-analyzer");
11pub const CARGO_DIAGNOSTICS_SOURCE_NAME: &str = "rustc";
12
13/// Experimental: Informs the end user about the state of the server
14///
15/// [Rust Analyzer Specification](https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#server-status)
16#[derive(Debug)]
17enum ServerStatus {}
18
19#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
20#[serde(rename_all = "camelCase")]
21struct ServerStatusParams {
22    pub health: ServerHealth,
23    pub message: Option<String>,
24}
25
26impl lsp::notification::Notification for ServerStatus {
27    type Params = ServerStatusParams;
28    const METHOD: &'static str = "experimental/serverStatus";
29}
30
31pub fn register_notifications(lsp_store: WeakEntity<LspStore>, language_server: &LanguageServer) {
32    let name = language_server.name();
33    let server_id = language_server.server_id();
34
35    language_server
36        .on_notification::<ServerStatus, _>({
37            move |params, cx| {
38                let message = params.message;
39                let log_message = message.as_ref().map(|message| {
40                    format!("Language server {name} (id {server_id}) status update: {message}")
41                });
42                let status = match &params.health {
43                    ServerHealth::Ok => {
44                        if let Some(log_message) = log_message {
45                            log::info!("{log_message}");
46                        }
47                        proto::ServerHealth::Ok
48                    }
49                    ServerHealth::Warning => {
50                        if let Some(log_message) = log_message {
51                            log::warn!("{log_message}");
52                        }
53                        proto::ServerHealth::Warning
54                    }
55                    ServerHealth::Error => {
56                        if let Some(log_message) = log_message {
57                            log::error!("{log_message}");
58                        }
59                        proto::ServerHealth::Error
60                    }
61                };
62
63                lsp_store
64                    .update(cx, |_, cx| {
65                        cx.emit(LspStoreEvent::LanguageServerUpdate {
66                            language_server_id: server_id,
67                            name: Some(name.clone()),
68                            message: proto::update_language_server::Variant::StatusUpdate(
69                                proto::StatusUpdate {
70                                    message,
71                                    status: Some(proto::status_update::Status::Health(
72                                        status as i32,
73                                    )),
74                                },
75                            ),
76                        });
77                    })
78                    .ok();
79            }
80        })
81        .detach();
82}
83
84pub fn cancel_flycheck(
85    project: Entity<Project>,
86    buffer_path: Option<ProjectPath>,
87    cx: &mut App,
88) -> Task<anyhow::Result<()>> {
89    let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
90    let lsp_store = project.read(cx).lsp_store();
91    let buffer = buffer_path.map(|buffer_path| {
92        project.update(cx, |project, cx| {
93            project.buffer_store().update(cx, |buffer_store, cx| {
94                buffer_store.open_buffer(buffer_path, cx)
95            })
96        })
97    });
98
99    cx.spawn(async move |cx| {
100        let buffer = match buffer {
101            Some(buffer) => Some(buffer.await?),
102            None => None,
103        };
104        let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
105        else {
106            return Ok(());
107        };
108
109        if let Some((client, project_id)) = upstream_client {
110            let request = proto::LspExtCancelFlycheck {
111                project_id,
112                language_server_id: rust_analyzer_server.to_proto(),
113            };
114            client
115                .request(request)
116                .await
117                .context("lsp ext cancel flycheck proto request")?;
118        } else {
119            lsp_store
120                .read_with(cx, |lsp_store, _| {
121                    if let Some(server) = lsp_store.language_server_for_id(rust_analyzer_server) {
122                        server.notify::<lsp_store::lsp_ext_command::LspExtCancelFlycheck>(())
123                    } else {
124                        Ok(())
125                    }
126                })
127                .context("lsp ext cancel flycheck")?;
128        };
129        anyhow::Ok(())
130    })
131}
132
133pub fn run_flycheck(
134    project: Entity<Project>,
135    buffer_path: Option<ProjectPath>,
136    cx: &mut App,
137) -> Task<anyhow::Result<()>> {
138    let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
139    let lsp_store = project.read(cx).lsp_store();
140    let buffer = buffer_path.map(|buffer_path| {
141        project.update(cx, |project, cx| {
142            project.buffer_store().update(cx, |buffer_store, cx| {
143                buffer_store.open_buffer(buffer_path, cx)
144            })
145        })
146    });
147
148    cx.spawn(async move |cx| {
149        let buffer = match buffer {
150            Some(buffer) => Some(buffer.await?),
151            None => None,
152        };
153        let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
154        else {
155            return Ok(());
156        };
157
158        if let Some((client, project_id)) = upstream_client {
159            let buffer_id = buffer
160                .map(|buffer| buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto()));
161            let request = proto::LspExtRunFlycheck {
162                project_id,
163                buffer_id,
164                language_server_id: rust_analyzer_server.to_proto(),
165                current_file_only: false,
166            };
167            client
168                .request(request)
169                .await
170                .context("lsp ext run flycheck proto request")?;
171        } else {
172            lsp_store
173                .read_with(cx, |lsp_store, _| {
174                    if let Some(server) = lsp_store.language_server_for_id(rust_analyzer_server) {
175                        server.notify::<lsp_store::lsp_ext_command::LspExtRunFlycheck>(
176                            lsp_store::lsp_ext_command::RunFlycheckParams {
177                                text_document: None,
178                            },
179                        )
180                    } else {
181                        Ok(())
182                    }
183                })
184                .context("lsp ext run flycheck")?;
185        };
186        anyhow::Ok(())
187    })
188}
189
190pub fn clear_flycheck(
191    project: Entity<Project>,
192    buffer_path: Option<ProjectPath>,
193    cx: &mut App,
194) -> Task<anyhow::Result<()>> {
195    let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
196    let lsp_store = project.read(cx).lsp_store();
197    let buffer = buffer_path.map(|buffer_path| {
198        project.update(cx, |project, cx| {
199            project.buffer_store().update(cx, |buffer_store, cx| {
200                buffer_store.open_buffer(buffer_path, cx)
201            })
202        })
203    });
204
205    cx.spawn(async move |cx| {
206        let buffer = match buffer {
207            Some(buffer) => Some(buffer.await?),
208            None => None,
209        };
210        let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
211        else {
212            return Ok(());
213        };
214
215        if let Some((client, project_id)) = upstream_client {
216            let request = proto::LspExtClearFlycheck {
217                project_id,
218                language_server_id: rust_analyzer_server.to_proto(),
219            };
220            client
221                .request(request)
222                .await
223                .context("lsp ext clear flycheck proto request")?;
224        } else {
225            lsp_store
226                .read_with(cx, |lsp_store, _| {
227                    if let Some(server) = lsp_store.language_server_for_id(rust_analyzer_server) {
228                        server.notify::<lsp_store::lsp_ext_command::LspExtClearFlycheck>(())
229                    } else {
230                        Ok(())
231                    }
232                })
233                .context("lsp ext clear flycheck")?;
234        };
235        anyhow::Ok(())
236    })
237}
238
239fn find_rust_analyzer_server(
240    project: &Entity<Project>,
241    buffer: Option<&Entity<Buffer>>,
242    cx: &mut AsyncApp,
243) -> Option<LanguageServerId> {
244    project.read_with(cx, |project, cx| {
245        buffer
246            .and_then(|buffer| {
247                project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
248            })
249            // If no rust-analyzer found for the current buffer (e.g. `settings.json`), fall back to the project lookup
250            // and use project's rust-analyzer if it's the only one.
251            .or_else(|| {
252                let rust_analyzer_servers = project
253                    .lsp_store()
254                    .read(cx)
255                    .language_server_statuses
256                    .iter()
257                    .filter_map(|(server_id, server_status)| {
258                        if server_status.name == RUST_ANALYZER_NAME {
259                            Some(*server_id)
260                        } else {
261                            None
262                        }
263                    })
264                    .collect::<Vec<_>>();
265                if rust_analyzer_servers.len() == 1 {
266                    rust_analyzer_servers.first().copied()
267                } else {
268                    None
269                }
270            })
271    })
272}
273
Served at tenant.openagents/omega Member data and write actions are omitted.