Class: TreeHaver::Backends::FFI::Node Private

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/tree_haver/backends/ffi.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

FFI-based tree-sitter node (raw backend node)

This is a raw backend node that wraps a TSNode by-value struct from the
tree-sitter C API. It provides the minimal interface needed for tree-sitter
operations but is NOT intended for direct use by application code.

== Architecture Note

Unlike pure-Ruby backends (Citrus, Parslet, Prism, Psych) which define Node
classes that inherit from TreeHaver::Base::Node, tree-sitter backends (MRI,
Rust, FFI, Java) define raw wrapper classes that get wrapped by TreeHaver::Node.

The wrapping hierarchy is:
FFI::Node (this class) → TreeHaver::Node → Base::Node

When you use TreeHaver::Parser#parse, the returned tree’s nodes are already
wrapped in TreeHaver::Node, which provides the full unified API including:

  • #children - Array of child nodes
  • #text - Extract text from source
  • #first_child, #last_child - Convenience accessors
  • #start_line, #end_line - 1-based line numbers
  • #source_position - Hash with position info
  • #each, #map, etc. - Enumerable methods
  • #to_s, #inspect - String representations

This raw class only implements methods that require direct FFI calls to the
tree-sitter C library. The wrapper adds Ruby-level conveniences.

Instance Method Summary collapse

Constructor Details

#initialize(ts_node_value) ⇒ Node

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Node.

Parameters:

  • ts_node_value (Native::TSNode)

    the TSNode struct (by value)



717
718
719
720
# File 'lib/tree_haver/backends/ffi.rb', line 717

def initialize(ts_node_value)
  # Store by-value struct (FFI will copy); methods pass it back by value
  @val = ts_node_value
end

Instance Method Details

#<=>(other) ⇒ Integer?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Compare nodes for ordering (used by Comparable module)

Nodes are ordered by their position in the source:

  1. First by start_byte (earlier nodes come first)
  2. Then by end_byte for tie-breaking (shorter spans come first)
  3. Then by type for deterministic ordering

Parameters:

  • other (Node)

    node to compare with

Returns:

  • (Integer, nil)

    -1, 0, 1, or nil if not comparable



990
991
992
993
994
995
996
997
998
999
1000
# File 'lib/tree_haver/backends/ffi.rb', line 990

def <=>(other)
  return unless other.is_a?(Node)

  cmp = start_byte <=> other.start_byte
  return cmp if cmp.nonzero?

  cmp = end_byte <=> other.end_byte
  return cmp if cmp.nonzero?

  type <=> other.type
end

#child(index) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get a child by index

Parameters:

  • index (Integer)

    child index

Returns:

  • (Node, nil)

    child node or nil if index out of bounds



740
741
742
743
744
# File 'lib/tree_haver/backends/ffi.rb', line 740

def child(index)
  return if index >= child_count || index < 0
  child_node = Native.ts_node_child(@val, index)
  Node.new(child_node)
end

#child_by_field_name(field_name) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get a child node by field name

Tree-sitter grammars define named fields for certain child positions.
For example, in JSON, a “pair” node has “key” and “value” fields.

Examples:

Get the key from a JSON pair

pair.child_by_field_name("key") #=> Node (type: "string")
pair.child_by_field_name("value") #=> Node (type: "string" or "number", etc.)

Parameters:

  • field_name (String)

    the field name to look up

Returns:

  • (Node, nil)

    the child node, or nil if no child has that field



756
757
758
759
760
761
762
763
# File 'lib/tree_haver/backends/ffi.rb', line 756

def child_by_field_name(field_name)
  name = String(field_name)
  child_node = Native.ts_node_child_by_field_name(@val, name, name.bytesize)
  # ts_node_child_by_field_name returns a null node if field not found
  return if Native.ts_node_is_null(child_node)

  Node.new(child_node)
end

#child_countInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the number of children

Returns:

  • (Integer)

    child count



732
733
734
# File 'lib/tree_haver/backends/ffi.rb', line 732

def child_count
  Native.ts_node_child_count(@val)
end

#descendant_for_byte_range(start_byte, end_byte) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find the smallest descendant that spans the given byte range

Parameters:

  • start_byte (Integer)

    start byte offset

  • end_byte (Integer)

    end byte offset

Returns:

  • (Node, nil)

    descendant node or nil if not found



905
906
907
908
909
910
# File 'lib/tree_haver/backends/ffi.rb', line 905

