]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/tests/heavy_tests/main.rs
Add capabilities tests.
[rust.git] / crates / rust-analyzer / tests / heavy_tests / main.rs
1 mod support;
2
3 use std::{collections::HashMap, path::PathBuf, time::Instant};
4
5 use lsp_types::{
6     notification::DidOpenTextDocument,
7     request::{
8         CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest,
9     },
10     CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
11     DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
12     PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
13     WorkDoneProgressParams,
14 };
15 use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
16 use serde_json::json;
17 use tempfile::TempDir;
18 use test_utils::skip_slow_tests;
19
20 use crate::support::{project, Project};
21
22 const PROFILE: &str = "";
23 // const PROFILE: &'static str = "*@3>100";
24
25 #[test]
26 fn completes_items_from_standard_library() {
27     if skip_slow_tests() {
28         return;
29     }
30
31     let project_start = Instant::now();
32     let server = Project::with_fixture(
33         r#"
34 //- Cargo.toml
35 [package]
36 name = "foo"
37 version = "0.0.0"
38
39 //- src/lib.rs
40 use std::collections::Spam;
41 "#,
42     )
43     .with_sysroot(true)
44     .server();
45     server.wait_until_workspace_is_loaded();
46     eprintln!("loading took    {:?}", project_start.elapsed());
47     let completion_start = Instant::now();
48     let res = server.send_request::<Completion>(CompletionParams {
49         text_document_position: TextDocumentPositionParams::new(
50             server.doc_id("src/lib.rs"),
51             Position::new(0, 23),
52         ),
53         context: None,
54         partial_result_params: PartialResultParams::default(),
55         work_done_progress_params: WorkDoneProgressParams::default(),
56     });
57     assert!(format!("{}", res).contains("HashMap"));
58     eprintln!("completion took {:?}", completion_start.elapsed());
59 }
60
61 #[test]
62 fn test_runnables_project() {
63     if skip_slow_tests() {
64         return;
65     }
66
67     let code = r#"
68 //- foo/Cargo.toml
69 [package]
70 name = "foo"
71 version = "0.0.0"
72
73 //- foo/src/lib.rs
74 pub fn foo() {}
75
76 //- foo/tests/spam.rs
77 #[test]
78 fn test_eggs() {}
79
80 //- bar/Cargo.toml
81 [package]
82 name = "bar"
83 version = "0.0.0"
84
85 //- bar/src/main.rs
86 fn main() {}
87 "#;
88
89     let server = Project::with_fixture(code).root("foo").root("bar").server();
90
91     server.wait_until_workspace_is_loaded();
92     server.request::<Runnables>(
93         RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
94         json!([
95           {
96             "args": {
97               "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
98               "executableArgs": ["test_eggs", "--exact", "--nocapture"],
99               "workspaceRoot": server.path().join("foo")
100             },
101             "kind": "cargo",
102             "label": "test test_eggs",
103             "location": {
104               "targetRange": {
105                 "end": { "character": 17, "line": 1 },
106                 "start": { "character": 0, "line": 0 }
107               },
108               "targetSelectionRange": {
109                 "end": { "character": 12, "line": 1 },
110                 "start": { "character": 3, "line": 1 }
111               },
112               "targetUri": "file:///[..]/tests/spam.rs"
113             }
114           },
115           {
116             "args": {
117               "cargoArgs": ["check", "--package", "foo"],
118               "executableArgs": [],
119               "workspaceRoot": server.path().join("foo")
120             },
121             "kind": "cargo",
122             "label": "cargo check -p foo"
123           },
124           {
125             "args": {
126               "cargoArgs": ["test", "--package", "foo"],
127               "executableArgs": [],
128               "workspaceRoot": server.path().join("foo")
129             },
130             "kind": "cargo",
131             "label": "cargo test -p foo"
132           }
133         ]),
134     );
135 }
136
137 #[test]
138 fn test_format_document() {
139     if skip_slow_tests() {
140         return;
141     }
142
143     let server = project(
144         r#"
145 //- Cargo.toml
146 [package]
147 name = "foo"
148 version = "0.0.0"
149
150 //- src/lib.rs
151 mod bar;
152
153 fn main() {
154 }
155
156 pub use std::collections::HashMap;
157 "#,
158     );
159     server.wait_until_workspace_is_loaded();
160
161     server.request::<Formatting>(
162         DocumentFormattingParams {
163             text_document: server.doc_id("src/lib.rs"),
164             options: FormattingOptions {
165                 tab_size: 4,
166                 insert_spaces: false,
167                 insert_final_newline: None,
168                 trim_final_newlines: None,
169                 trim_trailing_whitespace: None,
170                 properties: HashMap::new(),
171             },
172             work_done_progress_params: WorkDoneProgressParams::default(),
173         },
174         json!([
175             {
176                 "newText": r#"mod bar;
177
178 fn main() {}
179
180 pub use std::collections::HashMap;
181 "#,
182                 "range": {
183                     "end": {
184                         "character": 0,
185                         "line": 7
186                     },
187                     "start": {
188                         "character": 0,
189                         "line": 0
190                     }
191                 }
192             }
193         ]),
194     );
195 }
196
197 #[test]
198 fn test_format_document_2018() {
199     if skip_slow_tests() {
200         return;
201     }
202
203     let server = project(
204         r#"
205 //- Cargo.toml
206 [package]
207 name = "foo"
208 version = "0.0.0"
209 edition = "2018"
210
211 //- src/lib.rs
212 mod bar;
213
214 async fn test() {
215 }
216
217 fn main() {
218 }
219
220 pub use std::collections::HashMap;
221 "#,
222     );
223     server.wait_until_workspace_is_loaded();
224
225     server.request::<Formatting>(
226         DocumentFormattingParams {
227             text_document: server.doc_id("src/lib.rs"),
228             options: FormattingOptions {
229                 tab_size: 4,
230                 insert_spaces: false,
231                 properties: HashMap::new(),
232                 insert_final_newline: None,
233                 trim_final_newlines: None,
234                 trim_trailing_whitespace: None,
235             },
236             work_done_progress_params: WorkDoneProgressParams::default(),
237         },
238         json!([
239             {
240                 "newText": r#"mod bar;
241
242 async fn test() {}
243
244 fn main() {}
245
246 pub use std::collections::HashMap;
247 "#,
248                 "range": {
249                     "end": {
250                         "character": 0,
251                         "line": 10
252                     },
253                     "start": {
254                         "character": 0,
255                         "line": 0
256                     }
257                 }
258             }
259         ]),
260     );
261 }
262
263 #[test]
264 fn test_missing_module_code_action() {
265     if skip_slow_tests() {
266         return;
267     }
268
269     let server = project(
270         r#"
271 //- Cargo.toml
272 [package]
273 name = "foo"
274 version = "0.0.0"
275
276 //- src/lib.rs
277 mod bar;
278
279 fn main() {}
280 "#,
281     );
282     server.wait_until_workspace_is_loaded();
283     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
284     server.request::<CodeActionRequest>(
285         CodeActionParams {
286             text_document: server.doc_id("src/lib.rs"),
287             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
288             context: empty_context(),
289             partial_result_params: PartialResultParams::default(),
290             work_done_progress_params: WorkDoneProgressParams::default(),
291         },
292         json!([{
293             "edit": {
294               "documentChanges": [
295                 {
296                   "kind": "create",
297                   "uri": "file:///[..]/src/bar.rs"
298                 }
299               ]
300             },
301             "kind": "quickfix",
302             "title": "Create module"
303         }]),
304     );
305
306     server.request::<CodeActionRequest>(
307         CodeActionParams {
308             text_document: server.doc_id("src/lib.rs"),
309             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
310             context: empty_context(),
311             partial_result_params: PartialResultParams::default(),
312             work_done_progress_params: WorkDoneProgressParams::default(),
313         },
314         json!([]),
315     );
316 }
317
318 #[test]
319 fn test_missing_module_code_action_in_json_project() {
320     if skip_slow_tests() {
321         return;
322     }
323
324     let tmp_dir = TempDir::new().unwrap();
325
326     let path = tmp_dir.path();
327
328     let project = json!({
329         "roots": [path],
330         "crates": [ {
331             "root_module": path.join("src/lib.rs"),
332             "deps": [],
333             "edition": "2015",
334             "cfg": [ "cfg_atom_1", "feature=cfg_1"],
335         } ]
336     });
337
338     let code = format!(
339         r#"
340 //- rust-project.json
341 {PROJECT}
342
343 //- src/lib.rs
344 mod bar;
345
346 fn main() {{}}
347 "#,
348         PROJECT = project.to_string(),
349     );
350
351     let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
352
353     server.wait_until_workspace_is_loaded();
354     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
355     server.request::<CodeActionRequest>(
356         CodeActionParams {
357             text_document: server.doc_id("src/lib.rs"),
358             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
359             context: empty_context(),
360             partial_result_params: PartialResultParams::default(),
361             work_done_progress_params: WorkDoneProgressParams::default(),
362         },
363         json!([{
364             "edit": {
365               "documentChanges": [
366                 {
367                   "kind": "create",
368                   "uri": "file://[..]/src/bar.rs"
369                 }
370               ]
371             },
372             "kind": "quickfix",
373             "title": "Create module"
374         }]),
375     );
376
377     server.request::<CodeActionRequest>(
378         CodeActionParams {
379             text_document: server.doc_id("src/lib.rs"),
380             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
381             context: empty_context(),
382             partial_result_params: PartialResultParams::default(),
383             work_done_progress_params: WorkDoneProgressParams::default(),
384         },
385         json!([]),
386     );
387 }
388
389 #[test]
390 fn diagnostics_dont_block_typing() {
391     if skip_slow_tests() {
392         return;
393     }
394
395     let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
396     let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
397     let server = Project::with_fixture(&format!(
398         r#"
399 //- Cargo.toml
400 [package]
401 name = "foo"
402 version = "0.0.0"
403
404 //- src/lib.rs
405 {}
406
407 {}
408
409 fn main() {{}}
410 "#,
411         librs, libs
412     ))
413     .with_sysroot(true)
414     .server();
415
416     server.wait_until_workspace_is_loaded();
417     for i in 0..10 {
418         server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
419             text_document: TextDocumentItem {
420                 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
421                 language_id: "rust".to_string(),
422                 version: 0,
423                 text: "/// Docs\nfn foo() {}".to_string(),
424             },
425         });
426     }
427     let start = std::time::Instant::now();
428     server.request::<OnEnter>(
429         TextDocumentPositionParams {
430             text_document: server.doc_id("src/m0.rs"),
431             position: Position { line: 0, character: 5 },
432         },
433         json!([{
434             "insertTextFormat": 2,
435             "newText": "\n/// $0",
436             "range": {
437             "end": { "character": 5, "line": 0 },
438             "start": { "character": 5, "line": 0 }
439             }
440         }]),
441     );
442     let elapsed = start.elapsed();
443     assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
444 }
445
446 #[test]
447 fn preserves_dos_line_endings() {
448     if skip_slow_tests() {
449         return;
450     }
451
452     let server = Project::with_fixture(
453         &"
454 //- Cargo.toml
455 [package]
456 name = \"foo\"
457 version = \"0.0.0\"
458
459 //- src/main.rs
460 /// Some Docs\r\nfn main() {}
461 ",
462     )
463     .server();
464
465     server.request::<OnEnter>(
466         TextDocumentPositionParams {
467             text_document: server.doc_id("src/main.rs"),
468             position: Position { line: 0, character: 8 },
469         },
470         json!([{
471             "insertTextFormat": 2,
472             "newText": "\r\n/// $0",
473             "range": {
474             "end": { "line": 0, "character": 8 },
475             "start": { "line": 0, "character": 8 }
476             }
477         }]),
478     );
479 }
480
481 #[test]
482 fn out_dirs_check() {
483     if skip_slow_tests() {
484         return;
485     }
486
487     let server = Project::with_fixture(
488         r###"
489 //- Cargo.toml
490 [package]
491 name = "foo"
492 version = "0.0.0"
493
494 //- build.rs
495 use std::{env, fs, path::Path};
496
497 fn main() {
498     let out_dir = env::var_os("OUT_DIR").unwrap();
499     let dest_path = Path::new(&out_dir).join("hello.rs");
500     fs::write(
501         &dest_path,
502         r#"pub fn message() -> &'static str { "Hello, World!" }"#,
503     )
504     .unwrap();
505     println!("cargo:rustc-cfg=atom_cfg");
506     println!("cargo:rustc-cfg=featlike=\"set\"");
507     println!("cargo:rerun-if-changed=build.rs");
508 }
509 //- src/main.rs
510 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
511
512 #[cfg(atom_cfg)]
513 struct A;
514 #[cfg(bad_atom_cfg)]
515 struct A;
516 #[cfg(featlike = "set")]
517 struct B;
518 #[cfg(featlike = "not_set")]
519 struct B;
520
521 fn main() {
522     let va = A;
523     let vb = B;
524     message();
525 }
526
527 fn main() { message(); }
528 "###,
529     )
530     .with_config(|config| {
531         config.cargo.load_out_dirs_from_check = true;
532     })
533     .server();
534     server.wait_until_workspace_is_loaded();
535     let res = server.send_request::<GotoDefinition>(GotoDefinitionParams {
536         text_document_position_params: TextDocumentPositionParams::new(
537             server.doc_id("src/main.rs"),
538             Position::new(14, 8),
539         ),
540         work_done_progress_params: Default::default(),
541         partial_result_params: Default::default(),
542     });
543     assert!(format!("{}", res).contains("hello.rs"));
544     server.request::<GotoTypeDefinition>(
545         GotoDefinitionParams {
546             text_document_position_params: TextDocumentPositionParams::new(
547                 server.doc_id("src/main.rs"),
548                 Position::new(12, 9),
549             ),
550             work_done_progress_params: Default::default(),
551             partial_result_params: Default::default(),
552         },
553         json!([{
554             "originSelectionRange": {
555                 "end": {
556                     "character": 10,
557                     "line": 12
558                 },
559                 "start": {
560                     "character": 8,
561                     "line": 12
562                 }
563             },
564             "targetRange": {
565                 "end": {
566                     "character": 9,
567                     "line": 3
568                 },
569                 "start": {
570                     "character": 0,
571                     "line": 2
572                 }
573             },
574             "targetSelectionRange": {
575                 "end": {
576                     "character": 8,
577                     "line": 3
578                 },
579                 "start": {
580                     "character": 7,
581                     "line": 3
582                 }
583             },
584             "targetUri": "file:///[..]src/main.rs"
585         }]),
586     );
587     server.request::<GotoTypeDefinition>(
588         GotoDefinitionParams {
589             text_document_position_params: TextDocumentPositionParams::new(
590                 server.doc_id("src/main.rs"),
591                 Position::new(13, 9),
592             ),
593             work_done_progress_params: Default::default(),
594             partial_result_params: Default::default(),
595         },
596         json!([{
597             "originSelectionRange": {
598                 "end": {
599                     "character": 10,
600                     "line": 13
601                 },
602                 "start": {
603                     "character": 8,
604                     "line":13
605                 }
606             },
607             "targetRange": {
608                 "end": {
609                     "character": 9,
610                     "line": 7
611                 },
612                 "start": {
613                     "character": 0,
614                     "line":6
615                 }
616             },
617             "targetSelectionRange": {
618                 "end": {
619                     "character": 8,
620                     "line": 7
621                 },
622                 "start": {
623                     "character": 7,
624                     "line": 7
625                 }
626             },
627             "targetUri": "file:///[..]src/main.rs"
628         }]),
629     );
630 }
631
632 #[test]
633 fn resolve_proc_macro() {
634     if skip_slow_tests() {
635         return;
636     }
637     let server = Project::with_fixture(
638         r###"
639 //- foo/Cargo.toml
640 [package]
641 name = "foo"
642 version = "0.0.0"
643 edition = "2018"
644 [dependencies]
645 bar = {path = "../bar"}
646
647 //- foo/src/main.rs
648 use bar::Bar;
649 trait Bar {
650   fn bar();
651 }
652 #[derive(Bar)]
653 struct Foo {}
654 fn main() {
655   Foo::bar();
656 }
657
658 //- bar/Cargo.toml
659 [package]
660 name = "bar"
661 version = "0.0.0"
662 edition = "2018"
663
664 [lib]
665 proc-macro = true
666
667 //- bar/src/lib.rs
668 extern crate proc_macro;
669 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
670 macro_rules! t {
671     ($n:literal) => {
672         TokenTree::from(Ident::new($n, Span::call_site()))
673     };
674     ({}) => {
675         TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
676     };
677     (()) => {
678         TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
679     };
680 }
681 #[proc_macro_derive(Bar)]
682 pub fn foo(_input: TokenStream) -> TokenStream {
683     // We hard code the output here for preventing to use any deps
684     let mut res = TokenStream::new();
685
686     // impl Bar for Foo { fn bar() {} }
687     let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
688     let mut fn_stream = TokenStream::new();
689     fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
690     tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
691     res.extend(tokens);
692     res
693 }
694
695 "###,
696     )
697     .with_config(|config| {
698         let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
699
700         config.cargo.load_out_dirs_from_check = true;
701         config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
702     })
703     .root("foo")
704     .root("bar")
705     .server();
706     server.wait_until_workspace_is_loaded();
707     let res = server.send_request::<HoverRequest>(HoverParams {
708         text_document_position_params: TextDocumentPositionParams::new(
709             server.doc_id("foo/src/main.rs"),
710             Position::new(7, 9),
711         ),
712         work_done_progress_params: Default::default(),
713     });
714
715     let value = res.get("contents").unwrap().get("value").unwrap().to_string();
716     assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
717 }
718
719 #[test]
720 fn test_client_support_hover_actions() {
721     if skip_slow_tests() {
722         return;
723     }
724
725     let server = Project::with_fixture(
726         r#"
727 //- Cargo.toml
728 [package]
729 name = "foo"
730 version = "0.0.0"
731
732 //- src/lib.rs
733 struct Foo(u32);
734
735 struct NoImpl(u32);
736
737 impl Foo {
738     fn new() -> Self {
739         Self(1)
740     }
741 }
742 "#,
743     )
744     .with_config(|config| {
745         config.client_caps.hover_actions = true;
746     })
747     .server();
748
749     server.wait_until_workspace_is_loaded();
750
751     // has 1 implementation
752     server.request::<HoverRequest>(
753         HoverParams {
754             text_document_position_params: TextDocumentPositionParams::new(
755                 server.doc_id("src/lib.rs"),
756                 Position::new(0, 9),
757             ),
758             work_done_progress_params: Default::default(),
759         },
760         json!({
761             "actions": [{
762                 "commands": [{
763                     "arguments": [
764                       "file:///[..]src/lib.rs",
765                       {
766                         "character": 7,
767                         "line": 0
768                       },
769                       [{
770                           "range": { "end": { "character": 1, "line": 8 }, "start": { "character": 0, "line": 4 } },
771                           "uri": "file:///[..]src/lib.rs"
772                       }]
773                     ],
774                     "command": "rust-analyzer.showReferences",
775                     "title": "1 implementation",
776                     "tooltip": "Go to implementations"
777                 }]
778             }],
779             "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" },
780             "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } }
781         })
782     );
783
784     // no hover
785     server.request::<HoverRequest>(
786         HoverParams {
787             text_document_position_params: TextDocumentPositionParams::new(
788                 server.doc_id("src/lib.rs"),
789                 Position::new(1, 0),
790             ),
791             work_done_progress_params: Default::default(),
792         },
793         json!(null),
794     );
795
796     // no implementations
797     server.request::<HoverRequest>(
798         HoverParams {
799             text_document_position_params: TextDocumentPositionParams::new(
800                 server.doc_id("src/lib.rs"),
801                 Position::new(2, 12),
802             ),
803             work_done_progress_params: Default::default(),
804         },
805         json!({
806             "actions": [{
807                 "commands": [{
808                     "arguments": [
809                       "file:///[..]src/lib.rs",
810                       { "character": 7, "line": 2 },
811                       []
812                     ],
813                     "command": "rust-analyzer.showReferences",
814                     "title": "0 implementations",
815                     "tooltip": "Go to implementations"
816                 }]
817             }],
818             "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" },
819             "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } }
820         })
821     );
822 }
823
824 #[test]
825 fn test_client_does_not_support_hover_actions() {
826     if skip_slow_tests() {
827         return;
828     }
829
830     let server = Project::with_fixture(
831         r#"
832 //- Cargo.toml
833 [package]
834 name = "foo"
835 version = "0.0.0"
836
837 //- src/lib.rs
838 struct Foo(u32);
839
840 struct NoImpl(u32);
841
842 impl Foo {
843     fn new() -> Self {
844         Self(1)
845     }
846 }
847 "#,
848     )
849     .with_config(|config| {
850         config.client_caps.hover_actions = false;
851     })
852     .server();
853
854     server.wait_until_workspace_is_loaded();
855
856     // has 1 implementation
857     server.request::<HoverRequest>(
858         HoverParams {
859             text_document_position_params: TextDocumentPositionParams::new(
860                 server.doc_id("src/lib.rs"),
861                 Position::new(0, 9),
862             ),
863             work_done_progress_params: Default::default(),
864         },
865         json!({
866             "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" },
867             "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } }
868         })
869     );
870
871     // no hover
872     server.request::<HoverRequest>(
873         HoverParams {
874             text_document_position_params: TextDocumentPositionParams::new(
875                 server.doc_id("src/lib.rs"),
876                 Position::new(1, 0),
877             ),
878             work_done_progress_params: Default::default(),
879         },
880         json!(null),
881     );
882
883     // no implementations
884     server.request::<HoverRequest>(
885         HoverParams {
886             text_document_position_params: TextDocumentPositionParams::new(
887                 server.doc_id("src/lib.rs"),
888                 Position::new(2, 12),
889             ),
890             work_done_progress_params: Default::default(),
891         },
892         json!({
893             "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" },
894             "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } }
895         })
896     );
897 }