syntax.rs

  1use crate::*;
  2use gpui::ModelHandle;
  3use unindent::Unindent as _;
  4
  5#[gpui::test]
  6async fn test_reparse(mut cx: gpui::TestAppContext) {
  7    let buffer = cx.add_model(|cx| {
  8        let text = "fn a() {}".into();
  9        Buffer::from_history(0, History::new(text), None, Some(rust_lang()), cx)
 10    });
 11
 12    // Wait for the initial text to parse
 13    buffer
 14        .condition(&cx, |buffer, _| !buffer.is_parsing())
 15        .await;
 16    assert_eq!(
 17        get_tree_sexp(&buffer, &cx),
 18        concat!(
 19            "(source_file (function_item name: (identifier) ",
 20            "parameters: (parameters) ",
 21            "body: (block)))"
 22        )
 23    );
 24
 25    buffer.update(&mut cx, |buffer, _| {
 26        buffer.set_sync_parse_timeout(Duration::ZERO)
 27    });
 28
 29    // Perform some edits (add parameter and variable reference)
 30    // Parsing doesn't begin until the transaction is complete
 31    buffer.update(&mut cx, |buf, cx| {
 32        buf.start_transaction(None).unwrap();
 33
 34        let offset = buf.text().find(")").unwrap();
 35        buf.edit(vec![offset..offset], "b: C", cx);
 36        assert!(!buf.is_parsing());
 37
 38        let offset = buf.text().find("}").unwrap();
 39        buf.edit(vec![offset..offset], " d; ", cx);
 40        assert!(!buf.is_parsing());
 41
 42        buf.end_transaction(None, cx).unwrap();
 43        assert_eq!(buf.text(), "fn a(b: C) { d; }");
 44        assert!(buf.is_parsing());
 45    });
 46    buffer
 47        .condition(&cx, |buffer, _| !buffer.is_parsing())
 48        .await;
 49    assert_eq!(
 50        get_tree_sexp(&buffer, &cx),
 51        concat!(
 52            "(source_file (function_item name: (identifier) ",
 53            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 54            "body: (block (identifier))))"
 55        )
 56    );
 57
 58    // Perform a series of edits without waiting for the current parse to complete:
 59    // * turn identifier into a field expression
 60    // * turn field expression into a method call
 61    // * add a turbofish to the method call
 62    buffer.update(&mut cx, |buf, cx| {
 63        let offset = buf.text().find(";").unwrap();
 64        buf.edit(vec![offset..offset], ".e", cx);
 65        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
 66        assert!(buf.is_parsing());
 67    });
 68    buffer.update(&mut cx, |buf, cx| {
 69        let offset = buf.text().find(";").unwrap();
 70        buf.edit(vec![offset..offset], "(f)", cx);
 71        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
 72        assert!(buf.is_parsing());
 73    });
 74    buffer.update(&mut cx, |buf, cx| {
 75        let offset = buf.text().find("(f)").unwrap();
 76        buf.edit(vec![offset..offset], "::<G>", cx);
 77        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
 78        assert!(buf.is_parsing());
 79    });
 80    buffer
 81        .condition(&cx, |buffer, _| !buffer.is_parsing())
 82        .await;
 83    assert_eq!(
 84        get_tree_sexp(&buffer, &cx),
 85        concat!(
 86            "(source_file (function_item name: (identifier) ",
 87            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
 88            "body: (block (call_expression ",
 89            "function: (generic_function ",
 90            "function: (field_expression value: (identifier) field: (field_identifier)) ",
 91            "type_arguments: (type_arguments (type_identifier))) ",
 92            "arguments: (arguments (identifier))))))",
 93        )
 94    );
 95
 96    buffer.update(&mut cx, |buf, cx| {
 97        buf.undo(cx);
 98        assert_eq!(buf.text(), "fn a() {}");
 99        assert!(buf.is_parsing());
100    });
101    buffer
102        .condition(&cx, |buffer, _| !buffer.is_parsing())
103        .await;
104    assert_eq!(
105        get_tree_sexp(&buffer, &cx),
106        concat!(
107            "(source_file (function_item name: (identifier) ",
108            "parameters: (parameters) ",
109            "body: (block)))"
110        )
111    );
112
113    buffer.update(&mut cx, |buf, cx| {
114        buf.redo(cx);
115        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
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 (call_expression ",
127            "function: (generic_function ",
128            "function: (field_expression value: (identifier) field: (field_identifier)) ",
129            "type_arguments: (type_arguments (type_identifier))) ",
130            "arguments: (arguments (identifier))))))",
131        )
132    );
133
134    fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
135        buffer.read_with(cx, |buffer, _| {
136            buffer.syntax_tree().unwrap().root_node().to_sexp()
137        })
138    }
139}
140
141#[gpui::test]
142async fn test_enclosing_bracket_ranges(mut cx: gpui::TestAppContext) {
143    let buffer = cx.add_model(|cx| {
144        let text = "
145            mod x {
146                mod y {
147
148                }
149            }
150        "
151        .unindent()
152        .into();
153        Buffer::from_history(0, History::new(text), None, Some(rust_lang()), cx)
154    });
155    buffer
156        .condition(&cx, |buffer, _| !buffer.is_parsing())
157        .await;
158    buffer.read_with(&cx, |buf, _| {
159        assert_eq!(
160            buf.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
161            Some((
162                Point::new(0, 6)..Point::new(0, 7),
163                Point::new(4, 0)..Point::new(4, 1)
164            ))
165        );
166        assert_eq!(
167            buf.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
168            Some((
169                Point::new(1, 10)..Point::new(1, 11),
170                Point::new(3, 4)..Point::new(3, 5)
171            ))
172        );
173        assert_eq!(
174            buf.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
175            Some((
176                Point::new(1, 10)..Point::new(1, 11),
177                Point::new(3, 4)..Point::new(3, 5)
178            ))
179        );
180    });
181}
182
183#[gpui::test]
184async fn test_edit_with_autoindent(mut cx: gpui::TestAppContext) {
185    let buffer = cx.add_model(|cx| {
186        let text = "fn a() {}".into();
187        Buffer::from_history(0, History::new(text), None, Some(rust_lang()), cx)
188    });
189
190    buffer.update(&mut cx, |buffer, cx| {
191        buffer.edit_with_autoindent([8..8], "\n\n", cx);
192        assert_eq!(buffer.text(), "fn a() {\n    \n}");
193
194        buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
195        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
196
197        buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
198        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
199    });
200}
201
202#[test]
203fn test_contiguous_ranges() {
204    assert_eq!(
205        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12], 100).collect::<Vec<_>>(),
206        &[1..4, 5..7, 9..13]
207    );
208
209    // Respects the `max_len` parameter
210    assert_eq!(
211        contiguous_ranges([2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31], 3).collect::<Vec<_>>(),
212        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
213    );
214}
215
216fn rust_lang() -> Arc<Language> {
217    Arc::new(
218        Language::new(
219            LanguageConfig {
220                name: "Rust".to_string(),
221                path_suffixes: vec!["rs".to_string()],
222                ..Default::default()
223            },
224            tree_sitter_rust::language(),
225        )
226        .with_indents_query(
227            r#"
228                (call_expression) @indent
229                (field_expression) @indent
230                (_ "{" "}" @end) @indent
231            "#,
232        )
233        .unwrap()
234        .with_brackets_query(r#" ("{" @open "}" @close) "#)
235        .unwrap(),
236    )
237}