def descendant_for_byte_range(start_byte, end_byte)
  node = Native.ts_node_descendant_for_byte_range(@val, start_byte, end_byte)
  return if Native.ts_node_is_null(node)

  Node.new(node)
end

#descendant_for_point_range(start_point, end_point) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find the smallest descendant that spans the given point range

Parameters:

Returns:

  • (Node, nil)

    descendant node or nil if not found



929
930
931
932
933
934
935
936
937
938
939
940
941
942
# File 'lib/tree_haver/backends/ffi.rb', line 929

def descendant_for_point_range(start_point, end_point)
  start_pt = Native::TSPoint.new
  start_pt[:row] = start_point.respond_to?(:row) ? start_point.row : start_point[:row]
  start_pt[:column] = start_point.respond_to?(:column) ? start_point.column : start_point[:column]

  end_pt = Native::TSPoint.new
  end_pt[:row] = end_point.respond_to?(:row) ? end_point.row : end_point[:row]
  end_pt[:column] = end_point.respond_to?(:column) ? end_point.column : end_point[:column]

  node = Native.ts_node_descendant_for_point_range(@val, start_pt, end_pt)
  return if Native.ts_node_is_null(node)

  Node.new(node)
end

#each {|child| ... } ⇒ Enumerator?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Iterate over child nodes

Yield Parameters:

  • child (Node)

    each child node

Returns:

  • (Enumerator, nil)

    an enumerator if no block given, nil otherwise



968
969
970
971
972
973
974
975
976
977
978
979
# File 'lib/tree_haver/backends/ffi.rb', line 968

def each
  return enum_for(:each) unless block_given?

  count = child_count
  i = 0
  while i < count
    child = Native.ts_node_child(@val, i)
    yield Node.new(child)
    i += 1
  end
  nil
end

#end_byteInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get end byte offset

Returns:

  • (Integer)


775
776
777
# File 'lib/tree_haver/backends/ffi.rb', line 775

def end_byte
  Native.ts_node_end_byte(@val)
end

#end_pointTreeHaver::Point

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get end point

Returns:



791
792
793
794
795
# File 'lib/tree_haver/backends/ffi.rb', line 791

def end_point
  point = Native.ts_node_end_point(@val)
  # TSPoint is returned by value as an FFI::Struct with :row and :column fields
  TreeHaver::Point.new(point[:row], point[:column])
end

#has_error?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if node has error

Returns true if this node or any of its descendants have a syntax error.
This is the FFI equivalent of tree-sitter’s ts_node_has_error.

Returns:

  • (Boolean)

    true if node subtree contains errors



803
804
805
806
807
# File 'lib/tree_haver/backends/ffi.rb', line 803

def has_error?
  # Explicit boolean conversion ensures consistent behavior across Ruby versions
  # FFI :bool return type may behave differently on some platforms
  !!Native.ts_node_has_error(@val)
end

#missing?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if this is a MISSING node

A MISSING node represents a token that was expected by the grammar
but was not found in the source. Tree-sitter inserts MISSING nodes
to allow parsing to continue despite syntax errors.

Returns:

  • (Boolean)

    true if this is a MISSING node



816
817
818
# File 'lib/tree_haver/backends/ffi.rb', line 816

def missing?
  !!Native.ts_node_is_missing(@val)
end

#named?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if this is a named node

