Class: TreeSitter::Node

Inherits:
Object
  • Object
show all
Defined in:
lib/tree_sitter/node.rb,
ext/tree_sitter/node.c

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *_args, &_block) ⇒ Object

Allows access to child_by_field_name without using [].



70
71
72
73
74
75
76
# File 'lib/tree_sitter/node.rb', line 70

def method_missing(method_name, *_args, &_block)
  if fields.include?(method_name)
    child_by_field_name(method_name.to_s)
  else
    super
  end
end

Instance Method Details

#==(other) ⇒ Object



259
260
261
# File 'ext/tree_sitter/node.c', line 259

static VALUE node_eq(VALUE self, VALUE other) {
  return ts_node_eq(SELF, unwrap(other)->data) ? Qtrue : Qfalse;
}

#[](*keys) ⇒ Node | Array<Node>

Access node’s named children.

It’s similar to #fetch, but differes in input type, return values, and the internal implementation.

Both of these methods exist for separate use cases, but also because sometime tree-sitter does some monkey business and having both separate implementations can help.

Comparison with #fetch:

[]                            | fetch
------------------------------+----------------------

input types Integer, String, Symbol | Array<String, Symbol>

Array<Integer, String, Symbol>|
------------------------------+----------------------

returns 1-to-1 correspondance with | unique nodes

input                         |
------------------------------+----------------------

uses named_child | field_name_for_child

child_by_field_name           |   via each_node
------------------------------+----------------------

