# hacky hacky hacky -- generated by src/gen/rusty_gen.rb, NOT hand-tweaked

# this is a simplistic translation-by-regexp to ruby of the tree-sitter test:
# tree-sitter/cli/src/tests/tree_test.rs


def test_tree_edit()
    parser = TreeSitterFFI.parser()
    parser.set_language(get_language("javascript"))
    control_tree = parser.parse("  abc  !==  def", nil)
    tree = control_tree

    assert_eq!(
        tree.root_node().to_sexp(),
        "(program (expression_statement (binary_expression left: (identifier) right: (identifier))))"
    )

    # edit entirely within the tree's padding:
    # resize the padding of the tree and its leftmost descendants.
    # {
        tree = control_tree.copy()
        tree.edit(TreeSitterFFI::InputEdit.from_hash({
            start_byte: 1,
            old_end_byte: 1,
            new_end_byte: 2,
            start_position: TreeSitterFFI::Point.new(0, 1),
            old_end_position: TreeSitterFFI::Point.new(0, 1),
            new_end_position: TreeSitterFFI::Point.new(0, 2),
        }))

        expr = tree.root_node().child(0).child(0)
        child1 = expr.child(0)
        child2 = expr.child(1)

        assert!(expr.has_changes())
        assert_eq!(expr.start_byte(), 3)
        assert_eq!(expr.end_byte(), 16)
        assert!(child1.has_changes())
        assert_eq!(child1.start_byte(), 3)
        assert_eq!(child1.end_byte(), 6)
        assert!(!child2.has_changes())
        assert_eq!(child2.start_byte(), 8)
        assert_eq!(child2.end_byte(), 11)
    # }

    # edit starting in the tree's padding but extending into its content:
    # shrink the content to compenstate for the expanded padding.
    # {
        tree = control_tree.copy()
        tree.edit(TreeSitterFFI::InputEdit.from_hash({
            start_byte: 1,
            old_end_byte: 4,
            new_end_byte: 5,
            start_position: TreeSitterFFI::Point.new(0, 1),
            old_end_position: TreeSitterFFI::Point.new(0, 5),
            new_end_position: TreeSitterFFI::Point.new(0, 5),
        }))

        expr = tree.root_node().child(0).child(0)
        child1 = expr.child(0)
        child2 = expr.child(1)

        assert!(expr.has_changes())
        assert_eq!(expr.start_byte(), 5)
        assert_eq!(expr.end_byte(), 16)
        assert!(child1.has_changes())
        assert_eq!(child1.start_byte(), 5)
        assert_eq!(child1.end_byte(), 6)
        assert!(!child2.has_changes())
        assert_eq!(child2.start_byte(), 8)
        assert_eq!(child2.end_byte(), 11)
    # }

    # insertion at the edge of a tree's padding:
    # expand the tree's padding.
    # {
        tree = control_tree.copy()
        tree.edit(TreeSitterFFI::InputEdit.from_hash({
            start_byte: 2,
            old_end_byte: 2,
            new_end_byte: 4,
            start_position: TreeSitterFFI::Point.new(0, 2),
            old_end_position: TreeSitterFFI::Point.new(0, 2),
            new_end_position: TreeSitterFFI::Point.new(0, 4),
        }))

        expr = tree.root_node().child(0).child(0)
        child1 = expr.child(0)
        child2 = expr.child(1)

        assert!(expr.has_changes())
        assert_eq!(expr.start_byte(), 4)
        assert_eq!(expr.end_byte(), 17)
        assert!(child1.has_changes())
        assert_eq!(child1.start_byte(), 4)
        assert_eq!(child1.end_byte(), 7)
        assert!(!child2.has_changes())
        assert_eq!(child2.start_byte(), 9)
        assert_eq!(child2.end_byte(), 12)
    # }

    # replacement starting at the edge of the tree's padding:
    # resize the content and not the padding.
    # {
        tree = control_tree.copy()
        tree.edit(TreeSitterFFI::InputEdit.from_hash({
            start_byte: 2,
            old_end_byte: 2,
            new_end_byte: 4,
            start_position: TreeSitterFFI::Point.new(0, 2),
            old_end_position: TreeSitterFFI::Point.new(0, 2),
            new_end_position: TreeSitterFFI::Point.new(0, 4),
        }))

        expr = tree.root_node().child(0).child(0)
        child1 = expr.child(0)
        child2 = expr.child(1)

        assert!(expr.has_changes())
        assert_eq!(expr.start_byte(), 4)
        assert_eq!(expr.end_byte(), 17)
        assert!(child1.has_changes())
        assert_eq!(child1.start_byte(), 4)
        assert_eq!(child1.end_byte(), 7)
        assert!(!child2.has_changes())
        assert_eq!(child2.start_byte(), 9)
        assert_eq!(child2.end_byte(), 12)
    # }

    # deletion that spans more than one child node:
    # shrink subsequent child nodes.
    # {
        tree = control_tree.copy()
        tree.edit(TreeSitterFFI::InputEdit.from_hash({
            start_byte: 1,
            old_end_byte: 11,
            new_end_byte: 4,
            start_position: TreeSitterFFI::Point.new(0, 1),
            old_end_position: TreeSitterFFI::Point.new(0, 11),
            new_end_position: TreeSitterFFI::Point.new(0, 4),
        }))

        expr = tree.root_node().child(0).child(0)
        child1 = expr.child(0)
        child2 = expr.child(1)
        child3 = expr.child(2)

        assert!(expr.has_changes())
        assert_eq!(expr.start_byte(), 4)
        assert_eq!(expr.end_byte(), 8)
        assert!(child1.has_changes())
        assert_eq!(child1.start_byte(), 4)
        assert_eq!(child1.end_byte(), 4)
        assert!(child2.has_changes())
        assert_eq!(child2.start_byte(), 4)
        assert_eq!(child2.end_byte(), 4)
        assert!(child3.has_changes())
        assert_eq!(child3.start_byte(), 5)
        assert_eq!(child3.end_byte(), 8)
    # }

    # insertion at the end of the tree:
    # extend the tree's content.
    # {
        tree = control_tree.copy()
        tree.edit(TreeSitterFFI::InputEdit.from_hash({
            start_byte: 15,
            old_end_byte: 15,
            new_end_byte: 16,
            start_position: TreeSitterFFI::Point.new(0, 15),
            old_end_position: TreeSitterFFI::Point.new(0, 15),
            new_end_position: TreeSitterFFI::Point.new(0, 16),
        }))

        expr = tree.root_node().child(0).child(0)
        child1 = expr.child(0)
        child2 = expr.child(1)
        child3 = expr.child(2)

        assert!(expr.has_changes())
        assert_eq!(expr.start_byte(), 2)
        assert_eq!(expr.end_byte(), 16)
        assert!(!child1.has_changes())
        assert_eq!(child1.end_byte(), 5)
        assert!(!child2.has_changes())
        assert_eq!(child2.end_byte(), 10)
        assert!(child3.has_changes())
        assert_eq!(child3.end_byte(), 16)
    # }
