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)



720
721
722
723
# File 'lib/tree_haver/backends/ffi.rb', line 720

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



994
995
996
997
998
999
1000
1001
1002
1003
1004
# File 'lib/tree_haver/backends/ffi.rb', line 994

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



743
744
745
746
747
748
# File 'lib/tree_haver/backends/ffi.rb', line 743

def child(index)
  return if index >= child_count || index.negative?

  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



760
761
762
763
764
765
766
767
# File 'lib/tree_haver/backends/ffi.rb', line 760

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



735
736
737
# File 'lib/tree_haver/backends/ffi.rb', line 735

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



909
910
911
912
913
914
# File 'lib/tree_haver/backends/ffi.rb', line 909

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



933
934
935
936
937
938
939
940
941
942
943
944
945
946
# File 'lib/tree_haver/backends/ffi.rb', line 933

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



972
973
974
975
976
977
978
979
980
981
982
983
# File 'lib/tree_haver/backends/ffi.rb', line 972

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)


779
780
781
# File 'lib/tree_haver/backends/ffi.rb', line 779

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:



795
796
797
798
799
# File 'lib/tree_haver/backends/ffi.rb', line 795

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



807
808
809
810
811
# File 'lib/tree_haver/backends/ffi.rb', line 807

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



820
821
822
# File 'lib/tree_haver/backends/ffi.rb', line 820

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



830
831
832
# File 'lib/tree_haver/backends/ffi.rb', line 830

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



888
889
890
891
892
893
894
895
# File 'lib/tree_haver/backends/ffi.rb', line 888

def named_child(index)
  return if index.negative? || 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



900
901
902
# File 'lib/tree_haver/backends/ffi.rb', line 900

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



921
922
923
924
925
926
# File 'lib/tree_haver/backends/ffi.rb', line 921

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



953
954
955
956
957
958
959
960
961
962
963
964
965
966
# File 'lib/tree_haver/backends/ffi.rb', line 953

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



867
868
869
870
871
872
# File 'lib/tree_haver/backends/ffi.rb', line 867

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



847
848
849
850
851
852
# File 'lib/tree_haver/backends/ffi.rb', line 847

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



837
838
839
840
841
842
# File 'lib/tree_haver/backends/ffi.rb', line 837

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



877
878
879
880
881
882
# File 'lib/tree_haver/backends/ffi.rb', line 877

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



857
858
859
860
861
862
# File 'lib/tree_haver/backends/ffi.rb', line 857

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)


772
773
774
# File 'lib/tree_haver/backends/ffi.rb', line 772

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:



786
787
788
789
790
# File 'lib/tree_haver/backends/ffi.rb', line 786

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



728
729
730
# File 'lib/tree_haver/backends/ffi.rb', line 728

def type
  Native.ts_node_type(@val)
end