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)



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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)


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

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:



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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)


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

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:



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

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”)



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

def type
  Native.ts_node_type(@val)
end