Parameters:

  • keys (Integer | String | Symbol | Array<Integer, String, Symbol>, #read)

Returns:



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/tree_sitter/node.rb', line 47

def [](*keys)
  case keys.length
  when 0 then raise "#{self.class.name}##{__method__} requires a key."
  when 1
    case k = keys.first
    when Numeric        then named_child(k)
    when String, Symbol
      if fields.include?(k.to_sym)
        child_by_field_name(k.to_s)
      else
        raise "Cannot find field #{k}"
      end
    else raise <<~ERR
      #{self.class.name}##{__method__} accepts Integer and returns named child at given index,
          or a (String | Symbol) and returns the child by given field name.
    ERR
    end
  else
    keys.map { |key| self[key] }
  end
end

#changed?Boolean

Returns:

  • (Boolean)


86
87
88
# File 'ext/tree_sitter/node.c', line 86

static VALUE node_has_changes(VALUE self) {
  return ts_node_has_changes(SELF) ? Qtrue : Qfalse;
}

#child(idx) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
# File 'ext/tree_sitter/node.c', line 98

static VALUE node_child(VALUE self, VALUE idx) {
  TSNode node = SELF;
  uint32_t index = NUM2UINT(idx);
  uint32_t range = ts_node_child_count(node);

  if (index < range) {
    return new_node_by_val(ts_node_child(node, index));
  } else {
    rb_raise(rb_eIndexError, "Index %d is out of range (len = %d)", index,
             range);
  }
}

#child_by_field_id(field_id) ⇒ Object



157
158
159
# File 'ext/tree_sitter/node.c', line 157

static VALUE node_child_by_field_id(VALUE self, VALUE field_id) {
  return new_node_by_val(ts_node_child_by_field_id(SELF, NUM2UINT(field_id)));
}

#child_by_field_name(field_name) ⇒ Object



151
152
153
154
155
# File 'ext/tree_sitter/node.c', line 151

static VALUE node_child_by_field_name(VALUE self, VALUE field_name) {
  const char *name = StringValuePtr(field_name);
  uint32_t length = (uint32_t)RSTRING_LEN(field_name);
  return new_node_by_val(ts_node_child_by_field_name(SELF, name, length));
}

#child_countObject



124
125
126
127
128
129
130
131
132
# File 'ext/tree_sitter/node.c', line 124

static VALUE node_child_count(VALUE self) {
  TSNode node = SELF;
  const char *type = ts_node_type(node);
  if (strcmp(type, "end") == 0) {
    return UINT2NUM(0);
  } else {
    return UINT2NUM(ts_node_child_count(SELF));
  }
}

#descendant_for_byte_range(from, to) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
# File 'ext/tree_sitter/node.c', line 186

static VALUE node_descendant_for_byte_range(VALUE self, VALUE from, VALUE to) {
  uint32_t from_b = NUM2UINT(from);
  uint32_t to_b = NUM2UINT(to);

  if (from_b > to_b) {
    rb_raise(rb_eIndexError, "From > To: %d > %d", from_b, to_b);
  } else {
    return new_node_by_val(
        ts_node_descendant_for_byte_range(SELF, from_b, to_b));
  }
}

#descendant_for_point_range(from, to) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'ext/tree_sitter/node.c', line 198

static VALUE node_descendant_for_point_range(VALUE self, VALUE from, VALUE to) {
  TSNode node = SELF;
  TSPoint start = ts_node_start_point(node);
  TSPoint end = ts_node_end_point(node);
  TSPoint f = value_to_point(from);
  TSPoint t = value_to_point(to);

  if ((f.row < start.row) || (t.row > end.row) ||
      (f.row == start.row && (f.column < start.column)) ||
      (t.row == end.row && (t.column > end.column))) {
    rb_raise(rb_eIndexError,
             "Invalid point range: [%+" PRIsVALUE ", %+" PRIsVALUE
             "] is not in [%+" PRIsVALUE ", %+" PRIsVALUE "].",
             from, to, new_point(&start), new_point(&end));
  } else {
    return new_node_by_val(ts_node_descendant_for_point_range(node, f, t));
  }
}

#each {|child| ... } ⇒ Object

Iterate over a node’s children.

Yield Parameters:

  • child (Node)

    the child



86
87
88
89
90
91
92
# File 'lib/tree_sitter/node.rb', line 86

def each
  return enum_for __method__ if !block_given?

  (0...(child_count)).each do |i|
    yield child(i)
  end
end

#each_field {|name, child| ... } ⇒ Object

Iterate over a node’s children assigned to a field.

Yield Parameters:

  • name (NilClass | String)

    field name.

  • child (Node)

    the child.



98
99
100
101
102
103
104
105
106
107
# File 'lib/tree_sitter/node.rb', line 98

def each_field
  return enum_for __method__ if !block_given?

  each.with_index do |c, i|
    f = field_name_for_child(i)
    next if f.nil? || f.empty?

    yield f, c
  end
end

#each_named {|child| ... } ⇒ Object

Iterate over a node’s named children

Yield Parameters:

  • child (Node)

    the child



112
113
114
115
116
117
118
# File 'lib/tree_sitter/node.rb', line 112

def each_named
  return enum_for __method__ if !block_given?

  (0...(named_child_count)).each do |i|
    yield named_child(i)
  end
end

#edit(input_edit) ⇒ Object



251
252
253
254
255
256
257
# File 'ext/tree_sitter/node.c', line 251

static VALUE node_edit(VALUE self, VALUE input_edit) {
  TSNode node = SELF;
  TSInputEdit edit = value_to_input_edit(input_edit);
  ts_node_edit(&node, &edit);

  return Qnil;
}

#end_byteObject



53
54
55
# File 'ext/tree_sitter/node.c', line 53

static VALUE node_end_byte(VALUE self) {
  return UINT2NUM(ts_node_end_byte(SELF));
}

#end_pointObject



57
58
59
# File 'ext/tree_sitter/node.c', line 57

static VALUE node_end_point(VALUE self) {
  return new_point_by_val(ts_node_end_point(SELF));
}

#eq?(other) ⇒ Boolean

Returns:

  • (Boolean)


259
260
261
# File 'ext/tree_sitter/node.c', line 259

static VALUE node_eq(VALUE self, VALUE other) {
  return ts_node_eq(SELF, unwrap(other)->data) ? Qtrue : Qfalse;
}

#error?Boolean

Returns:

  • (Boolean)


90
91
92
# File 'ext/tree_sitter/node.c', line 90

static VALUE node_has_error(VALUE self) {
  return ts_node_has_error(SELF) ? Qtrue : Qfalse;
}

#extra?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'ext/tree_sitter/node.c', line 82

static VALUE node_is_extra(VALUE self) {
  return ts_node_is_extra(SELF) ? Qtrue : Qfalse;
}

#fetch(*keys) ⇒ Object

Access node’s named children.

It’s similar to #fetch, but differes in input type, return values, and the internal implementation.

Both of these methods exist for separate use cases, but also because sometime tree-sitter does some monkey business and having both separate implementations can help.

Comparison with #fetch:

[]                            | fetch
------------------------------+----------------------

input types Integer, String, Symbol | String, Symbol

Array<Integer, String, Symbol>| Array<String, Symbol>
------------------------------+----------------------

returns 1-to-1 correspondance with | unique nodes

input                         |
------------------------------+----------------------

uses named_child | field_name_for_child

child_by_field_name           |   via each_node
------------------------------+----------------------


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/tree_sitter/node.rb', line 146

def fetch(*keys)
  dict = {}
  keys.each.with_index do |k, i|
    dict[k.to_s] = i
  end

  res = {}
  each_field do |f, c|
    if dict.key?(f)
      res[dict[f]] = c
      dict.delete(f)
    end
    break if dict.empty?
  end

  res.sort.map { |_, v| v }
end

#field?(field) ⇒ Boolean

Returns:

  • (Boolean)


17
18
19
# File 'lib/tree_sitter/node.rb', line 17

def field?(field)
  fields.include?(field)
end

#field_name_for_child(idx) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
# File 'ext/tree_sitter/node.c', line 111

static VALUE node_field_name_for_child(VALUE self, VALUE idx) {
  TSNode node = SELF;
  uint32_t index = NUM2UINT(idx);
  uint32_t range = ts_node_child_count(node);

  if (index < range) {
    return safe_str(ts_node_field_name_for_child(node, index));
  } else {
    rb_raise(rb_eIndexError, "Index %d is out of range (len = %d)", index,
             range);
  }
}

#fieldsObject



5
6
7
8
9
10
11
12
13
14
15
# File 'lib/tree_sitter/node.rb', line 5

def fields
  return @fields if @fields

  @fields = Set.new
  child_count.times do |i|
    name = field_name_for_child(i)
    @fields << name.to_sym if name
  end

  @fields
end

#first_child_for_byte(byte) ⇒ Object



177
178
179
# File 'ext/tree_sitter/node.c', line 177

static VALUE node_first_child_for_byte(VALUE self, VALUE byte) {
  return new_node_by_val(ts_node_first_child_for_byte(SELF, NUM2UINT(byte)));
}

#first_named_child_for_byte(byte) ⇒ Object



181
182
183
184
# File 'ext/tree_sitter/node.c', line 181

static VALUE node_first_named_child_for_byte(VALUE self, VALUE byte) {
  return new_node_by_val(
      ts_node_first_named_child_for_byte(SELF, NUM2UINT(byte)));
}

#inspectObject



61
62
63
64
65
66
67
68
# File 'ext/tree_sitter/node.c', line 61

static VALUE node_string(VALUE self) {
  char *str = ts_node_string(SELF);
  VALUE res = safe_str(str);
  if (str) {
    free(str);
  }
  return res;
}

#missing?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'ext/tree_sitter/node.c', line 78

static VALUE node_is_missing(VALUE self) {
  return ts_node_is_missing(SELF) ? Qtrue : Qfalse;
}

#named?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'ext/tree_sitter/node.c', line 74

static VALUE node_is_named(VALUE self) {
  return ts_node_is_named(SELF) ? Qtrue : Qfalse;
}

#named_child(idx) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
# File 'ext/tree_sitter/node.c', line 134

static VALUE node_named_child(VALUE self, VALUE idx) {
  TSNode node = SELF;
  uint32_t index = NUM2UINT(idx);
  uint32_t range = ts_node_named_child_count(node);

  if (index < range) {
    return new_node_by_val(ts_node_named_child(node, index));
  } else {
    rb_raise(rb_eIndexError, "Index %d is out of range (len = %d)", index,
             range);
  }
}

#named_child_countObject



147
148
149
# File 'ext/tree_sitter/node.c', line 147

static VALUE node_named_child_count(VALUE self) {
  return UINT2NUM(ts_node_named_child_count(SELF));
}

#named_descendant_for_byte_range(from, to) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
# File 'ext/tree_sitter/node.c', line 217

static VALUE node_named_descendant_for_byte_range(VALUE self, VALUE from,
                                                  VALUE to) {
  uint32_t from_b = NUM2UINT(from);
  uint32_t to_b = NUM2UINT(to);

  if (from_b > to_b) {
    rb_raise(rb_eIndexError, "From > To: %d > %d", from_b, to_b);
  } else {
    return new_node_by_val(
        ts_node_named_descendant_for_byte_range(SELF, from_b, to_b));
  }
}

#named_descendant_for_point_range(from, to) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'ext/tree_sitter/node.c', line 230

static VALUE node_named_descendant_for_point_range(VALUE self, VALUE from,
                                                   VALUE to) {
  TSNode node = SELF;
  TSPoint start = ts_node_start_point(node);
  TSPoint end = ts_node_end_point(node);
  TSPoint f = value_to_point(from);
  TSPoint t = value_to_point(to);

  if ((f.row < start.row) || (t.row > end.row) ||
      (f.row == start.row && (f.column < start.column)) ||
      (t.row == end.row && (t.column > end.column))) {
    rb_raise(rb_eIndexError,
             "Invalid point range: [%+" PRIsVALUE ", %+" PRIsVALUE
             "] is not in [%+" PRIsVALUE ", %+" PRIsVALUE "].",
             from, to, new_point(&start), new_point(&end));
  } else {
    return new_node_by_val(
        ts_node_named_descendant_for_point_range(node, f, t));
  }
}

#next_named_siblingObject



169
170
171
# File 'ext/tree_sitter/node.c', line 169

static VALUE node_next_named_sibling(VALUE self) {
  return new_node_by_val(ts_node_next_named_sibling(SELF));
}

#next_siblingObject



161
162
163
# File 'ext/tree_sitter/node.c', line 161

static VALUE node_next_sibling(VALUE self) {
  return new_node_by_val(ts_node_next_sibling(SELF));
}

#null?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'ext/tree_sitter/node.c', line 70

static VALUE node_is_null(VALUE self) {
  return ts_node_is_null(SELF) ? Qtrue : Qfalse;
}

#parentObject



94
95
96
# File 'ext/tree_sitter/node.c', line 94

static VALUE node_parent(VALUE self) {
  return new_node_by_val(ts_node_parent(SELF));
}

#prev_named_siblingObject



173
174
175
# File 'ext/tree_sitter/node.c', line 173

static VALUE node_prev_named_sibling(VALUE self) {
  return new_node_by_val(ts_node_prev_named_sibling(SELF));
}

#prev_siblingObject



165
166
167
# File 'ext/tree_sitter/node.c', line 165

static VALUE node_prev_sibling(VALUE self) {
  return new_node_by_val(ts_node_prev_sibling(SELF));
}

#respond_to_missing?(*args) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
# File 'lib/tree_sitter/node.rb', line 78

def respond_to_missing?(*args)
  init_fields
  args.length == 1 && fields.include?(args[0])
end

#start_byteObject



45
46
47
# File 'ext/tree_sitter/node.c', line 45

static VALUE node_start_byte(VALUE self) {
  return UINT2NUM(ts_node_start_byte(SELF));
}

#start_pointObject



49
50
51
# File 'ext/tree_sitter/node.c', line 49

static VALUE node_start_point(VALUE self) {
  return new_point_by_val(ts_node_start_point(SELF));
}

#symbolObject



43
# File 'ext/tree_sitter/node.c', line 43

static VALUE node_symbol(VALUE self) { return UINT2NUM(ts_node_symbol(SELF)); }

#to_aObject



120
121
122
# File 'lib/tree_sitter/node.rb', line 120

def to_a
  each.to_a
end

#to_sObject



61
62
63
64
65
66
67
68
# File 'ext/tree_sitter/node.c', line 61

static VALUE node_string(VALUE self) {
  char *str = ts_node_string(SELF);
  VALUE res = safe_str(str);
  if (str) {
    free(str);
  }
  return res;
}

#to_strObject



61
62
63
64
65
66
67
68
# File 'ext/tree_sitter/node.c', line 61

static VALUE node_string(VALUE self) {
  char *str = ts_node_string(SELF);
  VALUE res = safe_str(str);
  if (str) {
    free(str);
  }
  return res;
}

#typeObject

Class methods