Skip to main content

gen_lsp_types/
lib.rs

1mod generated;
2pub mod json_rpc;
3
4pub use generated::*;
5
6/// # Tests for compilation success/failure.
7///
8/// ## Should not leak internal symbols
9///
10/// ```compile_fail
11/// use gen_lsp_types::generated;
12/// ```
13///
14/// ```compile_fail
15/// use gen_lsp_types::deserialize_some;
16/// ```
17///
18/// ```compile_fail
19/// use gen_lsp_types::SemanticToken::deserialize_tokens;
20/// ```
21///
22/// ```
23/// use gen_lsp_types::SemanticToken;
24/// ```
25///
26/// ```compile_fail
27/// use gen_lsp_types::json_rpc::deserialize_some;
28/// ```
29///
30/// ## Should have newtype Lsp(Request|Notification)Method semantics
31///
32/// ```compile_fail
33/// use gen_lsp_types::LspNotificationMethod;
34/// let foo: &str = LspNotificationMethod::CancelRequest;
35/// ```
36mod compiletest {}
37
38/// Tests for default features.
39#[cfg(test)]
40#[cfg(all(not(feature = "url"), not(feature = "fluent-uri")))]
41mod test {
42    #![allow(deprecated)]
43    use std::{
44        borrow::Cow,
45        collections::{HashMap, HashSet},
46    };
47
48    use serde_json::json;
49
50    use crate::*;
51
52    #[test]
53    fn nullable_field() {
54        let tdro = TextDocumentRegistrationOptions {
55            document_selector: None,
56        };
57        let tdro_str = serde_json::to_string(&tdro).unwrap();
58        assert_eq!(tdro_str, r#"{"documentSelector":null}"#);
59
60        let tdro = serde_json::from_str::<TextDocumentRegistrationOptions>(&tdro_str).unwrap();
61        assert_eq!(
62            tdro,
63            TextDocumentRegistrationOptions {
64                document_selector: None
65            }
66        );
67
68        // Missing documentSelector. Be liberal on deserialization, even though this is technically a
69        // noncompliant representation.
70        assert_eq!(tdro, serde_json::from_str("{}").unwrap());
71    }
72
73    #[test]
74    fn nullable_field_default() {
75        let ip = InitializeParams::default();
76
77        let ip_str = serde_json::to_string(&ip).unwrap();
78
79        assert_eq!(
80            ip_str,
81            r#"{"processId":null,"rootUri":null,"capabilities":{}}"#
82        );
83
84        assert_eq!(
85            InitializeParams::default(),
86            serde_json::from_str(&ip_str).unwrap()
87        );
88
89        // Missing processId. Be liberal on deserialization, even though this is technically a
90        // noncompliant representation.
91        let bad_ip_str = r#"{"rootUri":null,"capabilities":{}}"#;
92        assert_eq!(
93            InitializeParams::default(),
94            serde_json::from_str(bad_ip_str).unwrap()
95        );
96    }
97
98    #[test]
99    fn optional_field() {
100        let cp = ColorPresentation {
101            label: "Label".to_string(),
102            text_edit: None,
103            ..Default::default()
104        };
105        let cp_str = serde_json::to_string(&cp).unwrap();
106        assert_eq!(cp_str, r#"{"label":"Label"}"#);
107
108        let cp = serde_json::from_str::<ColorPresentation>(&cp_str).unwrap();
109        assert_eq!(
110            cp,
111            ColorPresentation {
112                label: "Label".to_string(),
113                text_edit: None,
114                ..Default::default()
115            }
116        );
117        // While technically illegal in the spec, this library chooses to be more liberal with
118        // DEserialization, in order to be tolerant of slightly spec-noncompliant communicators.
119        assert_eq!(
120            serde_json::from_str::<ColorPresentation>(r#"{"label":"Label","textEdit":null}"#)
121                .unwrap(),
122            cp
123        );
124    }
125
126    #[test]
127    fn optional_nullable_field() {
128        let wfip = WorkspaceFoldersInitializeParams {
129            workspace_folders: None,
130        };
131
132        let wfip_str = serde_json::to_string(&wfip).unwrap();
133
134        assert_eq!(wfip_str, r"{}");
135
136        assert_eq!(
137            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
138            wfip
139        );
140
141        let wfip = WorkspaceFoldersInitializeParams {
142            workspace_folders: Some(crate::WorkspaceFolders::Null),
143        };
144        let wfip_str = serde_json::to_string(&wfip).unwrap();
145        assert_eq!(wfip_str, r#"{"workspaceFolders":null}"#);
146        assert_eq!(
147            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
148            wfip
149        );
150
151        let wfip = WorkspaceFoldersInitializeParams {
152            workspace_folders: Some(crate::WorkspaceFolders::WorkspaceFolderList(Vec::new())),
153        };
154        let wfip_str = serde_json::to_string(&wfip).unwrap();
155        assert_eq!(wfip_str, r#"{"workspaceFolders":[]}"#);
156        assert_eq!(
157            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
158            wfip
159        );
160    }
161
162    #[test]
163    fn derives() {
164        let pos = Position::default();
165        let table = HashMap::from([(pos, 123)]);
166        assert_eq!(table.get(&pos), Some(&123));
167
168        let range = Range::default();
169        let table = HashSet::from([range]);
170        assert!(table.contains(&range));
171
172        let doc_sym = DocumentSymbol {
173            kind: crate::SymbolKind::Function,
174            name: String::default(),
175            detail: Option::default(),
176            tags: Option::default(),
177            deprecated: Option::default(),
178            range: Range::default(),
179            selection_range: Range::default(),
180            children: Option::default(),
181        };
182        let table = HashSet::from([doc_sym.clone()]);
183        assert!(table.contains(&doc_sym));
184
185        // From
186        let wfip = WorkspaceFoldersInitializeParams {
187            workspace_folders: Some(Vec::new().into()),
188        };
189        let wfip_str = serde_json::to_string(&wfip).unwrap();
190        assert_eq!(wfip_str, r#"{"workspaceFolders":[]}"#);
191        assert_eq!(
192            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
193            wfip
194        );
195        assert_eq!(WorkspaceFolders::Null, ().into());
196        let wfip = WorkspaceFoldersInitializeParams {
197            workspace_folders: Some(().into()),
198        };
199        let wfip_str = serde_json::to_string(&wfip).unwrap();
200        assert_eq!(wfip_str, r#"{"workspaceFolders":null}"#);
201        assert_eq!(
202            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
203            wfip
204        );
205        let wfsc = WorkspaceFoldersServerCapabilities {
206            change_notifications: Some("some-noti-id".into()),
207            ..Default::default()
208        };
209        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
210        assert_eq!(wfsc_str, r#"{"changeNotifications":"some-noti-id"}"#);
211        let wfsc = WorkspaceFoldersServerCapabilities {
212            change_notifications: Some(String::from("some-noti-id").into()),
213            ..Default::default()
214        };
215        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
216        assert_eq!(wfsc_str, r#"{"changeNotifications":"some-noti-id"}"#);
217        let wfsc = WorkspaceFoldersServerCapabilities {
218            change_notifications: Some(false.into()),
219            ..Default::default()
220        };
221        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
222        assert_eq!(wfsc_str, r#"{"changeNotifications":false}"#);
223        let wfsc = WorkspaceFoldersServerCapabilities {
224            change_notifications: Some('f'.into()),
225            ..Default::default()
226        };
227        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
228        assert_eq!(wfsc_str, r#"{"changeNotifications":"f"}"#);
229        let boxed_str: Box<str> = Box::from("foo");
230        let wfsc = WorkspaceFoldersServerCapabilities {
231            change_notifications: Some(boxed_str.into()),
232            ..Default::default()
233        };
234        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
235        assert_eq!(wfsc_str, r#"{"changeNotifications":"foo"}"#);
236    }
237
238    #[test]
239    fn special_impls() {
240        let pos = Position {
241            line: 2,
242            character: 0,
243        };
244        let pos2 = Position {
245            line: 1,
246            character: 9,
247        };
248        assert!(pos2 < pos);
249
250        // Copy
251        let pos3 = pos2;
252        assert_eq!(pos3, pos2);
253
254        let range = Range {
255            start: pos2,
256            end: pos,
257        };
258        // Copy
259        let range2 = range;
260        assert_eq!(range2, range);
261
262        let range3 = Range {
263            start: Position::default(),
264            end: Position {
265                line: 999,
266                character: 999,
267            },
268        };
269        assert!(range3 < range2);
270
271        let range4 = Range::default();
272        assert!(range4 < range3);
273
274        let method: String = LspRequestMethod::TextDocumentOnTypeFormatting.into();
275        assert_eq!(method, "textDocument/onTypeFormatting");
276        let method = LspRequestMethod::Shutdown.to_string();
277        assert_eq!(method, "shutdown");
278        let method = LspRequestMethod::Custom("foo").to_string();
279        assert_eq!(method, "foo");
280        let method = LspNotificationMethod::Custom("foo").to_string();
281        assert_eq!(method, "foo");
282        let method = LspNotificationMethod::CancelRequest.to_string();
283        assert_eq!(method, "$/cancelRequest");
284        let method = LspNotificationMethod::WorkspaceDidChangeWatchedFiles.to_string();
285        assert_eq!(method, "workspace/didChangeWatchedFiles");
286
287        let wk = WatchKind::Delete;
288        let wk2 = WatchKind::Create;
289        assert_eq!(wk | wk2, WatchKind::Custom(5));
290        assert_eq!(wk | wk2 | WatchKind::Change, WatchKind::Custom(7));
291        assert_eq!((wk | wk2) & wk, wk);
292        assert_eq!(wk | wk, wk);
293        assert_eq!((wk | wk2 | WatchKind::Change) & (wk | wk2), wk | wk2);
294        assert_eq!(WatchKind::Delete ^ WatchKind::Delete, WatchKind::Custom(0));
295        assert_eq!(WatchKind::Custom(0) ^ WatchKind::Create, WatchKind::Create);
296        let mut wk = WatchKind::Custom(0);
297        wk |= WatchKind::Delete;
298        assert_eq!(wk, WatchKind::Delete);
299        wk &= WatchKind::Create;
300        assert_eq!(wk, WatchKind::Custom(0));
301        wk ^= WatchKind::Delete;
302        assert_eq!(wk, WatchKind::Delete);
303    }
304
305    #[test]
306    fn string_literal_field() {
307        let wdpe = WorkDoneProgressEnd {
308            message: Some("change da world. my final message. goodbye".to_string()),
309        };
310
311        let ser = serde_json::to_string(&wdpe).unwrap();
312        assert_eq!(
313            ser,
314            r#"{"message":"change da world. my final message. goodbye","kind":"end"}"#
315        );
316
317        let deser = serde_json::from_str::<WorkDoneProgressEnd>(&ser).unwrap();
318        assert_eq!(deser, wdpe);
319
320        let fake_ser = r#"{"message":"change da world. my final message. goodbye","kind":"begin"}"#;
321        assert!(serde_json::from_str::<WorkDoneProgressEnd>(fake_ser).is_err());
322
323        let doc_change = CreateFile {
324            uri: "file:///foo.txt".to_string().into(),
325            options: None,
326            annotation_id: None,
327        };
328        let ser = serde_json::to_string(&doc_change).unwrap();
329        assert_eq!(ser, r#"{"uri":"file:///foo.txt","kind":"create"}"#);
330
331        let ser = r#"{"uri":"file:///foo.txt","kind":"create"}"#;
332        let deser = serde_json::from_str::<DocumentChange>(ser).unwrap();
333        assert_eq!(deser, doc_change.into());
334        let ser = r#"{"uri":"file:///foo.txt","kind":"delete","annotationId":"foo"}"#;
335        let deser = serde_json::from_str::<DocumentChange>(ser).unwrap();
336        assert_eq!(
337            deser,
338            DocumentChange::DeleteFile(DeleteFile {
339                uri: crate::Uri("file:///foo.txt".to_string()),
340                options: None,
341                annotation_id: Some(String::from("foo"))
342            })
343        );
344        let bad_ser = r#"{"uri":"file:///foo.txt","kind":"delet"}"#;
345        assert!(serde_json::from_str::<DocumentChange>(bad_ser).is_err());
346    }
347
348    #[test]
349    fn string_enum() {
350        let frk = FoldingRangeKind::Comment;
351        let ser = serde_json::to_string(&frk).unwrap();
352
353        assert_eq!(ser, "\"comment\"");
354        assert_eq!(
355            serde_json::from_str::<FoldingRangeKind>(&ser).unwrap(),
356            FoldingRangeKind::Comment
357        );
358
359        let frk = FoldingRangeKind::Custom(Cow::Borrowed("foo"));
360        let ser = serde_json::to_string(&frk).unwrap();
361
362        assert_eq!(ser, "\"foo\"");
363        assert_eq!(
364            serde_json::from_str::<FoldingRangeKind>(&ser).unwrap(),
365            FoldingRangeKind::Custom(Cow::Borrowed("foo"))
366        );
367        assert_eq!("foo", frk.as_str());
368
369        let mk = MarkupKind::PlainText;
370        assert_eq!("\"plaintext\"", serde_json::to_string(&mk).unwrap());
371        assert!(serde_json::from_str::<MarkupKind>("foo").is_err());
372        assert_eq!("plaintext", mk.as_str());
373    }
374
375    #[test]
376    fn str_enum_into() {
377        const CONSTANT: &str = "my_custom_variant";
378        let _parsed: LspNotificationMethod = CONSTANT.into();
379    }
380
381    #[test]
382    fn int_enum() {
383        let sk = SymbolKind::Namespace;
384        let ser = serde_json::to_string(&sk).unwrap();
385
386        assert_eq!(ser, "3");
387        assert_eq!(
388            serde_json::from_str::<SymbolKind>(&ser).unwrap(),
389            SymbolKind::Namespace
390        );
391        assert!(serde_json::from_str::<SymbolKind>("299").is_err());
392
393        let wk = WatchKind::Custom(123);
394        let ser = serde_json::to_string(&wk).unwrap();
395
396        assert_eq!(ser, "123");
397        assert_eq!(wk, serde_json::from_str::<WatchKind>(&ser).unwrap());
398        assert_eq!(
399            WatchKind::Change,
400            serde_json::from_str::<WatchKind>("2").unwrap()
401        );
402    }
403
404    #[test]
405    fn request_object_from_request() {
406        let params = TypeDefinitionParams {
407            work_done_progress_params: crate::WorkDoneProgressParams {
408                work_done_token: None,
409            },
410            partial_result_params: crate::PartialResultParams {
411                partial_result_token: None,
412            },
413            text_document_position_params: crate::TextDocumentPositionParams {
414                text_document: crate::TextDocumentIdentifier { uri: "foo".into() },
415                position: Position::default(),
416            },
417        };
418        let req = json_rpc::RequestObject::from_request::<TypeDefinitionRequest>(
419            json_rpc::Id::Number(123),
420            params.clone(),
421        );
422
423        let ser = serde_json::to_string(&req).unwrap();
424
425        assert_eq!(
426            ser,
427            r#"{"jsonrpc":"2.0","id":123,"method":"textDocument/typeDefinition","params":{"position":{"character":0,"line":0},"textDocument":{"uri":"foo"}}}"#
428        );
429        assert_eq!(req.id(), Some(&json_rpc::Id::Number(123)));
430        assert_eq!(req.method(), "textDocument/typeDefinition");
431        assert_eq!(req.params(), Some(&json!(params)));
432    }
433
434    #[test]
435    fn request_object_from_request_no_params() {
436        let req = json_rpc::RequestObject::from_request::<WorkspaceFoldersRequest>(
437            json_rpc::Id::String("foo".into()),
438            (),
439        );
440
441        let ser = serde_json::to_string(&req).unwrap();
442
443        assert_eq!(
444            ser,
445            r#"{"jsonrpc":"2.0","id":"foo","method":"workspace/workspaceFolders"}"#
446        );
447    }
448
449    #[test]
450    fn request_object_from_notification() {
451        let noti = json_rpc::RequestObject::from_notification::<InitializedNotification>(
452            InitializedParams {},
453        );
454
455        let ser = serde_json::to_string(&noti).unwrap();
456
457        assert_eq!(
458            ser,
459            r#"{"jsonrpc":"2.0","method":"initialized","params":{}}"#
460        );
461        assert_eq!(noti.id(), None);
462        assert_eq!(noti.method(), "initialized");
463        assert_eq!(noti.params(), Some(&json!(InitializedParams {})));
464    }
465
466    #[test]
467    fn request_object_from_notification_no_params() {
468        let noti = json_rpc::RequestObject::from_notification::<ExitNotification>(());
469
470        let ser = serde_json::to_string(&noti).unwrap();
471
472        assert_eq!(ser, r#"{"jsonrpc":"2.0","method":"exit"}"#);
473    }
474
475    #[test]
476    fn response_object_from_success() {
477        let id = json_rpc::Id::Number(123);
478
479        let res = json_rpc::ResponseObject::from_success::<ImplementationRequest>(
480            id.clone(),
481            Some(ImplementationResponse::DefinitionLinkList(Vec::new())),
482        );
483
484        let ser = serde_json::to_string(&res).unwrap();
485        assert_eq!(r#"{"jsonrpc":"2.0","result":[],"id":123}"#, &ser);
486        assert_eq!(res, serde_json::from_str(&ser).unwrap());
487
488        let res = json_rpc::ResponseObject::from_success::<ImplementationRequest>(id.clone(), None);
489
490        let ser = serde_json::to_string(&res).unwrap();
491        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
492        assert_eq!(res, serde_json::from_str(&ser).unwrap());
493
494        let res = json_rpc::ResponseObject::from_success::<ImplementationRequest>(id.clone(), None);
495
496        let ser = serde_json::to_string(&res).unwrap();
497        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
498        assert_eq!(res, serde_json::from_str(&ser).unwrap());
499
500        let res = json_rpc::ResponseObject::from_success::<ShowMessageRequest>(id.clone(), None);
501
502        let ser = serde_json::to_string(&res).unwrap();
503        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
504        assert_eq!(res, serde_json::from_str(&ser).unwrap());
505
506        let res = json_rpc::ResponseObject::from_success::<ShowMessageRequest>(
507            id.clone(),
508            Some(crate::MessageActionItem {
509                title: "foo".into(),
510            }),
511        );
512
513        let ser = serde_json::to_string(&res).unwrap();
514        assert_eq!(
515            r#"{"jsonrpc":"2.0","result":{"title":"foo"},"id":123}"#,
516            &ser
517        );
518        assert_eq!(res, serde_json::from_str(&ser).unwrap());
519
520        let res = json_rpc::ResponseObject::from_success::<CodeLensRefreshRequest>(id, ());
521
522        let ser = serde_json::to_string(&res).unwrap();
523        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
524        assert_eq!(res, serde_json::from_str(&ser).unwrap());
525    }
526
527    #[test]
528    fn response_object_from_error() {
529        let id = json_rpc::Id::Null;
530        let res = json_rpc::ResponseObject::from_error(
531            id,
532            json_rpc::Error {
533                code: crate::ErrorCodes::ParseError,
534                message: "invalid format".into(),
535                data: None,
536            },
537        );
538
539        let ser = serde_json::to_string(&res).unwrap();
540        assert_eq!(
541            r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"invalid format"},"id":null}"#,
542            &ser
543        );
544
545        let id = json_rpc::Id::String("foo-req".into());
546        let res = json_rpc::ResponseObject::from_error(
547            id,
548            json_rpc::Error {
549                code: crate::ErrorCodes::Custom(-32803),
550                message: "failed to foo the bar".into(),
551                data: Some(json!("hi")),
552            },
553        );
554
555        let ser = serde_json::to_string(&res).unwrap();
556        assert_eq!(
557            r#"{"jsonrpc":"2.0","error":{"code":-32803,"message":"failed to foo the bar","data":"hi"},"id":"foo-req"}"#,
558            &ser
559        );
560
561        let id = json_rpc::Id::String("foo-req".into());
562        let res = json_rpc::ResponseObject::from_error(
563            id,
564            json_rpc::Error {
565                code: crate::ErrorCodes::Custom(crate::LspErrorCodes::ContentModified.into()),
566                message: "failed to foo the bar".into(),
567                data: Some(json!("hi")),
568            },
569        );
570
571        let ser = serde_json::to_string(&res).unwrap();
572        assert_eq!(
573            r#"{"jsonrpc":"2.0","error":{"code":-32801,"message":"failed to foo the bar","data":"hi"},"id":"foo-req"}"#,
574            &ser
575        );
576    }
577
578    #[test]
579    fn structures_new_constructor() {
580        assert_eq!(
581            Position::new(1, 2),
582            Position {
583                line: 1,
584                character: 2
585            }
586        );
587        assert_eq!(
588            Range::new(Position::new(0, 0), Position::new(0, 99)),
589            Range {
590                start: Position::default(),
591                end: Position {
592                    line: 0,
593                    character: 99
594                }
595            }
596        );
597
598        assert_eq!(
599            Location::new(Uri("foo".into()), Range::default()),
600            Location {
601                uri: Uri("foo".into()),
602                range: Range::default()
603            }
604        );
605
606        // For some structures, the new() constructor does not help as much.
607        assert_eq!(
608            Diagnostic::new(
609                Range::default(),
610                Some(DiagnosticSeverity::Warning),
611                None,
612                None,
613                None,
614                "bad".into(),
615                None,
616                None,
617                None
618            ),
619            Diagnostic {
620                range: Range::default(),
621                message: "bad".into(),
622                severity: Some(DiagnosticSeverity::Warning),
623                ..Default::default()
624            }
625        );
626    }
627
628    #[test]
629    fn custom_request_object_methods() {
630        struct ParentModule;
631        impl Request for ParentModule {
632            type Params = ();
633            type Result = Option<DefinitionResponse>;
634            const METHOD: LspRequestMethod<'_> =
635                LspRequestMethod::Custom("experimental/parentModule");
636            const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer;
637        }
638
639        let req =
640            json_rpc::RequestObject::from_request::<ParentModule>(json_rpc::Id::Number(123), ());
641        assert_eq!(
642            r#"{"jsonrpc":"2.0","id":123,"method":"experimental/parentModule"}"#,
643            serde_json::to_string(&req).unwrap()
644        );
645
646        struct ServerStatusNotification;
647        impl Notification for ServerStatusNotification {
648            type Params = ();
649            const METHOD: LspNotificationMethod<'_> =
650                LspNotificationMethod::new("experimental/serverStatus");
651            const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer;
652        }
653
654        let noti = json_rpc::RequestObject::from_notification::<ServerStatusNotification>(());
655        assert_eq!(
656            r#"{"jsonrpc":"2.0","method":"experimental/serverStatus"}"#,
657            serde_json::to_string(&noti).unwrap()
658        );
659
660        // Compilation checks
661        let method: &'static str = <ParentModule as Request>::METHOD.as_str();
662        assert_eq!(method, "experimental/parentModule");
663        let method = LspRequestMethod::new("asdf");
664        let method_str: &'static str = method.as_str();
665        assert_eq!(method_str, "asdf");
666        let method = LspRequestMethod::Custom("asdf");
667        let method_str: &'static str = method.as_str();
668        assert_eq!(method_str, "asdf");
669        let owned = String::from("workspace/didCreateFiles");
670        let method: LspNotificationMethod<'_> = owned.as_str().into();
671        assert_eq!(method, LspNotificationMethod::WorkspaceDidCreateFiles);
672        let owned = String::from("foo");
673        let method: LspNotificationMethod<'_> = owned.as_str().into();
674        assert_eq!(method, LspNotificationMethod::new("foo"));
675        // Copy
676        let method1 = LspRequestMethod::TextDocumentCompletion;
677        let method2 = method1;
678        assert_eq!(method1, method2);
679        // We can `match` on local vars without extra allocations! Yay!
680        #[allow(clippy::match_same_arms)]
681        match method {
682            LspNotificationMethod::TextDocumentWillSave => {}
683            LspNotificationMethod::Custom("foo") => {}
684            LspNotificationMethod::Custom(_) => {}
685            _ => {}
686        }
687    }
688
689    #[test]
690    fn request_with_partial_results() {
691        fn foo<R: RequestWithPartialResults>(
692            _params: R::Params,
693            _partial_result: R::PartialResult,
694        ) {
695            // Do nothing!
696        }
697
698        foo::<DocumentSymbolRequest>(
699            DocumentSymbolParams {
700                text_document: TextDocumentIdentifier { uri: "".into() },
701                work_done_progress_params: WorkDoneProgressParams {
702                    work_done_token: None,
703                },
704                partial_result_params: PartialResultParams {
705                    partial_result_token: None,
706                },
707            },
708            DocumentSymbolPartialResponse::SymbolInformationList(vec![SymbolInformation {
709                deprecated: None,
710                location: Location {
711                    uri: "".into(),
712                    range: Range::default(),
713                },
714                base_symbol_information: BaseSymbolInformation {
715                    name: String::new(),
716                    kind: SymbolKind::File,
717                    tags: None,
718                    container_name: None,
719                },
720            }]),
721        );
722
723        let partial_result = DocumentSymbolPartialResponse::DocumentSymbolList(Vec::new());
724        let _ = match partial_result {
725            DocumentSymbolPartialResponse::SymbolInformationList(_) => 1,
726            DocumentSymbolPartialResponse::DocumentSymbolList(_) => 2,
727            // No `null` branch
728        };
729    }
730
731    #[test]
732    #[allow(clippy::similar_names)]
733    fn semantic_tokens() {
734        let ste = SemanticTokensEdit {
735            start: 0,
736            delete_count: 1,
737            data: None,
738        };
739        let ste_ser = r#"{"start":0,"deleteCount":1}"#;
740        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
741        assert_eq!(ste, serde_json::from_str(ste_ser).unwrap());
742
743        let ste = SemanticTokensEdit {
744            start: 0,
745            delete_count: 1,
746            data: None,
747        };
748        let ste_ser_fake = r#"{"start":0,"deleteCount":1,"data":null}"#;
749        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
750        // Be permissive on technically incorrect deserialization.
751        assert_eq!(ste, serde_json::from_str(ste_ser_fake).unwrap());
752
753        let ste = SemanticTokensEdit {
754            start: 0,
755            delete_count: 1,
756            data: Some(vec![
757                SemanticToken {
758                    delta_line: 2,
759                    delta_start: 5,
760                    length: 3,
761                    token_type: 0,
762                    token_modifiers_bitset: 3,
763                },
764                SemanticToken {
765                    delta_line: 0,
766                    delta_start: 5,
767                    length: 4,
768                    token_type: 1,
769                    token_modifiers_bitset: 0,
770                },
771            ]),
772        };
773        let ste_ser = r#"{"start":0,"deleteCount":1,"data":[2,5,3,0,3,0,5,4,1,0]}"#;
774        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
775        assert_eq!(ste, serde_json::from_str(ste_ser).unwrap());
776
777        let ste = SemanticTokensEdit {
778            start: 0,
779            delete_count: 1,
780            data: Some(Vec::new()),
781        };
782        let ste_ser = r#"{"start":0,"deleteCount":1,"data":[]}"#;
783        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
784        assert_eq!(ste, serde_json::from_str(ste_ser).unwrap());
785
786        let st = SemanticTokens {
787            result_id: None,
788            data: Vec::default(),
789        };
790        let st_ser = r#"{"data":[]}"#;
791        assert_eq!(serde_json::to_string(&st).unwrap(), st_ser);
792        assert_eq!(st, serde_json::from_str(st_ser).unwrap());
793
794        let st = SemanticTokens {
795            result_id: None,
796            data: vec![
797                SemanticToken {
798                    delta_line: 2,
799                    delta_start: 5,
800                    length: 3,
801                    token_type: 0,
802                    token_modifiers_bitset: 3,
803                },
804                SemanticToken {
805                    delta_line: 0,
806                    delta_start: 5,
807                    length: 4,
808                    token_type: 1,
809                    token_modifiers_bitset: 0,
810                },
811            ],
812        };
813        let st_ser = r#"{"data":[2,5,3,0,3,0,5,4,1,0]}"#;
814        assert_eq!(serde_json::to_string(&st).unwrap(), st_ser);
815        assert_eq!(st, serde_json::from_str(st_ser).unwrap());
816
817        let stpr = SemanticTokensPartialResult {
818            data: vec![
819                SemanticToken {
820                    delta_line: 2,
821                    delta_start: 5,
822                    length: 3,
823                    token_type: 0,
824                    token_modifiers_bitset: 3,
825                },
826                SemanticToken {
827                    delta_line: 0,
828                    delta_start: 5,
829                    length: 4,
830                    token_type: 1,
831                    token_modifiers_bitset: 0,
832                },
833            ],
834        };
835        let stpr_ser = r#"{"data":[2,5,3,0,3,0,5,4,1,0]}"#;
836        assert_eq!(serde_json::to_string(&stpr).unwrap(), stpr_ser);
837        assert_eq!(stpr, serde_json::from_str(stpr_ser).unwrap());
838
839        let stpr = SemanticTokensPartialResult {
840            data: Vec::default(),
841        };
842        let stpr_ser = r#"{"data":[]}"#;
843        assert_eq!(serde_json::to_string(&stpr).unwrap(), stpr_ser);
844        assert_eq!(stpr, serde_json::from_str(stpr_ser).unwrap());
845    }
846
847    #[test]
848    fn request_macro() {
849        let req = json_rpc::RequestObject::from_request::<lsp_request!("workspace/workspaceFolders")>(
850            json_rpc::Id::Number(123),
851            (),
852        );
853
854        assert_eq!(
855            r#"{"jsonrpc":"2.0","id":123,"method":"workspace/workspaceFolders"}"#,
856            serde_json::to_string(&req).unwrap()
857        );
858    }
859
860    #[test]
861    fn notification_macro() {
862        let req = json_rpc::RequestObject::from_notification::<lsp_notification!("exit")>(());
863
864        assert_eq!(
865            r#"{"jsonrpc":"2.0","method":"exit"}"#,
866            serde_json::to_string(&req).unwrap()
867        );
868    }
869
870    #[test]
871    fn tuple_serialization() {
872        let pil = ParameterInformationLabel::Tuple((1, 2));
873        assert_eq!("[1,2]", serde_json::to_string(&pil).unwrap());
874        assert_eq!(pil, serde_json::from_str("[1,2]").unwrap());
875    }
876}
877
878/// Tests for the "url" feature.
879#[cfg(test)]
880#[cfg(all(feature = "url", not(feature = "fluent-uri")))]
881mod test {
882    use crate::*;
883
884    #[test]
885    fn url_feature() {
886        let url = url::Url::parse("file://tmp/foo.txt/").unwrap();
887        let tdi = TextDocumentIdentifier { uri: url };
888        let ser = r#"{"uri":"file://tmp/foo.txt/"}"#;
889
890        assert_eq!(ser, serde_json::to_string(&tdi).unwrap());
891        assert_eq!(tdi, serde_json::from_str(ser).unwrap());
892    }
893}
894
895/// Tests for the "fluent-uri" feature.
896#[cfg(test)]
897#[cfg(all(feature = "fluent-uri", not(feature = "url")))]
898mod test {
899    use crate::*;
900
901    #[test]
902    fn url_feature() {
903        let uri = fluent_uri::Uri::try_from("file://tmp/foo.txt/".to_string()).unwrap();
904        let tdi = TextDocumentIdentifier { uri };
905        let ser = r#"{"uri":"file://tmp/foo.txt/"}"#;
906
907        assert_eq!(ser, serde_json::to_string(&tdi).unwrap());
908        assert_eq!(tdi, serde_json::from_str(ser).unwrap());
909    }
910}