end

def test_tree_cursor()
    parser = TreeSitterFFI.parser()
    parser.set_language(get_language("rust"))

    tree = parser
        .parse(
            "
                struct Stuff {
                    a: A;
                    b: Option<B>,
                }
            ",
            nil,
        )

    cursor = tree.walk()
    assert_eq!(cursor.node().type(), "source_file")

    assert!(cursor.goto_first_child())
    assert_eq!(cursor.node().type(), "struct_item")

    assert!(cursor.goto_first_child())
    assert_eq!(cursor.node().type(), "struct")
    assert_eq!(cursor.node().is_named(), false)

    assert!(cursor.goto_next_sibling())
    assert_eq!(cursor.node().type(), "type_identifier")
    assert_eq!(cursor.node().is_named(), true)

    assert!(cursor.goto_next_sibling())
    assert_eq!(cursor.node().type(), "field_declaration_list")
    assert_eq!(cursor.node().is_named(), true)
end

def test_tree_cursor_fields()
    parser = TreeSitterFFI.parser()
    parser.set_language(get_language("javascript"))

    tree = parser
        .parse("function /*1*/ bar /*2*/ () {}", nil)

    cursor = tree.walk()
    assert_eq!(cursor.node().type(), "program")

    cursor.goto_first_child()
    assert_eq!(cursor.node().type(), "function_declaration")
    assert_eq!(cursor.field_name(), nil)

    cursor.goto_first_child()
    assert_eq!(cursor.node().type(), "function")
    assert_eq!(cursor.field_name(), nil)

    cursor.goto_next_sibling()
    assert_eq!(cursor.node().type(), "comment")
    assert_eq!(cursor.field_name(), nil)

    cursor.goto_next_sibling()
    assert_eq!(cursor.node().type(), "identifier")
    assert_eq!(cursor.field_name(), ("name"))

    cursor.goto_next_sibling()
    assert_eq!(cursor.node().type(), "comment")
    assert_eq!(cursor.field_name(), nil)

    cursor.goto_next_sibling()
    assert_eq!(cursor.node().type(), "formal_parameters")
    assert_eq!(cursor.field_name(), ("parameters"))
