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 Diagnostic {
472 range: Point::new(3, 9)..Point::new(3, 11),
473 severity: DiagnosticSeverity::ERROR,
474 message: "undefined variable 'BB'".to_string()
475 },
476 Diagnostic {
477 range: Point::new(4, 9)..Point::new(4, 12),
478 severity: DiagnosticSeverity::ERROR,
479 message: "undefined variable 'CCC'".to_string()
480 }
481 ]
482 );
483 assert_eq!(
484 chunks_with_diagnostics(buffer, 0..buffer.len()),
485 [
486 ("\n\nfn a() { ".to_string(), None),
487 ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
488 (" }\nfn b() { ".to_string(), None),
489 ("BB".to_string(), Some(DiagnosticSeverity::ERROR)),
490 (" }\nfn c() { ".to_string(), None),
491 ("CCC".to_string(), Some(DiagnosticSeverity::ERROR)),
492 (" }\n".to_string(), None),
493 ]
494 );
495 assert_eq!(
496 chunks_with_diagnostics(buffer, Point::new(3, 10)..Point::new(4, 11)),
497 [
498 ("B".to_string(), Some(DiagnosticSeverity::ERROR)),
499 (" }\nfn c() { ".to_string(), None),
500 ("CC".to_string(), Some(DiagnosticSeverity::ERROR)),
501 ]
502 );
503
504 // Ensure overlapping diagnostics are highlighted correctly.
505 buffer
506 .update_diagnostics(
507 Some(open_notification.text_document.version),
508 vec![
509 lsp::Diagnostic {
510 range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
511 severity: Some(lsp::DiagnosticSeverity::ERROR),
512 message: "undefined variable 'A'".to_string(),
513 ..Default::default()
514 },
515 lsp::Diagnostic {
516 range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 12)),
517 severity: Some(lsp::DiagnosticSeverity::WARNING),
518 message: "unreachable statement".to_string(),
519 ..Default::default()
520 },
521 ],
522 cx,
523 )
524 .unwrap();
525 assert_eq!(
526 buffer
527 .diagnostics_in_range(Point::new(2, 0)..Point::new(3, 0))
528 .collect::<Vec<_>>(),
529 &[
530 Diagnostic {
531 range: Point::new(2, 9)..Point::new(2, 12),
532 severity: DiagnosticSeverity::WARNING,
533 message: "unreachable statement".to_string()
534 },
535 Diagnostic {
536 range: Point::new(2, 9)..Point::new(2, 10),
537 severity: DiagnosticSeverity::ERROR,
538 message: "undefined variable 'A'".to_string()
539 },
540 ]
541 );
542 assert_eq!(
543 chunks_with_diagnostics(buffer, Point::new(2, 0)..Point::new(3, 0)),
544 [
545 ("fn a() { ".to_string(), None),
546 ("A".to_string(), Some(DiagnosticSeverity::ERROR)),
547 (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
548 ("\n".to_string(), None),
549 ]
550 );
551 assert_eq!(
552 chunks_with_diagnostics(buffer, Point::new(2, 10)..Point::new(3, 0)),
553 [
554 (" }".to_string(), Some(DiagnosticSeverity::WARNING)),
555 ("\n".to_string(), None),
556 ]
557 );
558 });
559
560 // Keep editing the buffer and ensure disk-based diagnostics get translated according to the
561 // changes since the last save.
562 buffer.update(&mut cx, |buffer, cx| {
563 buffer.edit(Some(Point::new(2, 0)..Point::new(2, 0)), " ", cx);
564 buffer.edit(Some(Point::new(2, 8)..Point::new(2, 10)), "(x: usize)", cx);
565 });
566 let change_notification_2 = fake
567 .receive_notification::<lsp::notification::DidChangeTextDocument>()
568 .await;
569 assert!(
570 change_notification_2.text_document.version > change_notification_1.text_document.version
571 );
572
573 buffer.update(&mut cx, |buffer, cx| {
574 buffer
575 .update_diagnostics(
576 Some(change_notification_2.text_document.version),
577 vec![
578 lsp::Diagnostic {
579 range: lsp::Range::new(lsp::Position::new(1, 9), lsp::Position::new(1, 11)),
580 severity: Some(lsp::DiagnosticSeverity::ERROR),
581 message: "undefined variable 'BB'".to_string(),
582 source: Some("rustc".to_string()),
583 ..Default::default()
584 },
585 lsp::Diagnostic {
586 range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
587 severity: Some(lsp::DiagnosticSeverity::ERROR),
588 message: "undefined variable 'A'".to_string(),
589 source: Some("rustc".to_string()),
590 ..Default::default()
591 },
592 ],
593 cx,
594 )
595 .unwrap();
596 assert_eq!(
597 buffer
598 .diagnostics_in_range(0..buffer.len())
599 .collect::<Vec<_>>(),
600 &[
601 Diagnostic {
602 range: Point::new(2, 21)..Point::new(2, 22),
603 severity: DiagnosticSeverity::ERROR,
604 message: "undefined variable 'A'".to_string()
605 },
606 Diagnostic {
607 range: Point::new(3, 9)..Point::new(3, 11),
608 severity: DiagnosticSeverity::ERROR,
609 message: "undefined variable 'BB'".to_string()
610 },
611 ]
612 );
613 });
614
615 fn chunks_with_diagnostics<T: ToOffset>(
616 buffer: &Buffer,
617 range: Range<T>,
618 ) -> Vec<(String, Option<DiagnosticSeverity>)> {
619 let mut chunks: Vec<(String, Option<DiagnosticSeverity>)> = Vec::new();
620 for chunk in buffer.snapshot().highlighted_text_for_range(range) {
621 if chunks
622 .last()
623 .map_or(false, |prev_chunk| prev_chunk.1 == chunk.diagnostic)
624 {
625 chunks.last_mut().unwrap().0.push_str(chunk.text);
626 } else {
627 chunks.push((chunk.text.to_string(), chunk.diagnostic));
628 }
629 }
630 chunks
631 }
632}
633
634#[test]
635fn test_contiguous_ranges() {
636 assert_eq!(
637 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12], 100).collect::<Vec<_>>(),
638 &[1..4, 5..7, 9..13]
639 );
640
641 // Respects the `max_len` parameter
642 assert_eq!(
643 contiguous_ranges([2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31], 3).collect::<Vec<_>>(),
644 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
645 );
646}
647
648impl Buffer {
649 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
650 &self,
651 range: Range<T>,
652 ) -> Option<(Range<Point>, Range<Point>)> {
653 self.enclosing_bracket_ranges(range).map(|(start, end)| {
654 let point_start = start.start.to_point(self)..start.end.to_point(self);
655 let point_end = end.start.to_point(self)..end.end.to_point(self);
656 (point_start, point_end)
657 })
658 }
659}
660
661fn rust_lang() -> Option<Arc<Language>> {
662 Some(Arc::new(
663 Language::new(
664 LanguageConfig {
665 name: "Rust".to_string(),
666 path_suffixes: vec!["rs".to_string()],
667 language_server: Some(LanguageServerConfig {
668 binary: "rust-analyzer".to_string(),
669 disk_based_diagnostic_sources: HashSet::from_iter(vec!["rustc".to_string()]),
670 }),
671 ..Default::default()
672 },
673 tree_sitter_rust::language(),
674 )
675 .with_indents_query(
676 r#"
677 (call_expression) @indent
678 (field_expression) @indent
679 (_ "(" ")" @end) @indent
680 (_ "{" "}" @end) @indent
681 "#,
682 )
683 .unwrap()
684 .with_brackets_query(r#" ("{" @open "}" @close) "#)
685 .unwrap(),
686 ))
687}
688
689fn empty(point: Point) -> Range<Point> {
690 point..point
691}