diff --git a/cli/src/tests/parser_test.rs b/cli/src/tests/parser_test.rs index bd19717b..7b534be1 100644 --- a/cli/src/tests/parser_test.rs +++ b/cli/src/tests/parser_test.rs @@ -501,6 +501,67 @@ h + i ); } +#[test] +fn test_parsing_after_editing_tree_that_depends_on_column_position() { + let dir = fixtures_dir() + .join("test_grammars") + .join("depends_on_column"); + + let grammar_json = load_grammar_file(&dir.join("grammar.js"), None).unwrap(); + let (grammar_name, parser_code) = generate_parser_for_grammar(grammar_json.as_str()).unwrap(); + + let mut parser = Parser::new(); + parser + .set_language(&get_test_language(&grammar_name, &parser_code, Some(&dir))) + .unwrap(); + + let mut code = b"\n x".to_vec(); + let mut tree = parser.parse(&code, None).unwrap(); + assert_eq!(tree.root_node().to_sexp(), "(x_is_at (odd_column))"); + + perform_edit( + &mut tree, + &mut code, + &Edit { + position: 1, + deleted_length: 0, + inserted_text: b" ".to_vec(), + }, + ) + .unwrap(); + + assert_eq!(code, b"\n x"); + + let mut recorder = ReadRecorder::new(&code); + let mut tree = parser + .parse_with(&mut |i, _| recorder.read(i), Some(&tree)) + .unwrap(); + + assert_eq!(tree.root_node().to_sexp(), "(x_is_at (even_column))",); + assert_eq!(recorder.strings_read(), vec!["\n x"]); + + perform_edit( + &mut tree, + &mut code, + &Edit { + position: 1, + deleted_length: 0, + inserted_text: b"\n".to_vec(), + }, + ) + .unwrap(); + + assert_eq!(code, b"\n\n x"); + + let mut recorder = ReadRecorder::new(&code); + let tree = parser + .parse_with(&mut |i, _| recorder.read(i), Some(&tree)) + .unwrap(); + + assert_eq!(tree.root_node().to_sexp(), "(x_is_at (even_column))",); + assert_eq!(recorder.strings_read(), vec!["\n\n x"]); +} + #[test] fn test_parsing_after_detecting_error_in_the_middle_of_a_string_token() { let mut parser = Parser::new(); diff --git a/lib/src/subtree.c b/lib/src/subtree.c index 4524e182..2ab8f475 100644 --- a/lib/src/subtree.c +++ b/lib/src/subtree.c @@ -677,7 +677,8 @@ Subtree ts_subtree_edit(Subtree self, const TSInputEdit *input_edit, SubtreePool Edit edit = entry.edit; bool is_noop = edit.old_end.bytes == edit.start.bytes && edit.new_end.bytes == edit.start.bytes; bool is_pure_insertion = edit.old_end.bytes == edit.start.bytes; - bool invalidate_first_row = ts_subtree_depends_on_column(*entry.tree); + bool parent_depends_on_column = ts_subtree_depends_on_column(*entry.tree); + bool column_shifted = edit.new_end.extent.column != edit.old_end.extent.column; Length size = ts_subtree_size(*entry.tree); Length padding = ts_subtree_padding(*entry.tree); @@ -771,8 +772,12 @@ Subtree ts_subtree_edit(Subtree self, const TSInputEdit *input_edit, SubtreePool (child_left.bytes > edit.old_end.bytes) || (child_left.bytes == edit.old_end.bytes && child_size.bytes > 0 && i > 0) ) && ( - !invalidate_first_row || - child_left.extent.row > entry.tree->ptr->padding.extent.row + !parent_depends_on_column || + child_left.extent.row > padding.extent.row + ) && ( + !ts_subtree_depends_on_column(*child) || + !column_shifted || + child_left.extent.row > edit.old_end.extent.row )) { break; } diff --git a/test/fixtures/test_grammars/depends_on_column/corpus.txt b/test/fixtures/test_grammars/depends_on_column/corpus.txt new file mode 100644 index 00000000..5c8dbbe6 --- /dev/null +++ b/test/fixtures/test_grammars/depends_on_column/corpus.txt @@ -0,0 +1,21 @@ +================== +X is at odd column +================== + + x + +--- + +(x_is_at + (odd_column)) + +=================== +X is at even column +=================== + + x + +--- + +(x_is_at + (even_column)) diff --git a/test/fixtures/test_grammars/depends_on_column/grammar.js b/test/fixtures/test_grammars/depends_on_column/grammar.js new file mode 100644 index 00000000..6f74810e --- /dev/null +++ b/test/fixtures/test_grammars/depends_on_column/grammar.js @@ -0,0 +1,7 @@ +module.exports = grammar({ + name: "depends_on_column", + rules: { + x_is_at: ($) => seq(/[ \r\n]*/, choice($.odd_column, $.even_column), "x"), + }, + externals: ($) => [$.odd_column, $.even_column], +}); diff --git a/test/fixtures/test_grammars/depends_on_column/scanner.c b/test/fixtures/test_grammars/depends_on_column/scanner.c new file mode 100644 index 00000000..29c2eb1b --- /dev/null +++ b/test/fixtures/test_grammars/depends_on_column/scanner.c @@ -0,0 +1,40 @@ +#include "tree_sitter/parser.h" + +enum TokenType { ODD_COLUMN, EVEN_COLUMN }; + +// The scanner is stateless + +void *tree_sitter_depends_on_column_external_scanner_create() { + return NULL; +} + +void tree_sitter_depends_on_column_external_scanner_destroy( + void *payload +) { + // no-op +} + +unsigned tree_sitter_depends_on_column_external_scanner_serialize( + void *payload, + char *buffer +) { + return 0; +} + +void tree_sitter_depends_on_column_external_scanner_deserialize( + void *payload, + const char *buffer, + unsigned length +) { + // no-op +} + +bool tree_sitter_depends_on_column_external_scanner_scan( + void *payload, + TSLexer *lexer, + const bool *valid_symbols +) { + lexer->result_symbol = + lexer->get_column(lexer) % 2 ? ODD_COLUMN : EVEN_COLUMN; + return true; +}