tests.rs

  1use super::*;
  2use crate::language::LanguageServerConfig;
  3use gpui::{ModelHandle, MutableAppContext};
  4use std::{iter::FromIterator, rc::Rc};
  5use unindent::Unindent as _;
  6
  7#[gpui::test]
  8fn test_edit_events(cx: &mut gpui::MutableAppContext) {
  9    let mut now = Instant::now();
 10    let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
 11    let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
 12
 13    let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
 14    let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
 15    let buffer_ops = buffer1.update(cx, |buffer, cx| {
 16        let buffer_1_events = buffer_1_events.clone();
 17        cx.subscribe(&buffer1, move |_, _, event, _| {
 18            buffer_1_events.borrow_mut().push(event.clone())
 19        })
 20        .detach();
 21        let buffer_2_events = buffer_2_events.clone();
 22        cx.subscribe(&buffer2, move |_, _, event, _| {
 23            buffer_2_events.borrow_mut().push(event.clone())
 24        })
 25        .detach();
 26
 27        // An edit emits an edited event, followed by a dirtied event,
 28        // since the buffer was previously in a clean state.
 29        buffer.edit(Some(2..4), "XYZ", cx);
 30
 31        // An empty transaction does not emit any events.
 32        buffer.start_transaction(None).unwrap();
 33        buffer.end_transaction(None, cx).unwrap();
 34
 35        // A transaction containing two edits emits one edited event.
 36        now += Duration::from_secs(1);
 37        buffer.start_transaction_at(None, now).unwrap();
 38        buffer.edit(Some(5..5), "u", cx);
 39        buffer.edit(Some(6..6), "w", cx);
 40        buffer.end_transaction_at(None, now, cx).unwrap();
 41
 42        // Undoing a transaction emits one edited event.
 43        buffer.undo(cx);
 44
 45        buffer.operations.clone()
 46    });
 47
 48    // Incorporating a set of remote ops emits a single edited event,
 49    // followed by a dirtied event.
 50    buffer2.update(cx, |buffer, cx| {
 51        buffer.apply_ops(buffer_ops, cx).unwrap();
 52    });
 53
 54    let buffer_1_events = buffer_1_events.borrow();
 55    assert_eq!(
 56        *buffer_1_events,
 57        vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited]
 58    );
 59
 60    let buffer_2_events = buffer_2_events.borrow();
 61    assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]);
 62}
 63
 64#[gpui::test]
 65async fn test_apply_diff(mut cx: gpui::TestAppContext) {
 66    let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
 67    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
 68
 69    let text = "a\nccc\ndddd\nffffff\n";
 70    let diff = buffer.read_with(&cx, |b, cx| b.diff(text.into(), cx)).await;
 71    buffer.update(&mut cx, |b, cx| b.apply_diff(diff, cx));
 72    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
 73
 74    let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
 75    let diff = buffer.read_with(&cx, |b, cx| b.diff(text.into(), cx)).await;
 76    buffer.update(&mut cx, |b, cx| b.apply_diff(diff, cx));
 77    cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
 78}
 79
 80#[gpui::test]
 81async fn test_reparse(mut cx: gpui::TestAppContext) {
 82    let text = "fn a() {}";
 83    let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(rust_lang(), None, cx));
 84
 85    // Wait for the initial text to parse
 86    buffer
 87        .condition(&cx, |buffer, _| !buffer.is_parsing())
 88        .await;
 89    assert_eq!(
 90        get_tree_sexp(&buffer, &cx),
 91        concat!(
 92            "(source_file (function_item name: (identifier) ",
 93            "parameters: (parameters) ",
 94            "body: (block)))"
 95        )
 96    );
 97
 98    buffer.update(&mut cx, |buffer, _| {
 99        buffer.set_sync_parse_timeout(Duration::ZERO)
100    });
101
102    // Perform some edits (add parameter and variable reference)
103    // Parsing doesn't begin until the transaction is complete
104    buffer.update(&mut cx, |buf, cx| {
105        buf.start_transaction(None).unwrap();
106
107        let offset = buf.text().find(")").unwrap();
108        buf.edit(vec![offset..offset], "b: C", cx);
109        assert!(!buf.is_parsing());
110
111        let offset = buf.text().find("}").unwrap();
112        buf.edit(vec![offset..offset], " d; ", cx);
113        assert!(!buf.is_parsing());
114
115        buf.end_transaction(None, cx).unwrap();
116        assert_eq!(buf.text(), "fn a(b: C) { d; }");
117        assert!(buf.is_parsing());
118    });
119    buffer
120        .condition(&cx, |buffer, _| !buffer.is_parsing())
121        .await;
122    assert_eq!(
123        get_tree_sexp(&buffer, &cx),
124        concat!(
125            "(source_file (function_item name: (identifier) ",
126            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
127            "body: (block (identifier))))"
128        )
129    );
130
131    // Perform a series of edits without waiting for the current parse to complete:
132    // * turn identifier into a field expression
133    // * turn field expression into a method call
134    // * add a turbofish to the method call
135    buffer.update(&mut cx, |buf, cx| {
136        let offset = buf.text().find(";").unwrap();
137        buf.edit(vec![offset..offset], ".e", cx);
138        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
139        assert!(buf.is_parsing());
140    });
141    buffer.update(&mut cx, |buf, cx| {
142        let offset = buf.text().find(";").unwrap();
143        buf.edit(vec![offset..offset], "(f)", cx);
144        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
145        assert!(buf.is_parsing());
146    });
147    buffer.update(&mut cx, |buf, cx| {
148        let offset = buf.text().find("(f)").unwrap();
149        buf.edit(vec![offset..offset], "::<G>", cx);
150        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
151        assert!(buf.is_parsing());
152    });
153    buffer
154        .condition(&cx, |buffer, _| !buffer.is_parsing())
155        .await;
156    assert_eq!(
157        get_tree_sexp(&buffer, &cx),
158        concat!(
159            "(source_file (function_item name: (identifier) ",
160            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
161            "body: (block (call_expression ",
162            "function: (generic_function ",
163            "function: (field_expression value: (identifier) field: (field_identifier)) ",
164            "type_arguments: (type_arguments (type_identifier))) ",
165            "arguments: (arguments (identifier))))))",
166        )
167    );
168
169    buffer.update(&mut cx, |buf, cx| {
170        buf.undo(cx);
171        assert_eq!(buf.text(), "fn a() {}");
172        assert!(buf.is_parsing());
173    });
174    buffer
175        .condition(&cx, |buffer, _| !buffer.is_parsing())
176        .await;
177    assert_eq!(
178        get_tree_sexp(&buffer, &cx),
179        concat!(
180            "(source_file (function_item name: (identifier) ",
181            "parameters: (parameters) ",
182            "body: (block)))"
183        )
184    );
185
186    buffer.update(&mut cx, |buf, cx| {
187        buf.redo(cx);
188        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
189        assert!(buf.is_parsing());
190    });
191    buffer
192        .condition(&cx, |buffer, _| !buffer.is_parsing())
193        .await;
194    assert_eq!(
195        get_tree_sexp(&buffer, &cx),
196        concat!(
197            "(source_file (function_item name: (identifier) ",
198            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
199            "body: (block (call_expression ",
200            "function: (generic_function ",
201            "function: (field_expression value: (identifier) field: (field_identifier)) ",
202            "type_arguments: (type_arguments (type_identifier))) ",
203            "arguments: (arguments (identifier))))))",
204        )
205    );
206
207    fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
208        buffer.read_with(cx, |buffer, _| {
209            buffer.syntax_tree().unwrap().root_node().to_sexp()
210        })
211    }
212}
213
214#[gpui::test]
215fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
216    let buffer = cx.add_model(|cx| {
217        let text = "
218            mod x {
219                mod y {
220
221                }
222            }
223        "
224        .unindent();
225        Buffer::new(0, text, cx).with_language(rust_lang(), None, cx)
226    });
227    let buffer = buffer.read(cx);
228    assert_eq!(
229        buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
230        Some((
231            Point::new(0, 6)..Point::new(0, 7),
232            Point::new(4, 0)..Point::new(4, 1)
233        ))
234    );
235    assert_eq!(
236        buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
237        Some((
238            Point::new(1, 10)..Point::new(1, 11),
239            Point::new(3, 4)..Point::new(3, 5)
240        ))
241    );
242    assert_eq!(
243        buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
244        Some((
245            Point::new(1, 10)..Point::new(1, 11),
246            Point::new(3, 4)..Point::new(3, 5)
247        ))
248    );
249}
250
251#[gpui::test]
252fn test_edit_with_autoindent(cx: &mut MutableAppContext) {
253    cx.add_model(|cx| {
254        let text = "fn a() {}";
255        let mut buffer = Buffer::new(0, text, cx).with_language(rust_lang(), None, cx);
256
257        buffer.edit_with_autoindent([8..8], "\n\n", cx);
258        assert_eq!(buffer.text(), "fn a() {\n    \n}");
259
260        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
261        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
262
263        buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
264        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
265
266        buffer
267    });
268}
269
270#[gpui::test]
271fn test_autoindent_moves_selections(cx: &mut MutableAppContext) {
272    cx.add_model(|cx| {
273        let text = "fn a() {}";
274
275        let mut buffer = Buffer::new(0, text, cx).with_language(rust_lang(), None, cx);
276
277        let selection_set_id = buffer.add_selection_set::<usize>(&[], cx);
278        buffer.start_transaction(Some(selection_set_id)).unwrap();
279        buffer.edit_with_autoindent([5..5, 9..9], "\n\n", cx);
280        buffer
281            .update_selection_set(
282                selection_set_id,
283                &[
284                    Selection {
285                        id: 0,
286                        start: Point::new(1, 0),
287                        end: Point::new(1, 0),
288                        reversed: false,
289                        goal: SelectionGoal::None,
290                    },
291                    Selection {
292                        id: 1,
293                        start: Point::new(4, 0),
294                        end: Point::new(4, 0),
295                        reversed: false,
296                        goal: SelectionGoal::None,
297                    },
298                ],
299                cx,
300            )
301            .unwrap();
302        assert_eq!(buffer.text(), "fn a(\n\n) {}\n\n");
303
304        // Ending the transaction runs the auto-indent. The selection
305        // at the start of the auto-indented row is pushed to the right.
306        buffer.end_transaction(Some(selection_set_id), cx).unwrap();
307        assert_eq!(buffer.text(), "fn a(\n    \n) {}\n\n");
308        let selection_ranges = buffer
309            .selection_set(selection_set_id)
310            .unwrap()
311            .point_selections(&buffer)
312            .map(|selection| selection.point_range(&buffer))
313            .collect::<Vec<_>>();
314
315        assert_eq!(selection_ranges[0], empty(Point::new(1, 4)));
316        assert_eq!(selection_ranges[1], empty(Point::new(4, 0)));
317
318        buffer
319    });
320}
321
322#[gpui::test]
323fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
324    cx.add_model(|cx| {
325        let text = "
326            fn a() {
327            c;
328            d;
329            }
330        "
331        .unindent();
332
333        let mut buffer = Buffer::new(0, text, cx).with_language(rust_lang(), None, cx);
334
335        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
336        // their indentation is not adjusted.
337        buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
338        assert_eq!(
339            buffer.text(),
340            "
341            fn a() {
342            c();
343            d();
344            }
345            "
346            .unindent()
347        );
348
349        // When appending new content after these lines, the indentation is based on the
350        // preceding lines' actual indentation.
351        buffer.edit_with_autoindent(
352            [empty(Point::new(1, 1)), empty(Point::new(2, 1))],
353            "\n.f\n.g",
354            cx,
355        );
356        assert_eq!(
357            buffer.text(),
358            "
359            fn a() {
360            c
361                .f
362                .g();
363            d
364                .f
365                .g();
366            }
367            "
368            .unindent()
369        );
370        buffer
371    });
372}
373
374#[gpui::test]
375fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
376    cx.add_model(|cx| {
377        let text = "
378            fn a() {}
379        "
380        .unindent();
381
382        let mut buffer = Buffer::new(0, text, cx).with_language(rust_lang(), None, cx);
383
384        buffer.edit_with_autoindent([5..5], "\nb", cx);
385        assert_eq!(
386            buffer.text(),
387            "
388                fn a(
389                    b) {}
390            "
391            .unindent()
392        );
393
394        // The indentation suggestion changed because `@end` node (a close paren)
395        // is now at the beginning of the line.
396        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
397        assert_eq!(
398            buffer.text(),
399            "
400                fn a(
401                ) {}
402            "
403            .unindent()
404        );
405
406        buffer
407    });
408}
409
410#[gpui::test]
411async fn test_diagnostics(mut cx: gpui::TestAppContext) {
412    let (language_server, mut fake) = lsp::LanguageServer::fake(cx.background()).await;
413
414    let text = "
415        fn a() { A }
416        fn b() { BB }
417        fn c() { CCC }
418    "
419    .unindent();
420
421    let buffer = cx.add_model(|cx| {
422        Buffer::new(0, text, cx).with_language(rust_lang(), Some(language_server), cx)
423    });
424
425    let open_notification = fake
426        .receive_notification::<lsp::notification::DidOpenTextDocument>()
427        .await;
428
429    // Edit the buffer, moving the content down
430    buffer.update(&mut cx, |buffer, cx| buffer.edit([0..0], "\n\n", cx));
431    let change_notification_1 = fake
432        .receive_notification::<lsp::notification::DidChangeTextDocument>()
433        .await;
434    assert!(change_notification_1.text_document.version > open_notification.text_document.version);
435
436    buffer.update(&mut cx, |buffer, cx| {
437        // Receive diagnostics for an earlier version of the buffer.
438        buffer
439            .update_diagnostics(
440                Some(open_notification.text_document.version),
441                vec![
442                    lsp::Diagnostic {
443                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
444                        severity: Some(lsp::DiagnosticSeverity::ERROR),
445                        message: "undefined variable 'A'".to_string(),
446                        ..Default::default()
447                    },
448                    lsp::Diagnostic {
449                        range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
450                        severity: Some(lsp::DiagnosticSeverity::ERROR),
451                        message: "undefined variable 'BB'".to_string(),
452                        ..Default::default()
453                    },
454                    lsp::Diagnostic {
455                        range: lsp::Range::new(lsp::Position::new(2, 9), lsp::Position::new(2, 12)),
456                        severity: Some(lsp::DiagnosticSeverity::ERROR),
457                        message: "undefined variable 'CCC'".to_string(),
458                        ..Default::default()
459                    },
460                ],
461                cx,
462            )
463            .unwrap();
464
465        // The diagnostics have moved down since they were created.
466        assert_eq!(
467            buffer
468                .diagnostics_in_range(Point::new(3, 0)..Point::new(5, 0))
469                .collect::<Vec<_>>(),
470            &[
471                (
472                    Point::new(3, 9)..Point::new(3, 11),
473                    &Diagnostic {
474                        severity: DiagnosticSeverity::ERROR,
475                        message: "undefined variable 'BB'".to_string()
476                    },
477                ),
478                (
479                    Point::new(4, 9)..Point::new(4, 12),
480                    &Diagnostic {
481                        severity: DiagnosticSeverity::ERROR,
482                        message: "undefined variable 'CCC'".to_string()
483                    }
484                )
485            ]
486        );
487        assert_eq!(
488            chunks_with_diagnostics(buffer, 0..buffer.len()),
489            [
490                ("\n\nfn a() { ".to_string(), None),
491                ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
492                (" }\nfn b() { ".to_string(), None),
493                ("BB".to_string(), Some(DiagnosticSeverity::ERROR)),
494                (" }\nfn c() { ".to_string(), None),
495                ("CCC".to_string(), Some(DiagnosticSeverity::ERROR)),
496                (" }\n".to_string(), None),
497            ]
498        );
499        assert_eq!(
500            chunks_with_diagnostics(buffer, Point::new(3, 10)..Point::new(4, 11)),
501            [
502                ("B".to_string(), Some(DiagnosticSeverity::ERROR)),
503                (" }\nfn c() { ".to_string(), None),
504                ("CC".to_string(), Some(DiagnosticSeverity::ERROR)),
505            ]
506        );
507
508        // Ensure overlapping diagnostics are highlighted correctly.
509        buffer
510            .update_diagnostics(
511                Some(open_notification.text_document.version),
512                vec![
513                    lsp::Diagnostic {
514                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
515                        severity: Some(lsp::DiagnosticSeverity::ERROR),
516                        message: "undefined variable 'A'".to_string(),
517                        ..Default::default()
518                    },
519                    lsp::Diagnostic {
520                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 12)),
521                        severity: Some(lsp::DiagnosticSeverity::WARNING),
522                        message: "unreachable statement".to_string(),
523                        ..Default::default()
524                    },
525                ],
526                cx,
527            )
528            .unwrap();
529        assert_eq!(
530            buffer
531                .diagnostics_in_range(Point::new(2, 0)..Point::new(3, 0))
532                .collect::<Vec<_>>(),
533            &[
534                (
535                    Point::new(2, 9)..Point::new(2, 12),
536                    &Diagnostic {
537                        severity: DiagnosticSeverity::WARNING,
538                        message: "unreachable statement".to_string()
539                    }
540                ),
541                (
542                    Point::new(2, 9)..Point::new(2, 10),
543                    &Diagnostic {
544                        severity: DiagnosticSeverity::ERROR,
545                        message: "undefined variable 'A'".to_string()
546                    },
547                )
548            ]
549        );
550        assert_eq!(
551            chunks_with_diagnostics(buffer, Point::new(2, 0)..Point::new(3, 0)),
552            [
553                ("fn a() { ".to_string(), None),
554                ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
555                (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
556                ("\n".to_string(), None),
557            ]
558        );
559        assert_eq!(
560            chunks_with_diagnostics(buffer, Point::new(2, 10)..Point::new(3, 0)),
561            [
562                (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
563                ("\n".to_string(), None),
564            ]
565        );
566    });
567
568    // Keep editing the buffer and ensure disk-based diagnostics get translated according to the
569    // changes since the last save.
570    buffer.update(&mut cx, |buffer, cx| {
571        buffer.edit(Some(Point::new(2, 0)..Point::new(2, 0)), "    ", cx);
572        buffer.edit(Some(Point::new(2, 8)..Point::new(2, 10)), "(x: usize)", cx);
573    });
574    let change_notification_2 = fake
575        .receive_notification::<lsp::notification::DidChangeTextDocument>()
576        .await;
577    assert!(
578        change_notification_2.text_document.version > change_notification_1.text_document.version
579    );
580
581    buffer.update(&mut cx, |buffer, cx| {
582        buffer
583            .update_diagnostics(
584                Some(change_notification_2.text_document.version),
585                vec![
586                    lsp::Diagnostic {
587                        range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
588                        severity: Some(lsp::DiagnosticSeverity::ERROR),
589                        message: "undefined variable 'BB'".to_string(),
590                        source: Some("rustc".to_string()),
591                        ..Default::default()
592                    },
593                    lsp::Diagnostic {
594                        range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
595                        severity: Some(lsp::DiagnosticSeverity::ERROR),
596                        message: "undefined variable 'A'".to_string(),
597                        source: Some("rustc".to_string()),
598                        ..Default::default()
599                    },
600                ],
601                cx,
602            )
603            .unwrap();
604        assert_eq!(
605            buffer
606                .diagnostics_in_range(0..buffer.len())
607                .collect::<Vec<_>>(),
608            &[
609                (
610                    Point::new(2, 21)..Point::new(2, 22),
611                    &Diagnostic {
612                        severity: DiagnosticSeverity::ERROR,
613                        message: "undefined variable 'A'".to_string()
614                    }
615                ),
616                (
617                    Point::new(3, 9)..Point::new(3, 11),
618                    &Diagnostic {
619                        severity: DiagnosticSeverity::ERROR,
620                        message: "undefined variable 'BB'".to_string()
621                    },
622                )
623            ]
624        );
625    });
626
627    fn chunks_with_diagnostics<T: ToOffset>(
628        buffer: &Buffer,
629        range: Range<T>,
630    ) -> Vec<(String, Option<DiagnosticSeverity>)> {
631        let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
632        for chunk in buffer.snapshot().highlighted_text_for_range(range) {
633            if chunks
634                .last()
635                .map_or(false, |prev_chunk| prev_chunk.1 == chunk.diagnostic)
636            {
637                chunks.last_mut().unwrap().0.push_str(chunk.text);
638            } else {
639                chunks.push((chunk.text.to_string(), chunk.diagnostic));
640            }
641        }
642        chunks
643    }
644}
645
646#[test]
647fn test_contiguous_ranges() {
648    assert_eq!(
649        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12], 100).collect::<Vec<_>>(),
650        &[1..4, 5..7, 9..13]
651    );
652
653    // Respects the `max_len` parameter
654    assert_eq!(
655        contiguous_ranges([2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31], 3).collect::<Vec<_>>(),
656        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
657    );
658}
659
660impl Buffer {
661    pub fn enclosing_bracket_point_ranges<T: ToOffset>(
662        &self,
663        range: Range<T>,
664    ) -> Option<(Range<Point>, Range<Point>)> {
665        self.enclosing_bracket_ranges(range).map(|(start, end)| {
666            let point_start = start.start.to_point(self)..start.end.to_point(self);
667            let point_end = end.start.to_point(self)..end.end.to_point(self);
668            (point_start, point_end)
669        })
670    }
671}
672
673fn rust_lang() -> Option<Arc<Language>> {
674    Some(Arc::new(
675        Language::new(
676            LanguageConfig {
677                name: "Rust".to_string(),
678                path_suffixes: vec!["rs".to_string()],
679                language_server: Some(LanguageServerConfig {
680                    binary: "rust-analyzer".to_string(),
681                    disk_based_diagnostic_sources: HashSet::from_iter(vec!["rustc".to_string()]),
682                }),
683                ..Default::default()
684            },
685            tree_sitter_rust::language(),
686        )
687        .with_indents_query(
688            r#"
689                (call_expression) @indent
690                (field_expression) @indent
691                (_ "(" ")" @end) @indent
692                (_ "{" "}" @end) @indent
693            "#,
694        )
695        .unwrap()
696        .with_brackets_query(r#" ("{" @open "}" @close) "#)
697        .unwrap(),
698    ))
699}
700
701fn empty(point: Point) -> Range<Point> {
702    point..point
703}