Named nodes represent syntactic constructs (e.g., “pair”, “object”).
Anonymous nodes represent syntax/punctuation (e.g., “{“, “,”).

Returns:

  • (Boolean)

    true if this is a named node



826
827
828
# File 'lib/tree_haver/backends/ffi.rb', line 826

def named?
  !!Native.ts_node_is_named(@val)
end

#named_child(index) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get a named child by index

Parameters:

  • index (Integer)

    named child index (0-based)

Returns:

  • (Node, nil)

    named child or nil if index out of bounds



884
885
886
887
888
889
890
891
# File 'lib/tree_haver/backends/ffi.rb', line 884

def named_child(index)
  return if index < 0 || index >= named_child_count

  child_node = Native.ts_node_named_child(@val, index)
  return if Native.ts_node_is_null(child_node)

  Node.new(child_node)
end

#named_child_countInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the count of named children

Returns:

  • (Integer)

    number of named children



896
897
898
# File 'lib/tree_haver/backends/ffi.rb', line 896

def named_child_count
  Native.ts_node_named_child_count(@val)
end

#named_descendant_for_byte_range(start_byte, end_byte) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find the smallest named descendant that spans the given byte range

Parameters:

  • start_byte (Integer)

    start byte offset

  • end_byte (Integer)

    end byte offset

Returns:

  • (Node, nil)

    named descendant node or nil if not found



917
918
919
920
921
922
# File 'lib/tree_haver/backends/ffi.rb', line 917

def named_descendant_for_byte_range(start_byte, end_byte)
  node = Native.ts_node_named_descendant_for_byte_range(@val, start_byte, end_byte)
  return if Native.ts_node_is_null(node)

  Node.new(node)
end

#named_descendant_for_point_range(start_point, end_point) ⇒ Node?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find the smallest named descendant that spans the given point range

Parameters:

Returns:

  • (Node, nil)

    named descendant node or nil if not found



949
950
951
952
953
954
955
956
957
958
959
960
961
962
# File 'lib/tree_haver/backends/ffi.rb', line 949

def named_descendant_for_point_range(start_point, end_point)
  start_pt = Native::TSPoint.new
  start_pt[:row] = start_point.respond_to?(:row) ? start_point.row : start_point[:row]
  start_pt[:column] = start_point.respond_to?(:column) ? start_point.column : start_point[:column]

  end_pt = Native::TSPoint.new
  end_pt[:row] = end_point.respond_to?(:row) ? end_point.row : end_point[:row]
  end_pt[:column] = end_point.respond_to?(:column) ? end_point.column : end_point[:column]

  node = Native.ts_node_named_descendant_for_point_range(@val, start_pt, end_pt)
  return if Native.ts_node_is_null(node)

  Node.new(node)
end

#next_named_siblingNode?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the next named sibling node

Returns:

  • (Node, nil)

    next named sibling or nil if none



863
864
865
866
867
868
# File 'lib/tree_haver/backends/ffi.rb', line 863

def next_named_sibling
  sibling = Native.ts_node_next_named_sibling(@val)
  return if Native.ts_node_is_null(sibling)

  Node.new(sibling)
end

#next_siblingNode?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the next sibling node

Returns:

  • (Node, nil)

    next sibling or nil if none



843
844
845
846
847
848
# File 'lib/tree_haver/backends/ffi.rb', line 843

def next_sibling
  sibling = Native.ts_node_next_sibling(@val)
  return if Native.ts_node_is_null(sibling)

  Node.new(sibling)
end

#parentNode?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the parent node

Returns:

  • (Node, nil)

    parent node or nil if this is the root



833
834
835
836
837
838
# File 'lib/tree_haver/backends/ffi.rb', line 833

def parent
  parent_node = Native.ts_node_parent(@val)
  return if Native.ts_node_is_null(parent_node)

  Node.new(parent_node)
end

#prev_named_siblingNode?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the previous named sibling node

Returns:

  • (Node, nil)

    previous named sibling or nil if none



873
874
875
876
877
878
# File 'lib/tree_haver/backends/ffi.rb', line 873

def prev_named_sibling
  sibling = Native.ts_node_prev_named_sibling(@val)
  return if Native.ts_node_is_null(sibling)

  Node.new(sibling)
end

#prev_siblingNode?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the previous sibling node

Returns:

  • (Node, nil)

    previous sibling or nil if none



853
854
855
856
857
858
# File 'lib/tree_haver/backends/ffi.rb', line 853

def prev_sibling
  sibling = Native.ts_node_prev_sibling(@val)
  return if Native.ts_node_is_null(sibling)

  Node.new(sibling)
end

#start_byteInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get start byte offset

Returns:

  • (Integer)


768
769
770
# File 'lib/tree_haver/backends/ffi.rb', line 768

def start_byte
  Native.ts_node_start_byte(@val)
end

#start_pointTreeHaver::Point

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get start point

Returns:



782
783
784
785
786
# File 'lib/tree_haver/backends/ffi.rb', line 782

def start_point
  point = Native.ts_node_start_point(@val)
  # TSPoint is returned by value as an FFI::Struct with :row and :column fields
  TreeHaver::Point.new(point[:row], point[:column])
end

#typeString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the type name of this node

Returns:

  • (String)

    the node type (e.g., “document”, “table”, “pair”)



725
726
727
# File 'lib/tree_haver/backends/ffi.rb', line 725

def type
  Native.ts_node_type(@val)
end