Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T06:51:53.288Z 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

lsp_store.rs

391 lines · 11.9 KB · rust
1use std::{
2    path::Path,
3    sync::Arc,
4    time::{Duration, Instant},
5};
6
7use fs::FakeFs;
8use futures::StreamExt;
9use gpui::TestAppContext;
10use language::{CodeLabel, FakeLspAdapter, HighlightId, rust_lang};
11use lsp::Uri;
12use parking_lot::Mutex;
13use project::{
14    Project,
15    lsp_store::{log_store::TestRpcRequestTracker, *},
16};
17use serde_json::json;
18use util::path;
19
20use crate::init_test;
21
22#[gpui::test]
23async fn test_removing_invisible_worktree_cleans_reused_lsp_bookkeeping(cx: &mut TestAppContext) {
24    init_test(cx);
25    cx.executor().allow_parking();
26
27    let fs = FakeFs::new(cx.executor());
28    fs.insert_tree(path!("/the-root"), json!({ "main.rs": "fn main() {}" }))
29        .await;
30    fs.insert_tree(
31        path!("/the-registry"),
32        json!({ "dep": { "src": { "dep.rs": "pub fn dep() {}" } } }),
33    )
34    .await;
35
36    let project = Project::test(fs, [path!("/the-root").as_ref()], cx).await;
37    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
38    language_registry.add(rust_lang());
39    let mut fake_servers = language_registry.register_fake_lsp("Rust", FakeLspAdapter::default());
40
41    let (_visible_buffer, _visible_handle) = project
42        .update(cx, |project, cx| {
43            project.open_local_buffer_with_lsp(path!("/the-root/main.rs"), cx)
44        })
45        .await
46        .unwrap();
47    fake_servers.next().await.unwrap();
48    cx.run_until_parked();
49
50    let server_id = project.read_with(cx, |project, cx| {
51        project
52            .lsp_store()
53            .read(cx)
54            .language_server_statuses()
55            .next()
56            .unwrap()
57            .0
58    });
59    let external_buffer = project
60        .update(cx, |project, cx| {
61            project.open_local_buffer_via_lsp(
62                Uri::from_file_path(path!("/the-registry/dep/src/dep.rs")).unwrap(),
63                server_id,
64                cx,
65            )
66        })
67        .await
68        .unwrap();
69    cx.run_until_parked();
70
71    let invisible_worktree_id =
72        external_buffer.read_with(cx, |buffer, cx| buffer.file().unwrap().worktree_id(cx));
73    project.read_with(cx, |project, cx| {
74        let worktree = project.worktree_for_id(invisible_worktree_id, cx).unwrap();
75        assert!(!worktree.read(cx).is_visible());
76        assert!(
77            project
78                .lsp_store()
79                .read(cx)
80                .has_language_server_seed_for_worktree(invisible_worktree_id)
81        );
82    });
83
84    project.update(cx, |project, cx| {
85        project.remove_worktree(invisible_worktree_id, cx);
86    });
87    cx.run_until_parked();
88
89    project.read_with(cx, |project, cx| {
90        let lsp_store = project.lsp_store();
91        let lsp_store = lsp_store.read(cx);
92        assert!(
93            lsp_store
94                .language_server_statuses()
95                .any(|(status_server_id, _)| status_server_id == server_id)
96        );
97        assert!(!lsp_store.has_language_server_seed_for_worktree(invisible_worktree_id));
98    });
99}
100
101#[test]
102fn test_rpc_request_tracker_distinguishes_request_directions() {
103    let mut tracker = TestRpcRequestTracker::new();
104    let started_at = Instant::now();
105
106    assert_eq!(
107        tracker.observe(
108            false,
109            r#"{"jsonrpc":"2.0","id":1,"method":"textDocument/hover"}"#,
110            started_at,
111        ),
112        None
113    );
114    assert_eq!(
115        tracker.observe(
116            true,
117            r#"{"jsonrpc":"2.0","id":1,"method":"workspace/configuration"}"#,
118            started_at + Duration::from_millis(10),
119        ),
120        None
121    );
122    assert_eq!(
123        tracker.observe(
124            false,
125            r#"{"jsonrpc":"2.0","id":1,"result":[]}"#,
126            started_at + Duration::from_millis(30),
127        ),
128        Some(Duration::from_millis(20))
129    );
130    assert_eq!(
131        tracker.observe(
132            true,
133            r#"{"jsonrpc":"2.0","id":1,"result":null}"#,
134            started_at + Duration::from_millis(50),
135        ),
136        Some(Duration::from_millis(50))
137    );
138}
139
140#[test]
141fn test_rpc_request_tracker_decodes_ids_and_times_cancelled_requests() {
142    let mut tracker = TestRpcRequestTracker::new();
143    let started_at = Instant::now();
144
145    tracker.observe(
146        true,
147        r#"{"jsonrpc":"2.0","id":"foo\u002fbar","method":"workspace/configuration"}"#,
148        started_at,
149    );
150    assert_eq!(
151        tracker.observe(
152            false,
153            r#"{"jsonrpc":"2.0","id":"foo/bar","result":[]}"#,
154            started_at + Duration::from_millis(25),
155        ),
156        Some(Duration::from_millis(25))
157    );
158
159    tracker.observe(
160        false,
161        r#"{"jsonrpc":"2.0","id":7,"method":"textDocument/hover"}"#,
162        started_at,
163    );
164    tracker.observe(
165        false,
166        r#"{"jsonrpc":"2.0","method":"$/cancelRequest","params":{"id":7}}"#,
167        started_at + Duration::from_millis(1),
168    );
169    assert_eq!(tracker.pending_request_count(), 1);
170    assert_eq!(
171        tracker.observe(
172            true,
173            r#"{"jsonrpc":"2.0","id":7,"error":{"code":-32800,"message":"Request was cancelled"}}"#,
174            started_at + Duration::from_millis(10),
175        ),
176        Some(Duration::from_millis(10))
177    );
178    assert_eq!(tracker.pending_request_count(), 0);
179}
180
181#[test]
182fn test_rpc_request_tracker_bounds_unanswered_requests() {
183    let mut tracker = TestRpcRequestTracker::new();
184    let started_at = Instant::now();
185    let max_pending_requests = TestRpcRequestTracker::max_pending_requests();
186
187    for id in 0..=max_pending_requests {
188        tracker.observe(
189            false,
190            &format!(r#"{{"jsonrpc":"2.0","id":{id},"method":"textDocument/hover"}}"#),
191            started_at + Duration::from_nanos(id as u64),
192        );
193    }
194
195    assert_eq!(tracker.pending_request_count(), max_pending_requests);
196    assert_eq!(
197        tracker.observe(
198            true,
199            r#"{"jsonrpc":"2.0","id":0,"result":null}"#,
200            started_at + Duration::from_secs(1),
201        ),
202        None
203    );
204    assert!(
205        tracker
206            .observe(
207                true,
208                r#"{"jsonrpc":"2.0","id":1,"result":null}"#,
209                started_at + Duration::from_secs(1),
210            )
211            .is_some()
212    );
213}
214
215#[test]
216fn test_rpc_log_duration_proto_roundtrip() {
217    let log_type = LanguageServerLogType::Rpc {
218        received: true,
219        elapsed: Some(Duration::from_micros(1234)),
220    };
221
222    assert_eq!(
223        LanguageServerLogType::from_proto(log_type.to_proto()),
224        log_type
225    );
226}
227
228#[test]
229fn test_glob_literal_prefix() {
230    assert_eq!(glob_literal_prefix(Path::new("**/*.js")), Path::new(""));
231    assert_eq!(
232        glob_literal_prefix(Path::new("node_modules/**/*.js")),
233        Path::new("node_modules")
234    );
235    assert_eq!(
236        glob_literal_prefix(Path::new("foo/{bar,baz}.js")),
237        Path::new("foo")
238    );
239    assert_eq!(
240        glob_literal_prefix(Path::new("foo/bar/baz.js")),
241        Path::new("foo/bar/baz.js")
242    );
243
244    #[cfg(target_os = "windows")]
245    {
246        assert_eq!(glob_literal_prefix(Path::new("**\\*.js")), Path::new(""));
247        assert_eq!(
248            glob_literal_prefix(Path::new("node_modules\\**/*.js")),
249            Path::new("node_modules")
250        );
251        assert_eq!(
252            glob_literal_prefix(Path::new("foo/{bar,baz}.js")),
253            Path::new("foo")
254        );
255        assert_eq!(
256            glob_literal_prefix(Path::new("foo\\bar\\baz.js")),
257            Path::new("foo/bar/baz.js")
258        );
259    }
260}
261
262#[test]
263fn test_multi_len_chars_normalization() {
264    let mut label = CodeLabel::new(
265        "myElˇ (parameter) myElˇ: {\n    foo: string;\n}".to_string(),
266        0..6,
267        vec![(0..6, HighlightId::new(1))],
268    );
269    ensure_uniform_list_compatible_label(&mut label);
270    assert_eq!(
271        label,
272        CodeLabel::new(
273            "myElˇ (parameter) myElˇ: { foo: string; }".to_string(),
274            0..6,
275            vec![(0..6, HighlightId::new(1))],
276        )
277    );
278}
279
280#[test]
281fn test_trailing_newline_in_completion_documentation() {
282    let doc =
283        lsp::Documentation::String("Inappropriate argument value (of correct type).\n".to_string());
284    let completion_doc: CompletionDocumentation = doc.into();
285    assert!(
286        matches!(completion_doc, CompletionDocumentation::SingleLine(s) if s == "Inappropriate argument value (of correct type).")
287    );
288
289    let doc = lsp::Documentation::String("  some value  \n".to_string());
290    let completion_doc: CompletionDocumentation = doc.into();
291    assert!(matches!(
292        completion_doc,
293        CompletionDocumentation::SingleLine(s) if s == "some value"
294    ));
295}
296
297#[gpui::test]
298async fn test_user_initialization_options_override_adapter_arrays(cx: &mut TestAppContext) {
299    init_test(cx);
300
301    let user_settings = serde_json::json!({
302        "lsp": {
303            "the-fake-language-server": {
304                "initialization_options": {
305                    "preview": {
306                        "background": {
307                            "enabled": true,
308                            "args": ["--data-plane-host=127.0.0.1:23635", "--invert-colors=never"],
309                        },
310                    },
311                    "plugins": ["user-plugin"],
312                    "userOnly": ["user"],
313                },
314            },
315        },
316    });
317
318    let fs = FakeFs::new(cx.executor());
319    fs.insert_tree(
320        path!("/the-root"),
321        json!({
322            ".zed": {
323                "settings.json": user_settings.to_string(),
324            },
325            "main.rs": "fn main() {}",
326        }),
327    )
328    .await;
329
330    let project = Project::test(fs, [path!("/the-root").as_ref()], cx).await;
331    let language_registry = project.read_with(cx, |project, _| project.languages().clone());
332    language_registry.add(rust_lang());
333
334    let sent_initialization_options = Arc::new(Mutex::new(None));
335    let mut fake_servers = language_registry.register_fake_lsp(
336        "Rust",
337        FakeLspAdapter {
338            name: "the-fake-language-server",
339            initialization_options: Some(json!({
340                "preview": {
341                    "background": {
342                        "args": ["--data-plane-host=127.0.0.1:23635", "--invert-colors=never"],
343                        "partialRendering": true,
344                    },
345                },
346                "plugins": ["default-plugin", "user-plugin"],
347                "adapterOnly": [1, 2],
348            })),
349            initializer: Some(Box::new({
350                let sent_initialization_options = sent_initialization_options.clone();
351                move |fake_server| {
352                    let sent_initialization_options = sent_initialization_options.clone();
353                    fake_server.set_request_handler::<lsp::request::Initialize, _, _>(
354                        move |params, _| {
355                            *sent_initialization_options.lock() = params.initialization_options;
356                            async move { Ok(lsp::InitializeResult::default()) }
357                        },
358                    );
359                }
360            })),
361            ..FakeLspAdapter::default()
362        },
363    );
364    cx.run_until_parked();
365
366    project
367        .update(cx, |project, cx| {
368            project.open_local_buffer_with_lsp(path!("/the-root/main.rs"), cx)
369        })
370        .await
371        .unwrap();
372    fake_servers.next().await.unwrap();
373    cx.run_until_parked();
374
375    assert_eq!(
376        sent_initialization_options.lock().take(),
377        Some(json!({
378            "preview": {
379                "background": {
380                    "enabled": true,
381                    "args": ["--data-plane-host=127.0.0.1:23635", "--invert-colors=never"],
382                    "partialRendering": true,
383                },
384            },
385            "plugins": ["user-plugin"],
386            "adapterOnly": [1, 2],
387            "userOnly": ["user"],
388        })),
389    );
390}
391
Served at tenant.openagents/omega Member data and write actions are omitted.