end

def test_tree_cursor_child_for_point()
    parser = TreeSitterFFI.parser()
    parser.set_language(get_language("javascript"))
    source = "    [
        one,
        {
            two: tree
        },
        four, five, six
    ];"
    tree = parser.parse(source, nil)

    c = tree.walk()
    assert_eq!(c.node().type(), "program")

    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(7, 0)), -1)
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(6, 6)), -1)
    assert_eq!(c.node().type(), "program")

    # descend to expression statement
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(6, 5)), (0))
    assert_eq!(c.node().type(), "expression_statement")

    # step into ';' and back up
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(7, 0)), -1)
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(6, 5)), (1))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        [";", TreeSitterFFI::Point.new(6, 5)]
    )
    assert!(c.goto_parent())

    # descend into array
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(6, 4)), (0))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["array", TreeSitterFFI::Point.new(0, 4)]
    )

    # step into '[' and back up
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(0, 4)), (0))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["[", TreeSitterFFI::Point.new(0, 4)]
    )
    assert!(c.goto_parent())

    # step into identifier 'one' and back up
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(0, 5)), (1))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["identifier", TreeSitterFFI::Point.new(1, 8)]
    )
    assert!(c.goto_parent())
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(1, 10)), (1))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["identifier", TreeSitterFFI::Point.new(1, 8)]
    )
    assert!(c.goto_parent())

    # step into first ',' and back up
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(1, 11)), (2))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        [",", TreeSitterFFI::Point.new(1, 11)]
    )
    assert!(c.goto_parent())

    # step into identifier 'four' and back up
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(4, 10)), (5))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["identifier", TreeSitterFFI::Point.new(5, 8)]
    )
    assert!(c.goto_parent())
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(5, 0)), (5))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["identifier", TreeSitterFFI::Point.new(5, 8)]
    )
    assert!(c.goto_parent())

    # step into ']' and back up
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(6, 0)), (10))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["]", TreeSitterFFI::Point.new(6, 4)]
    )
    assert!(c.goto_parent())
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(5, 23)), (10))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["]", TreeSitterFFI::Point.new(6, 4)]
    )
    assert!(c.goto_parent())

    # descend into object
    assert_eq!(c.goto_first_child_for_point(TreeSitterFFI::Point.new(2, 0)), (3))
    assert_array_eq!(
        [c.node().type(), c.node().start_point()],
        ["object", TreeSitterFFI::Point.new(2, 8)]
    )
end

def test_tree_node_equality()
    parser = TreeSitterFFI.parser()
    parser.set_language(get_language("rust"))
    tree = parser.parse("struct A {}", nil)
    node1 = tree.root_node()
    node2 = tree.root_node()
    assert_eq!(node1, node2)
    assert_eq!(node1.child(0), node2.child(0))
    assert_ne!(node1.child(0), node2)
end

=begin
def test_get_changed_ranges()
    source_code = b"{a: null};\n".to_vec()

    parser = TreeSitterFFI.parser()
    parser.set_language(get_language("javascript"))
#     control_tree = parser.parse(&source_code, nil)
    tree = control_tree

    assert_eq!(
        tree.root_node().to_sexp(),
        "(program (expression_statement (object (pair key: (property_identifier) value: (null)))))"
    )

    # Updating one token
    # {
        tree = control_tree.copy()
        source_code = control_source_code.copy()

        # Replace `null` with `nothing` - that token has changed syntax
        edit = Edit {
            position: source_code.index("ull"),
            deleted_length: 3,
            inserted_text: b"othing".to_vec(),
        }
