use super::*;use crate::{LanguageConfig, LanguageMatcher};use rand::rngs::StdRng;use std::{env, ops::Range, sync::Arc};use text::{Buffer, BufferId};use tree_sitter::Node;use unindent::Unindent as _;use util::test::marked_text_ranges; #[test]fn test_splice_included_ranges() { let ranges = vec![ts_range(20..30), ts_range(50..60), ts_range(80..90)]; let (new_ranges, change) = splice_included_ranges( ranges.clone(), &[54..56, 58..68], &[ts_range(50..54), ts_range(59..67)], ); assert_eq!( new_ranges, &[ ts_range(20..30), ts_range(50..54), ts_range(59..67), ts_range(80..90), ] ); assert_eq!(change, 1..3); let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[70..71, 91..100], &[]); assert_eq!( new_ranges, &[ts_range(20..30), ts_range(50..60), ts_range(80..90)] ); assert_eq!(change, 2..3); let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[], &[ts_range(0..2), ts_range(70..75)]); assert_eq!( new_ranges, &[ ts_range(0..2), ts_range(20..30), ts_range(50..60), ts_range(70..75), ts_range(80..90) ] ); assert_eq!(change, 0..4); let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[30..50], &[ts_range(25..55)]); assert_eq!(new_ranges, &[ts_range(25..55), ts_range(80..90)]); assert_eq!(change, 0..1); // does not create overlapping ranges let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[0..18], &[ts_range(20..32)]); assert_eq!( new_ranges, &[ts_range(20..32), ts_range(50..60), ts_range(80..90)] ); assert_eq!(change, 0..1); fn ts_range(range: Range<usize>) -> tree_sitter::Range { tree_sitter::Range { start_byte: range.start, start_point: tree_sitter::Point { row: 0, column: range.start, }, end_byte: range.end, end_point: tree_sitter::Point { row: 0, column: range.end, }, } }} #[gpui::test]fn test_syntax_map_layers_for_range() { let registry = Arc::new(LanguageRegistry::test()); let language = Arc::new(rust_lang()); registry.add(language.clone()); let mut buffer = Buffer::new( 0, BufferId::new(1).unwrap(), r#" fn a() { assert_eq!( b(vec![C {}]), vec![d.e], ); println!("{}", f(|_| true)); } "# .unindent(), ); let mut syntax_map = SyntaxMap::new(); syntax_map.set_language_registry(registry.clone()); syntax_map.reparse(language.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 0)..Point::new(2, 0), &[ "...(function_item ... (block (expression_statement (macro_invocation...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", ], ); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (struct_expression ...", ], ); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 14)..Point::new(3, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (field_expression ...", ], ); assert_layers_for_range( &syntax_map, &buffer, Point::new(5, 12)..Point::new(5, 16), &[ "...(function_item ...", "...(call_expression ... (arguments (closure_expression ...", ], ); // Replace a vec! macro invocation with a plain slice, removing a syntactic layer. let macro_name_range = range_for_text(&buffer, "vec!"); buffer.edit([(macro_name_range, "&")]); syntax_map.interpolate(&buffer); syntax_map.reparse(language.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (reference_expression value: (array_expression...", ], ); // Put the vec! macro back, adding back the syntactic layer. buffer.undo(); syntax_map.interpolate(&buffer); syntax_map.reparse(language.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (struct_expression ...", ], );} #[gpui::test]fn test_dynamic_language_injection() { let registry = Arc::new(LanguageRegistry::test()); let markdown = Arc::new(markdown_lang()); registry.add(markdown.clone()); registry.add(Arc::new(rust_lang())); registry.add(Arc::new(ruby_lang())); let mut buffer = Buffer::new( 0, BufferId::new(1).unwrap(), r#" This is a code block: ```rs fn foo() {} ``` "# .unindent(), ); let mut syntax_map = SyntaxMap::new(); syntax_map.set_language_registry(registry.clone()); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", "...(function_item name: (identifier) parameters: (parameters) body: (block)...", ], ); // Replace Rust with Ruby in code block. let macro_name_range = range_for_text(&buffer, "rs"); buffer.edit([(macro_name_range, "ruby")]); syntax_map.interpolate(&buffer); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", "...(call method: (identifier) arguments: (argument_list (call method: (identifier) arguments: (argument_list) block: (block)...", ], ); // Replace Ruby with a language that hasn't been loaded yet. let macro_name_range = range_for_text(&buffer, "ruby"); buffer.edit([(macro_name_range, "html")]); syntax_map.interpolate(&buffer); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter..." ], ); assert!(syntax_map.contains_unknown_injections()); registry.add(Arc::new(html_lang())); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", "(fragment (text))", ], ); assert!(!syntax_map.contains_unknown_injections());} #[gpui::test]fn test_typing_multiple_new_injections() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ "fn a() { test_macro }", "fn a() { test_macro«!» }", "fn a() { test_macro!«()» }", "fn a() { test_macro!(«b») }", "fn a() { test_macro!(b«.») }", "fn a() { test_macro!(b.«c») }", "fn a() { test_macro!(b.c«()») }", "fn a() { test_macro!(b.c(«vec»)) }", "fn a() { test_macro!(b.c(vec«!»)) }", "fn a() { test_macro!(b.c(vec!«[]»)) }", "fn a() { test_macro!(b.c(vec![«d»])) }", "fn a() { test_macro!(b.c(vec![d«.»])) }", "fn a() { test_macro!(b.c(vec![d.«e»])) }", ], ); assert_capture_ranges( &syntax_map, &buffer, &["field"], "fn a() { test_macro!(b.«c»(vec![d.«e»])) }", );} #[gpui::test]fn test_pasting_new_injection_line_between_others() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ " fn a() { b!(B {}); c!(C {}); d!(D {}); e!(E {}); f!(F {}); g!(G {}); } ", " fn a() { b!(B {}); c!(C {}); d!(D {}); « h!(H {}); » e!(E {}); f!(F {}); g!(G {}); } ", ], ); assert_capture_ranges( &syntax_map, &buffer, &["struct"], " fn a() { b!(«B {}»); c!(«C {}»); d!(«D {}»); h!(«H {}»); e!(«E {}»); f!(«F {}»); g!(«G {}»); } ", );} #[gpui::test]fn test_joining_injections_with_child_injections() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ " fn a() { b!( c![one.two.three], d![four.five.six], ); e!( f![seven.eight], ); } ", " fn a() { b!( c![one.two.three], d![four.five.six], ˇ f![seven.eight], ); } ", ], ); assert_capture_ranges( &syntax_map, &buffer, &["field"], " fn a() { b!( c![one.«two».«three»], d![four.«five».«six»], f![seven.«eight»], ); } ", );} #[gpui::test]fn test_editing_edges_of_injection() { test_edit_sequence( "Rust", &[ " fn a() { b!(c!()) } ", " fn a() { «d»!(c!()) } ", " fn a() { «e»d!(c!()) } ", " fn a() { ed!«[»c!()«]» } ", ], );} #[gpui::test]fn test_edits_preceding_and_intersecting_injection() { test_edit_sequence( "Rust", &[ // "const aaaaaaaaaaaa: B = c!(d(e.f));", "const aˇa: B = c!(d(eˇ));", ], );} #[gpui::test]fn test_non_local_changes_create_injections() { test_edit_sequence( "Rust", &[ " // a! { static B: C = d; // } ", " ˇa! { static B: C = d; ˇ} ", ], );} #[gpui::test]fn test_creating_many_injections_in_one_edit() { test_edit_sequence( "Rust", &[ " fn a() { one(Two::three(3)); four(Five::six(6)); seven(Eight::nine(9)); } ", " fn a() { one«!»(Two::three(3)); four«!»(Five::six(6)); seven«!»(Eight::nine(9)); } ", " fn a() { one!(Two::three«!»(3)); four!(Five::six«!»(6)); seven!(Eight::nine«!»(9)); } ", ], );} #[gpui::test]fn test_editing_across_injection_boundary() { test_edit_sequence( "Rust", &[ " fn one() { two(); three!( three.four, five.six, ); } ", " fn one() { two(); th«irty_five![» three.four, five.six, « seven.eight, ];» } ", ], );} #[gpui::test]fn test_removing_injection_by_replacing_across_boundary() { test_edit_sequence( "Rust", &[ " fn one() {Lines 1–500 of 1344. Load more
use super::*; use crate::{LanguageConfig, LanguageMatcher}; use rand::rngs::StdRng; use std::{env, ops::Range, sync::Arc}; use text::{Buffer, BufferId}; use tree_sitter::Node; use unindent::Unindent as _; use util::test::marked_text_ranges; #[test] fn test_splice_included_ranges() { let ranges = vec![ts_range(20..30), ts_range(50..60), ts_range(80..90)]; let (new_ranges, change) = splice_included_ranges( ranges.clone(), &[54..56, 58..68], &[ts_range(50..54), ts_range(59..67)], ); assert_eq!( new_ranges, &[ ts_range(20..30), ts_range(50..54), ts_range(59..67), ts_range(80..90), ] ); assert_eq!(change, 1..3); let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[70..71, 91..100], &[]); assert_eq!( new_ranges, &[ts_range(20..30), ts_range(50..60), ts_range(80..90)] ); assert_eq!(change, 2..3); let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[], &[ts_range(0..2), ts_range(70..75)]); assert_eq!( new_ranges, &[ ts_range(0..2), ts_range(20..30), ts_range(50..60), ts_range(70..75), ts_range(80..90) ] ); assert_eq!(change, 0..4); let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[30..50], &[ts_range(25..55)]); assert_eq!(new_ranges, &[ts_range(25..55), ts_range(80..90)]); assert_eq!(change, 0..1); // does not create overlapping ranges let (new_ranges, change) = splice_included_ranges(ranges.clone(), &[0..18], &[ts_range(20..32)]); assert_eq!( new_ranges, &[ts_range(20..32), ts_range(50..60), ts_range(80..90)] ); assert_eq!(change, 0..1); fn ts_range(range: Range<usize>) -> tree_sitter::Range { tree_sitter::Range { start_byte: range.start, start_point: tree_sitter::Point { row: 0, column: range.start, }, end_byte: range.end, end_point: tree_sitter::Point { row: 0, column: range.end, }, } } } #[gpui::test] fn test_syntax_map_layers_for_range() { let registry = Arc::new(LanguageRegistry::test()); let language = Arc::new(rust_lang()); registry.add(language.clone()); let mut buffer = Buffer::new( 0, BufferId::new(1).unwrap(), r#" fn a() { assert_eq!( b(vec![C {}]), vec![d.e], ); println!("{}", f(|_| true)); } "# .unindent(), ); let mut syntax_map = SyntaxMap::new(); syntax_map.set_language_registry(registry.clone()); syntax_map.reparse(language.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 0)..Point::new(2, 0), &[ "...(function_item ... (block (expression_statement (macro_invocation...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", ], ); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (struct_expression ...", ], ); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 14)..Point::new(3, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (field_expression ...", ], ); assert_layers_for_range( &syntax_map, &buffer, Point::new(5, 12)..Point::new(5, 16), &[ "...(function_item ...", "...(call_expression ... (arguments (closure_expression ...", ], ); // Replace a vec! macro invocation with a plain slice, removing a syntactic layer. let macro_name_range = range_for_text(&buffer, "vec!"); buffer.edit([(macro_name_range, "&")]); syntax_map.interpolate(&buffer); syntax_map.reparse(language.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (reference_expression value: (array_expression...", ], ); // Put the vec! macro back, adding back the syntactic layer. buffer.undo(); syntax_map.interpolate(&buffer); syntax_map.reparse(language.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(2, 14)..Point::new(2, 16), &[ "...(function_item ...", "...(tuple_expression (call_expression ... arguments: (arguments (macro_invocation...", "...(array_expression (struct_expression ...", ], ); } #[gpui::test] fn test_dynamic_language_injection() { let registry = Arc::new(LanguageRegistry::test()); let markdown = Arc::new(markdown_lang()); registry.add(markdown.clone()); registry.add(Arc::new(rust_lang())); registry.add(Arc::new(ruby_lang())); let mut buffer = Buffer::new( 0, BufferId::new(1).unwrap(), r#" This is a code block: ```rs fn foo() {} ``` "# .unindent(), ); let mut syntax_map = SyntaxMap::new(); syntax_map.set_language_registry(registry.clone()); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", "...(function_item name: (identifier) parameters: (parameters) body: (block)...", ], ); // Replace Rust with Ruby in code block. let macro_name_range = range_for_text(&buffer, "rs"); buffer.edit([(macro_name_range, "ruby")]); syntax_map.interpolate(&buffer); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", "...(call method: (identifier) arguments: (argument_list (call method: (identifier) arguments: (argument_list) block: (block)...", ], ); // Replace Ruby with a language that hasn't been loaded yet. let macro_name_range = range_for_text(&buffer, "ruby"); buffer.edit([(macro_name_range, "html")]); syntax_map.interpolate(&buffer); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter..." ], ); assert!(syntax_map.contains_unknown_injections()); registry.add(Arc::new(html_lang())); syntax_map.reparse(markdown.clone(), &buffer); assert_layers_for_range( &syntax_map, &buffer, Point::new(3, 0)..Point::new(3, 0), &[ "...(fenced_code_block (fenced_code_block_delimiter) (info_string (language)) (code_fence_content) (fenced_code_block_delimiter...", "(fragment (text))", ], ); assert!(!syntax_map.contains_unknown_injections()); } #[gpui::test] fn test_typing_multiple_new_injections() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ "fn a() { test_macro }", "fn a() { test_macro«!» }", "fn a() { test_macro!«()» }", "fn a() { test_macro!(«b») }", "fn a() { test_macro!(b«.») }", "fn a() { test_macro!(b.«c») }", "fn a() { test_macro!(b.c«()») }", "fn a() { test_macro!(b.c(«vec»)) }", "fn a() { test_macro!(b.c(vec«!»)) }", "fn a() { test_macro!(b.c(vec!«[]»)) }", "fn a() { test_macro!(b.c(vec![«d»])) }", "fn a() { test_macro!(b.c(vec![d«.»])) }", "fn a() { test_macro!(b.c(vec![d.«e»])) }", ], ); assert_capture_ranges( &syntax_map, &buffer, &["field"], "fn a() { test_macro!(b.«c»(vec![d.«e»])) }", ); } #[gpui::test] fn test_pasting_new_injection_line_between_others() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ " fn a() { b!(B {}); c!(C {}); d!(D {}); e!(E {}); f!(F {}); g!(G {}); } ", " fn a() { b!(B {}); c!(C {}); d!(D {}); « h!(H {}); » e!(E {}); f!(F {}); g!(G {}); } ", ], ); assert_capture_ranges( &syntax_map, &buffer, &["struct"], " fn a() { b!(«B {}»); c!(«C {}»); d!(«D {}»); h!(«H {}»); e!(«E {}»); f!(«F {}»); g!(«G {}»); } ", ); } #[gpui::test] fn test_joining_injections_with_child_injections() { let (buffer, syntax_map) = test_edit_sequence( "Rust", &[ " fn a() { b!( c![one.two.three], d![four.five.six], ); e!( f![seven.eight], ); } ", " fn a() { b!( c![one.two.three], d![four.five.six], ˇ f![seven.eight], ); } ", ], ); assert_capture_ranges( &syntax_map, &buffer, &["field"], " fn a() { b!( c![one.«two».«three»], d![four.«five».«six»], f![seven.«eight»], ); } ", ); } #[gpui::test] fn test_editing_edges_of_injection() { test_edit_sequence( "Rust", &[ " fn a() { b!(c!()) } ", " fn a() { «d»!(c!()) } ", " fn a() { «e»d!(c!()) } ", " fn a() { ed!«[»c!()«]» } ", ], ); } #[gpui::test] fn test_edits_preceding_and_intersecting_injection() { test_edit_sequence( "Rust", &[ // "const aaaaaaaaaaaa: B = c!(d(e.f));", "const aˇa: B = c!(d(eˇ));", ], ); } #[gpui::test] fn test_non_local_changes_create_injections() { test_edit_sequence( "Rust", &[ " // a! { static B: C = d; // } ", " ˇa! { static B: C = d; ˇ} ", ], ); } #[gpui::test] fn test_creating_many_injections_in_one_edit() { test_edit_sequence( "Rust", &[ " fn a() { one(Two::three(3)); four(Five::six(6)); seven(Eight::nine(9)); } ", " fn a() { one«!»(Two::three(3)); four«!»(Five::six(6)); seven«!»(Eight::nine(9)); } ", " fn a() { one!(Two::three«!»(3)); four!(Five::six«!»(6)); seven!(Eight::nine«!»(9)); } ", ], ); } #[gpui::test] fn test_editing_across_injection_boundary() { test_edit_sequence( "Rust", &[ " fn one() { two(); three!( three.four, five.six, ); } ", " fn one() { two(); th«irty_five![» three.four, five.six, « seven.eight, ];» } ", ], ); } #[gpui::test] fn test_removing_injection_by_replacing_across_boundary() { test_edit_sequence( "Rust", &[ " fn one() {