tree_haver 5.0.5 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +299 -660
- data/lib/tree_haver/backend_api.rb +78 -35
- data/lib/tree_haver/backend_context.rb +28 -0
- data/lib/tree_haver/backend_registry.rb +119 -382
- data/lib/tree_haver/backends/citrus.rb +55 -53
- data/lib/tree_haver/backends/ffi.rb +105 -101
- data/lib/tree_haver/backends/java.rb +92 -76
- data/lib/tree_haver/backends/mri.rb +44 -39
- data/lib/tree_haver/backends/parslet.rb +53 -48
- data/lib/tree_haver/backends/prism.rb +140 -43
- data/lib/tree_haver/backends/psych.rb +30 -26
- data/lib/tree_haver/backends/rust.rb +21 -17
- data/lib/tree_haver/backends/tslp.rb +274 -0
- data/lib/tree_haver/base/comment.rb +320 -0
- data/lib/tree_haver/base/language.rb +2 -2
- data/lib/tree_haver/base/node.rb +39 -31
- data/lib/tree_haver/base/parser.rb +4 -0
- data/lib/tree_haver/base/point.rb +3 -3
- data/lib/tree_haver/base/tree.rb +1 -1
- data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
- data/lib/tree_haver/contracts.rb +1025 -0
- data/lib/tree_haver/grammar_finder.rb +125 -70
- data/lib/tree_haver/kaitai_backend.rb +30 -0
- data/lib/tree_haver/language.rb +36 -37
- data/lib/tree_haver/language_pack.rb +41 -0
- data/lib/tree_haver/language_registry.rb +34 -3
- data/lib/tree_haver/library_path_utils.rb +7 -7
- data/lib/tree_haver/node.rb +40 -31
- data/lib/tree_haver/parser.rb +44 -37
- data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
- data/lib/tree_haver/path_validator.rb +39 -36
- data/lib/tree_haver/peg_backends.rb +76 -0
- data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
- data/lib/tree_haver/rspec.rb +1 -31
- data/lib/tree_haver/tree.rb +16 -7
- data/lib/tree_haver/version.rb +5 -14
- data/lib/tree_haver.rb +536 -1240
- data/sig/tree_haver.rbs +1 -229
- data.tar.gz.sig +0 -0
- metadata +154 -70
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -1393
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -359
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
- data/lib/tree_haver/base.rb +0 -12
- data/lib/tree_haver/compat.rb +0 -43
- data/lib/tree_haver/rspec/testable_node.rb +0 -217
- data/sig/tree_haver/backends.rbs +0 -352
- data/sig/tree_haver/grammar_finder.rbs +0 -29
- data/sig/tree_haver/path_validator.rbs +0 -32
|
@@ -28,19 +28,20 @@ module TreeHaver
|
|
|
28
28
|
# @return [Boolean] true if psych is available
|
|
29
29
|
class << self
|
|
30
30
|
def available?
|
|
31
|
-
return @loaded if @load_attempted
|
|
32
|
-
|
|
31
|
+
return @loaded if @load_attempted
|
|
32
|
+
|
|
33
|
+
@load_attempted = true
|
|
33
34
|
begin
|
|
34
|
-
require
|
|
35
|
-
@loaded = true
|
|
35
|
+
require 'psych'
|
|
36
|
+
@loaded = true
|
|
36
37
|
rescue LoadError
|
|
37
|
-
@loaded = false
|
|
38
|
+
@loaded = false
|
|
38
39
|
rescue StandardError
|
|
39
|
-
# :
|
|
40
|
-
@loaded = false
|
|
41
|
-
# :
|
|
40
|
+
# simplecov:disable defensive code - StandardError during require is extremely rare
|
|
41
|
+
@loaded = false
|
|
42
|
+
# simplecov:enable
|
|
42
43
|
end
|
|
43
|
-
@loaded
|
|
44
|
+
@loaded
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
# Reset the load state (primarily for testing)
|
|
@@ -48,8 +49,8 @@ module TreeHaver
|
|
|
48
49
|
# @return [void]
|
|
49
50
|
# @api private
|
|
50
51
|
def reset!
|
|
51
|
-
@load_attempted = false
|
|
52
|
-
@loaded = false
|
|
52
|
+
@load_attempted = false
|
|
53
|
+
@loaded = false
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
# Get capabilities supported by this backend
|
|
@@ -57,6 +58,7 @@ module TreeHaver
|
|
|
57
58
|
# @return [Hash{Symbol => Object}] capability map
|
|
58
59
|
def capabilities
|
|
59
60
|
return {} unless available?
|
|
61
|
+
|
|
60
62
|
{
|
|
61
63
|
backend: :psych,
|
|
62
64
|
query: false, # Psych doesn't have tree-sitter-style queries
|
|
@@ -65,6 +67,7 @@ module TreeHaver
|
|
|
65
67
|
pure_ruby: false, # Psych has native libyaml C extension
|
|
66
68
|
yaml_only: true, # Psych only parses YAML
|
|
67
69
|
error_tolerant: false, # Psych raises on syntax errors
|
|
70
|
+
comment_support: :none
|
|
68
71
|
}
|
|
69
72
|
end
|
|
70
73
|
end
|
|
@@ -108,8 +111,8 @@ module TreeHaver
|
|
|
108
111
|
|
|
109
112
|
unless lang_name == :yaml
|
|
110
113
|
raise TreeHaver::NotAvailable,
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
"Psych backend only supports YAML, not #{lang_name}. " \
|
|
115
|
+
"Use a tree-sitter backend for #{lang_name} support."
|
|
113
116
|
end
|
|
114
117
|
|
|
115
118
|
yaml
|
|
@@ -132,8 +135,9 @@ module TreeHaver
|
|
|
132
135
|
# @return [Tree] Parsed tree
|
|
133
136
|
# @raise [::Psych::SyntaxError] on syntax errors
|
|
134
137
|
def parse(source)
|
|
135
|
-
raise
|
|
136
|
-
|
|
138
|
+
raise 'Language not set' unless language
|
|
139
|
+
|
|
140
|
+
Psych.available? or raise 'Psych not available'
|
|
137
141
|
|
|
138
142
|
ast = ::Psych.parse_stream(source)
|
|
139
143
|
Tree.new(ast, source)
|
|
@@ -193,7 +197,7 @@ module TreeHaver
|
|
|
193
197
|
#
|
|
194
198
|
# @return [String] Node type
|
|
195
199
|
def type
|
|
196
|
-
inner_node.class.name.split(
|
|
200
|
+
inner_node.class.name.split('::').last.downcase
|
|
197
201
|
end
|
|
198
202
|
|
|
199
203
|
# Alias for type (API compatibility)
|
|
@@ -336,7 +340,7 @@ module TreeHaver
|
|
|
336
340
|
#
|
|
337
341
|
# @return [String] Extracted text
|
|
338
342
|
def extract_text_from_location
|
|
339
|
-
return
|
|
343
|
+
return '' unless inner_node.respond_to?(:start_line) && inner_node.respond_to?(:end_line)
|
|
340
344
|
|
|
341
345
|
start_ln = inner_node.start_line || 0
|
|
342
346
|
end_ln = inner_node.end_line || start_ln
|
|
@@ -344,19 +348,19 @@ module TreeHaver
|
|
|
344
348
|
end_col = inner_node.end_column || 0
|
|
345
349
|
|
|
346
350
|
if start_ln == end_ln
|
|
347
|
-
line = lines[start_ln] ||
|
|
348
|
-
line[start_col...end_col] ||
|
|
351
|
+
line = lines[start_ln] || ''
|
|
352
|
+
line[start_col...end_col] || ''
|
|
349
353
|
else
|
|
350
354
|
result = []
|
|
351
355
|
(start_ln..end_ln).each do |ln|
|
|
352
|
-
line = lines[ln] ||
|
|
356
|
+
line = lines[ln] || ''
|
|
353
357
|
result << if ln == start_ln
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
line[start_col..]
|
|
359
|
+
elsif ln == end_ln
|
|
360
|
+
line[0...end_col]
|
|
361
|
+
else
|
|
362
|
+
line
|
|
363
|
+
end
|
|
360
364
|
end
|
|
361
365
|
result.compact.join
|
|
362
366
|
end
|
|
@@ -50,23 +50,24 @@ module TreeHaver
|
|
|
50
50
|
# end
|
|
51
51
|
class << self
|
|
52
52
|
def available?
|
|
53
|
-
return @loaded if @load_attempted
|
|
54
|
-
|
|
53
|
+
return @loaded if @load_attempted
|
|
54
|
+
|
|
55
|
+
@load_attempted = true
|
|
55
56
|
begin
|
|
56
57
|
# tree_stump uses magnus which requires MRI's C API
|
|
57
58
|
# It doesn't work on JRuby or TruffleRuby
|
|
58
|
-
if RUBY_ENGINE ==
|
|
59
|
-
require
|
|
60
|
-
@loaded = true
|
|
59
|
+
if RUBY_ENGINE == 'ruby'
|
|
60
|
+
require 'tree_stump'
|
|
61
|
+
@loaded = true
|
|
61
62
|
else
|
|
62
|
-
@loaded = false
|
|
63
|
+
@loaded = false
|
|
63
64
|
end
|
|
64
65
|
rescue LoadError
|
|
65
|
-
@loaded = false
|
|
66
|
+
@loaded = false
|
|
66
67
|
rescue StandardError
|
|
67
|
-
@loaded = false
|
|
68
|
+
@loaded = false
|
|
68
69
|
end
|
|
69
|
-
@loaded
|
|
70
|
+
@loaded
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
# Reset the load state (primarily for testing)
|
|
@@ -74,8 +75,8 @@ module TreeHaver
|
|
|
74
75
|
# @return [void]
|
|
75
76
|
# @api private
|
|
76
77
|
def reset!
|
|
77
|
-
@load_attempted = false
|
|
78
|
-
@loaded = false
|
|
78
|
+
@load_attempted = false
|
|
79
|
+
@loaded = false
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
# Get capabilities supported by this backend
|
|
@@ -83,14 +84,16 @@ module TreeHaver
|
|
|
83
84
|
# @return [Hash{Symbol => Object}] capability map
|
|
84
85
|
# @example
|
|
85
86
|
# TreeHaver::Backends::Rust.capabilities
|
|
86
|
-
# # => { backend: :rust, query: true, bytes_field: true, incremental: false }
|
|
87
|
+
# # => { backend: :rust, query: true, bytes_field: true, incremental: false, comment_support: :nodes_only }
|
|
87
88
|
def capabilities
|
|
88
89
|
return {} unless available?
|
|
90
|
+
|
|
89
91
|
{
|
|
90
92
|
backend: :rust,
|
|
91
93
|
query: true,
|
|
92
94
|
bytes_field: true,
|
|
93
|
-
incremental: false,
|
|
95
|
+
incremental: false, # TreeStump doesn't currently expose incremental parsing to Ruby
|
|
96
|
+
comment_support: :nodes_only
|
|
94
97
|
}
|
|
95
98
|
end
|
|
96
99
|
end
|
|
@@ -145,7 +148,7 @@ module TreeHaver
|
|
|
145
148
|
end
|
|
146
149
|
|
|
147
150
|
# Alias eql? to ==
|
|
148
|
-
|
|
151
|
+
alias eql? ==
|
|
149
152
|
|
|
150
153
|
# Load a language from a shared library path
|
|
151
154
|
#
|
|
@@ -158,7 +161,7 @@ module TreeHaver
|
|
|
158
161
|
# lang = TreeHaver::Backends::Rust::Language.from_library("/usr/local/lib/libtree-sitter-toml.so")
|
|
159
162
|
class << self
|
|
160
163
|
def from_library(path, symbol: nil, name: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
161
|
-
raise TreeHaver::NotAvailable,
|
|
164
|
+
raise TreeHaver::NotAvailable, 'tree_stump not available' unless Rust.available?
|
|
162
165
|
|
|
163
166
|
# Validate the path exists before calling register_lang to provide a clear error
|
|
164
167
|
raise TreeHaver::NotAvailable, "Language library not found: #{path}" unless File.exist?(path)
|
|
@@ -174,7 +177,7 @@ module TreeHaver
|
|
|
174
177
|
end
|
|
175
178
|
|
|
176
179
|
# Backward-compatible alias for from_library
|
|
177
|
-
|
|
180
|
+
alias from_path from_library
|
|
178
181
|
end
|
|
179
182
|
end
|
|
180
183
|
|
|
@@ -186,7 +189,8 @@ module TreeHaver
|
|
|
186
189
|
#
|
|
187
190
|
# @raise [TreeHaver::NotAvailable] if tree_stump is not available
|
|
188
191
|
def initialize
|
|
189
|
-
raise TreeHaver::NotAvailable,
|
|
192
|
+
raise TreeHaver::NotAvailable, 'tree_stump not available' unless Rust.available?
|
|
193
|
+
|
|
190
194
|
@parser = ::TreeStump::Parser.new
|
|
191
195
|
end
|
|
192
196
|
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TreeHaver
|
|
4
|
+
module Backends
|
|
5
|
+
# TSLP backend using tree_sitter_language_pack's on-demand parser API.
|
|
6
|
+
#
|
|
7
|
+
# This backend intentionally requires the real parser API. The language-pack
|
|
8
|
+
# process API is not a TreeHaver parser backend and must not be used as a
|
|
9
|
+
# merge-gem integration surface.
|
|
10
|
+
module Tslp
|
|
11
|
+
@load_attempted = false
|
|
12
|
+
@loaded = false
|
|
13
|
+
@unavailable_reason = nil
|
|
14
|
+
@language_availability = {}
|
|
15
|
+
@language_unavailable_reasons = {}
|
|
16
|
+
PARSER_SMOKE_SOURCES = {
|
|
17
|
+
'json' => '{}',
|
|
18
|
+
'json5' => '{}',
|
|
19
|
+
'bash' => "echo tree_haver\n",
|
|
20
|
+
'go' => "package main\nfunc main() {}\n",
|
|
21
|
+
'html' => "<!doctype html>\n<title>TreeHaver</title>\n",
|
|
22
|
+
'markdown' => "# TreeHaver\n",
|
|
23
|
+
'ruby' => "class TreeHaverSmoke\nend\n",
|
|
24
|
+
'rust' => "fn main() {}\n",
|
|
25
|
+
'typescript' => "export const treeHaver = true;\n",
|
|
26
|
+
'toml' => "title = \"tree_haver\"\n",
|
|
27
|
+
'yaml' => "tree_haver: true\n"
|
|
28
|
+
}.freeze
|
|
29
|
+
DEFAULT_PARSER_SMOKE_SOURCE = ''
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
attr_reader :unavailable_reason
|
|
33
|
+
|
|
34
|
+
def available?
|
|
35
|
+
return @loaded if @load_attempted
|
|
36
|
+
|
|
37
|
+
@load_attempted = true
|
|
38
|
+
begin
|
|
39
|
+
require 'tree_sitter_language_pack' unless defined?(::TreeSitterLanguagePack)
|
|
40
|
+
@loaded = parser_api_available?
|
|
41
|
+
if !@loaded && @unavailable_reason.to_s.empty?
|
|
42
|
+
@unavailable_reason = 'tree_sitter_language_pack parser API is not exposed'
|
|
43
|
+
end
|
|
44
|
+
rescue LoadError => e
|
|
45
|
+
@loaded = false
|
|
46
|
+
@unavailable_reason = e.message
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
@loaded = false
|
|
49
|
+
@unavailable_reason = e.message
|
|
50
|
+
end
|
|
51
|
+
@loaded
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def reset!
|
|
55
|
+
@load_attempted = false
|
|
56
|
+
@loaded = false
|
|
57
|
+
@unavailable_reason = nil
|
|
58
|
+
@language_availability = {}
|
|
59
|
+
@language_unavailable_reasons = {}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def capabilities
|
|
63
|
+
return {} unless available?
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
backend: :tslp,
|
|
67
|
+
query: false,
|
|
68
|
+
bytes_field: true,
|
|
69
|
+
incremental: false,
|
|
70
|
+
comment_support: :nodes_only,
|
|
71
|
+
language_pack: true
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parser_available_for?(language_name)
|
|
76
|
+
return false unless available?
|
|
77
|
+
|
|
78
|
+
name = language_name.to_s
|
|
79
|
+
return @language_availability.fetch(name) if @language_availability.key?(name)
|
|
80
|
+
|
|
81
|
+
@language_availability[name] = smoke_parse_language(name)
|
|
82
|
+
@language_unavailable_reasons[name] = @unavailable_reason unless @language_availability.fetch(name)
|
|
83
|
+
@language_availability.fetch(name)
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
@unavailable_reason = e.message
|
|
86
|
+
@language_unavailable_reasons[language_name.to_s] = e.message
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def parser_api_available?
|
|
93
|
+
return false unless ::TreeSitterLanguagePack.respond_to?(:get_parser)
|
|
94
|
+
return false unless defined?(::TreeSitterLanguagePack::Parser)
|
|
95
|
+
return false unless ::TreeSitterLanguagePack::Parser.instance_methods.include?(:parse)
|
|
96
|
+
|
|
97
|
+
parser_api_smoke_test
|
|
98
|
+
rescue StandardError => e
|
|
99
|
+
@unavailable_reason = e.message
|
|
100
|
+
false
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parser_api_smoke_test
|
|
104
|
+
language_name, source = PARSER_SMOKE_SOURCES.find do |name, _smoke_source|
|
|
105
|
+
!::TreeSitterLanguagePack.respond_to?(:has_language) ||
|
|
106
|
+
::TreeSitterLanguagePack.has_language(name)
|
|
107
|
+
end
|
|
108
|
+
return false unless language_name
|
|
109
|
+
|
|
110
|
+
smoke_parse_language(language_name, source: source)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def smoke_parse_language(language_name, source: smoke_source_for(language_name))
|
|
114
|
+
name = language_name.to_s
|
|
115
|
+
if ::TreeSitterLanguagePack.respond_to?(:has_language) &&
|
|
116
|
+
!::TreeSitterLanguagePack.has_language(name)
|
|
117
|
+
@unavailable_reason = "tree_sitter_language_pack does not publish #{name}"
|
|
118
|
+
return false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
parser = ::TreeSitterLanguagePack.get_parser(name)
|
|
122
|
+
return false unless parser
|
|
123
|
+
|
|
124
|
+
tree = parser.parse(source)
|
|
125
|
+
return false unless tree&.respond_to?(:root_node)
|
|
126
|
+
|
|
127
|
+
root = tree.root_node
|
|
128
|
+
root && !node_has_error?(root)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def smoke_source_for(language_name)
|
|
132
|
+
PARSER_SMOKE_SOURCES.fetch(language_name.to_s, DEFAULT_PARSER_SMOKE_SOURCE)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def node_has_error?(node)
|
|
136
|
+
if node.respond_to?(:has_error)
|
|
137
|
+
node.has_error
|
|
138
|
+
elsif node.respond_to?(:has_error?)
|
|
139
|
+
node.has_error?
|
|
140
|
+
else
|
|
141
|
+
false
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class Language < TreeHaver::Base::Language
|
|
147
|
+
def initialize(name)
|
|
148
|
+
super(name.to_sym, backend: :tslp, options: {})
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class << self
|
|
152
|
+
def from_library(_path = nil, symbol: nil, name: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
153
|
+
new(name || :unknown)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class Parser < TreeHaver::Base::Parser
|
|
159
|
+
def parse(source)
|
|
160
|
+
raise TreeHaver::NotAvailable, unavailable_message unless Tslp.available?
|
|
161
|
+
raise TreeHaver::NotAvailable, 'TSLP language is not set' unless language
|
|
162
|
+
|
|
163
|
+
parser = ::TreeSitterLanguagePack.get_parser(language.name.to_s)
|
|
164
|
+
raise TreeHaver::NotAvailable, "TSLP did not return a parser for #{language.name}" unless parser
|
|
165
|
+
|
|
166
|
+
raw_tree = parser.parse(source)
|
|
167
|
+
raise TreeHaver::NotAvailable, "TSLP did not return a parse tree for #{language.name}" unless raw_tree
|
|
168
|
+
|
|
169
|
+
Tree.new(raw_tree, source: source, language: language.name)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
|
|
174
|
+
def unavailable_message
|
|
175
|
+
reason = Tslp.unavailable_reason
|
|
176
|
+
detail = reason.to_s.empty? ? 'unknown reason' : reason
|
|
177
|
+
"tree_sitter_language_pack parser API is unavailable: #{detail}"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
class Tree < TreeHaver::Base::Tree
|
|
182
|
+
attr_reader :language
|
|
183
|
+
|
|
184
|
+
def initialize(inner_tree = nil, source: nil, lines: nil, language: nil)
|
|
185
|
+
super(inner_tree, source: source, lines: lines)
|
|
186
|
+
@language = language.to_s
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def root_node
|
|
190
|
+
Node.new(inner_tree.root_node, source: source, lines: lines, language: language)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
class Node < TreeHaver::Base::Node
|
|
195
|
+
NODE_TYPE_ALIASES = {
|
|
196
|
+
'json5' => {
|
|
197
|
+
'file' => 'document',
|
|
198
|
+
'member' => 'pair'
|
|
199
|
+
}
|
|
200
|
+
}.freeze
|
|
201
|
+
|
|
202
|
+
attr_reader :language
|
|
203
|
+
|
|
204
|
+
def initialize(node, source: nil, lines: nil, language: nil)
|
|
205
|
+
super(node, source: source, lines: lines)
|
|
206
|
+
@language = language.to_s
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def type
|
|
210
|
+
NODE_TYPE_ALIASES.fetch(language, {}).fetch(native_type, native_type)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def native_type
|
|
214
|
+
inner_node.kind
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def start_byte
|
|
218
|
+
inner_node.start_byte
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def end_byte
|
|
222
|
+
inner_node.end_byte
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def start_point
|
|
226
|
+
point = inner_node.start_position
|
|
227
|
+
{ row: point.row, column: point.column }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def end_point
|
|
231
|
+
point = inner_node.end_position
|
|
232
|
+
{ row: point.row, column: point.column }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def children
|
|
236
|
+
Array.new(inner_node.child_count) do |index|
|
|
237
|
+
child = inner_node.child(index)
|
|
238
|
+
child && self.class.new(child, source: source, lines: lines, language: language)
|
|
239
|
+
end.compact
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def child_by_field_name(name)
|
|
243
|
+
child = inner_node.child_by_field_name(name.to_s) if inner_node.respond_to?(:child_by_field_name)
|
|
244
|
+
child && self.class.new(child, source: source, lines: lines, language: language)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def parent
|
|
248
|
+
parent = inner_node.parent if inner_node.respond_to?(:parent)
|
|
249
|
+
parent && self.class.new(parent, source: source, lines: lines, language: language)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def named?
|
|
253
|
+
inner_node.is_named
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def has_error?
|
|
257
|
+
inner_node.has_error
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def error?
|
|
261
|
+
inner_node.is_error
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def missing?
|
|
265
|
+
inner_node.is_missing
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def extra?
|
|
269
|
+
inner_node.is_extra
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|