#         inverse_edit = invert_edit(&source_code, &edit)
        ranges = get_changed_ranges(parser, tree, source_code, edit)
#         assert_eq!(ranges, vec![range_of(&source_code, "nothing")])

        # Replace `nothing` with `null` - that token has changed syntax
        ranges = get_changed_ranges(parser, tree, source_code, inverse_edit)
#         assert_eq!(ranges, vec![range_of(&source_code, "null")])
    # }

    # Changing only leading whitespace
    # {
        tree = control_tree.copy()
        source_code = control_source_code.copy()

        # Insert leading newline - no changed ranges
        edit = Edit {
            position: 0,
            deleted_length: 0,
            inserted_text: b"\n".to_vec(),
        }
#         inverse_edit = invert_edit(&source_code, &edit)
        ranges = get_changed_ranges(parser, tree, source_code, edit)
        assert_eq!(ranges, vec![])

        # Remove leading newline - no changed ranges
        ranges = get_changed_ranges(parser, tree, source_code, inverse_edit)
        assert_eq!(ranges, vec![])
    # }

    # Inserting elements
    # {
        tree = control_tree.copy()
        source_code = control_source_code.copy()

        # Insert a key-value pair before the `}` - those tokens are changed
        edit1 = Edit {
            position: source_code.index("}"),
            deleted_length: 0,
            inserted_text: b", b: false".to_vec(),
        }
#         inverse_edit1 = invert_edit(&source_code, &edit1)
        ranges = get_changed_ranges(parser, tree, source_code, edit1)
#         assert_eq!(ranges, vec![range_of(&source_code, ", b: false")])

        edit2 = Edit {
            position: source_code.index(", b"),
            deleted_length: 0,
            inserted_text: b", c: 1".to_vec(),
        }
#         inverse_edit2 = invert_edit(&source_code, &edit2)
        ranges = get_changed_ranges(parser, tree, source_code, edit2)
#         assert_eq!(ranges, vec![range_of(&source_code, ", c: 1")])

        # Remove the middle pair
        ranges = get_changed_ranges(parser, tree, source_code, inverse_edit2)
        assert_eq!(ranges, vec![])

        # Remove the second pair
        ranges = get_changed_ranges(parser, tree, source_code, inverse_edit1)
        assert_eq!(ranges, vec![])
    # }

    # Wrapping elements in larger expressions
    # {
        tree = control_tree.copy()
        source_code = control_source_code.copy()

        # Replace `null` with the binary expression `b === null`
        edit1 = Edit {
            position: source_code.index("null"),
            deleted_length: 0,
            inserted_text: b"b === ".to_vec(),
        }
#         inverse_edit1 = invert_edit(&source_code, &edit1)
        ranges = get_changed_ranges(parser, tree, source_code, edit1)
#         assert_eq!(ranges, vec![range_of(&source_code, "b === null")])

        # Undo
        ranges = get_changed_ranges(parser, tree, source_code, inverse_edit1)
#         assert_eq!(ranges, vec![range_of(&source_code, "null")])
    # }
end
=end

=begin
def index_of(text: &Vec<u8>, substring: &str)
    TreeSitterFFI::str.from_utf8(text.as_slice())
        .index(substring)
end
=end

=begin
def range_of(text: &Vec<u8>, substring: &str)
    start_byte = index_of(text, substring)
    end_byte = start_byte + substring.as_bytes().len()
    Range {
        start_byte,
        end_byte,
        start_point: TreeSitterFFI::Point.new(0, start_byte),
        end_point: TreeSitterFFI::Point.new(0, end_byte),
    }
end
=end

=begin
def get_changed_ranges(
    parser: &mut Parser,
    tree: &mut Tree,
    source_code: &mut Vec<u8>,
    edit: Edit,
)
    # perform_edit(tree, source_code, &edit)
#     new_tree = parser.parse(&source_code, (tree))
#     result = tree.changed_ranges(&new_tree).collect()
    *tree = new_tree
    result
end
=end


