tests.rs

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