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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.md +13 -0
  4. data/README.md +299 -660
  5. data/lib/tree_haver/backend_api.rb +78 -35
  6. data/lib/tree_haver/backend_context.rb +28 -0
  7. data/lib/tree_haver/backend_registry.rb +119 -382
  8. data/lib/tree_haver/backends/citrus.rb +55 -53
  9. data/lib/tree_haver/backends/ffi.rb +105 -101
  10. data/lib/tree_haver/backends/java.rb +92 -76
  11. data/lib/tree_haver/backends/mri.rb +44 -39
  12. data/lib/tree_haver/backends/parslet.rb +53 -48
  13. data/lib/tree_haver/backends/prism.rb +140 -43
  14. data/lib/tree_haver/backends/psych.rb +30 -26
  15. data/lib/tree_haver/backends/rust.rb +21 -17
  16. data/lib/tree_haver/backends/tslp.rb +274 -0
  17. data/lib/tree_haver/base/comment.rb +320 -0
  18. data/lib/tree_haver/base/language.rb +2 -2
  19. data/lib/tree_haver/base/node.rb +39 -31
  20. data/lib/tree_haver/base/parser.rb +4 -0
  21. data/lib/tree_haver/base/point.rb +3 -3
  22. data/lib/tree_haver/base/tree.rb +1 -1
  23. data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
  24. data/lib/tree_haver/contracts.rb +1025 -0
  25. data/lib/tree_haver/grammar_finder.rb +125 -70
  26. data/lib/tree_haver/kaitai_backend.rb +30 -0
  27. data/lib/tree_haver/language.rb +36 -37
  28. data/lib/tree_haver/language_pack.rb +41 -0
  29. data/lib/tree_haver/language_registry.rb +34 -3
  30. data/lib/tree_haver/library_path_utils.rb +7 -7
  31. data/lib/tree_haver/node.rb +40 -31
  32. data/lib/tree_haver/parser.rb +44 -37
  33. data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
  34. data/lib/tree_haver/path_validator.rb +39 -36
  35. data/lib/tree_haver/peg_backends.rb +76 -0
  36. data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
  37. data/lib/tree_haver/rspec.rb +1 -31
  38. data/lib/tree_haver/tree.rb +16 -7
  39. data/lib/tree_haver/version.rb +5 -14
  40. data/lib/tree_haver.rb +536 -1240
  41. data/sig/tree_haver.rbs +1 -229
  42. data.tar.gz.sig +0 -0
  43. metadata +154 -70
  44. metadata.gz.sig +0 -0
  45. data/CHANGELOG.md +0 -1393
  46. data/CITATION.cff +0 -20
  47. data/CODE_OF_CONDUCT.md +0 -134
  48. data/CONTRIBUTING.md +0 -359
  49. data/FUNDING.md +0 -74
  50. data/LICENSE.txt +0 -21
  51. data/REEK +0 -0
  52. data/RUBOCOP.md +0 -71
  53. data/SECURITY.md +0 -21
  54. data/lib/tree_haver/base.rb +0 -12
  55. data/lib/tree_haver/compat.rb +0 -43
  56. data/lib/tree_haver/rspec/testable_node.rb +0 -217
  57. data/sig/tree_haver/backends.rbs +0 -352
  58. data/sig/tree_haver/grammar_finder.rbs +0 -29
  59. data/sig/tree_haver/path_validator.rbs +0 -32
data/RUBOCOP.md DELETED
@@ -1,71 +0,0 @@
1
- # RuboCop Usage Guide
2
-
3
- ## Overview
4
-
5
- A tale of two RuboCop plugin gems.
6
-
7
- ### RuboCop Gradual
8
-
9
- This project uses `rubocop_gradual` instead of vanilla RuboCop for code style checking. The `rubocop_gradual` tool allows for gradual adoption of RuboCop rules by tracking violations in a lock file.
10
-
11
- ### RuboCop LTS
12
-
13
- This project uses `rubocop-lts` to ensure, on a best-effort basis, compatibility with Ruby >= 1.9.2.
14
- RuboCop rules are meticulously configured by the `rubocop-lts` family of gems to ensure that a project is compatible with a specific version of Ruby. See: https://rubocop-lts.gitlab.io for more.
15
-
16
- ## Checking RuboCop Violations
17
-
18
- To check for RuboCop violations in this project, always use:
19
-
20
- ```bash
21
- bundle exec rake rubocop_gradual:check
22
- ```
23
-
24
- **Do not use** the standard RuboCop commands like:
25
- - `bundle exec rubocop`
26
- - `rubocop`
27
-
28
- ## Understanding the Lock File
29
-
30
- The `.rubocop_gradual.lock` file tracks all current RuboCop violations in the project. This allows the team to:
31
-
32
- 1. Prevent new violations while gradually fixing existing ones
33
- 2. Track progress on code style improvements
34
- 3. Ensure CI builds don't fail due to pre-existing violations
35
-
36
- ## Common Commands
37
-
38
- - **Check violations**
39
- - `bundle exec rake rubocop_gradual`
40
- - `bundle exec rake rubocop_gradual:check`
41
- - **(Safe) Autocorrect violations, and update lockfile if no new violations**
42
- - `bundle exec rake rubocop_gradual:autocorrect`
43
- - **Force update the lock file (w/o autocorrect) to match violations present in code**
44
- - `bundle exec rake rubocop_gradual:force_update`
45
-
46
- ## Workflow
47
-
48
- 1. Before submitting a PR, run `bundle exec rake rubocop_gradual:autocorrect`
49
- a. or just the default `bundle exec rake`, as autocorrection is a pre-requisite of the default task.
50
- 2. If there are new violations, either:
51
- - Fix them in your code
52
- - Run `bundle exec rake rubocop_gradual:force_update` to update the lock file (only for violations you can't fix immediately)
53
- 3. Commit the updated `.rubocop_gradual.lock` file along with your changes
54
-
55
- ## Never add inline RuboCop disables
56
-
57
- Do not add inline `rubocop:disable` / `rubocop:enable` comments anywhere in the codebase (including specs, except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). We handle exceptions in two supported ways:
58
-
59
- - Permanent/structural exceptions: prefer adjusting the RuboCop configuration (e.g., in `.rubocop.yml`) to exclude a rule for a path or file pattern when it makes sense project-wide.
60
- - Temporary exceptions while improving code: record the current violations in `.rubocop_gradual.lock` via the gradual workflow:
61
- - `bundle exec rake rubocop_gradual:autocorrect` (preferred; will autocorrect what it can and update the lock only if no new violations were introduced)
62
- - If needed, `bundle exec rake rubocop_gradual:force_update` (as a last resort when you cannot fix the newly reported violations immediately)
63
-
64
- In general, treat the rules as guidance to follow; fix violations rather than ignore them. For example, RSpec conventions in this project expect `described_class` to be used in specs that target a specific class under test.
65
-
66
- ## Benefits of rubocop_gradual
67
-
68
- - Allows incremental adoption of code style rules
69
- - Prevents CI failures due to pre-existing violations
70
- - Provides a clear record of code style debt
71
- - Enables focused efforts on improving code quality over time
data/SECURITY.md DELETED
@@ -1,21 +0,0 @@
1
- # Security Policy
2
-
3
- ## Supported Versions
4
-
5
- | Version | Supported |
6
- |----------|-----------|
7
- | 5.latest | ✅ |
8
-
9
- ## Security contact information
10
-
11
- To report a security vulnerability, please use the
12
- [Tidelift security contact](https://tidelift.com/security).
13
- Tidelift will coordinate the fix and disclosure.
14
-
15
- ## Additional Support
16
-
17
- If you are interested in support for versions older than the latest release,
18
- please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
19
- or find other sponsorship links in the [README].
20
-
21
- [README]: README.md
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TreeHaver
4
- # Base classes for backend implementation
5
- module Base
6
- autoload :Node, File.join(__dir__, "base", "node")
7
- autoload :Tree, File.join(__dir__, "base", "tree")
8
- autoload :Parser, File.join(__dir__, "base", "parser")
9
- autoload :Language, File.join(__dir__, "base", "language")
10
- autoload :Point, File.join(__dir__, "base", "point")
11
- end
12
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Compatibility shim for code that expects TreeSitter constants
4
- #
5
- # When required, this file creates a TreeSitter module that maps to TreeHaver
6
- # equivalents, allowing code written for ruby_tree_sitter to work with TreeHaver
7
- # without modification.
8
- #
9
- # This shim is safe and idempotent:
10
- # - If TreeSitter is already defined (real ruby_tree_sitter is loaded), this does nothing
11
- # - If TreeSitter is not defined, it creates aliases to TreeHaver
12
- #
13
- # @example Using the compatibility shim
14
- # require "tree_haver/compat"
15
- #
16
- # # Now code expecting TreeSitter will work
17
- # parser = TreeSitter::Parser.new # Actually creates TreeHaver::Parser
18
- # tree = parser.parse(source)
19
- #
20
- # @note This is an opt-in feature. Only require this file if you need compatibility
21
- # @see TreeHaver The main module this aliases to
22
-
23
- unless defined?(TreeSitter)
24
- # Compatibility module aliasing TreeHaver classes to TreeSitter
25
- #
26
- # @note Only defined if TreeSitter doesn't already exist
27
- module TreeSitter; end
28
-
29
- # @!parse
30
- # module TreeSitter
31
- # Error = TreeHaver::Error
32
- # Parser = TreeHaver::Parser
33
- # Tree = TreeHaver::Tree
34
- # Node = TreeHaver::Node
35
- # Language = TreeHaver::Language
36
- # end
37
-
38
- TreeSitter::Error = TreeHaver::Error
39
- TreeSitter::Parser = TreeHaver::Parser
40
- TreeSitter::Tree = TreeHaver::Tree
41
- TreeSitter::Node = TreeHaver::Node
42
- TreeSitter::Language = TreeHaver::Language
43
- end
@@ -1,217 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Ensure TreeHaver::Node and TreeHaver::Point are loaded
4
- require "tree_haver"
5
-
6
- module TreeHaver
7
- module RSpec
8
- # A mock inner node that provides the minimal interface TreeHaver::Node expects.
9
- #
10
- # This is what TreeHaver::Node wraps - it simulates the backend-specific node
11
- # (like tree-sitter's Node, Markly::Node, etc.)
12
- #
13
- # @api private
14
- class MockInnerNode
15
- attr_reader :type, :start_byte, :end_byte, :children_data
16
-
17
- def initialize(
18
- type:,
19
- text: nil,
20
- start_byte: 0,
21
- end_byte: nil,
22
- start_row: 0,
23
- start_column: 0,
24
- end_row: nil,
25
- end_column: nil,
26
- children: []
27
- )
28
- @type = type.to_s
29
- @text_content = text
30
- @start_byte = start_byte
31
- @end_byte = end_byte || (text ? start_byte + text.length : start_byte)
32
- @start_row = start_row
33
- @start_column = start_column
34
- @end_row = end_row || start_row
35
- @end_column = end_column || (text ? start_column + text.length : start_column)
36
- @children_data = children
37
- end
38
-
39
- def start_point
40
- TreeHaver::Point.new(@start_row, @start_column)
41
- end
42
-
43
- def end_point
44
- TreeHaver::Point.new(@end_row, @end_column)
45
- end
46
-
47
- def child_count
48
- @children_data.length
49
- end
50
-
51
- def child(index)
52
- return if index.nil? || index < 0 || index >= @children_data.length
53
-
54
- @children_data[index]
55
- end
56
-
57
- # Return children array (for enumerable behavior)
58
- def children
59
- @children_data
60
- end
61
-
62
- def first_child
63
- @children_data.first
64
- end
65
-
66
- def last_child
67
- @children_data.last
68
- end
69
-
70
- # Iterate over children
71
- def each(&block)
72
- return enum_for(:each) unless block
73
-
74
- @children_data.each(&block)
75
- end
76
-
77
- def named?
78
- true
79
- end
80
-
81
- # Test nodes are always valid (no parse errors)
82
- def has_error?
83
- false
84
- end
85
-
86
- # Test nodes are never missing (not error recovery insertions)
87
- def missing?
88
- false
89
- end
90
-
91
- # Some backends provide text directly
92
- def text
93
- @text_content
94
- end
95
-
96
- # For backends that use string_content (like Markly/Commonmarker)
97
- def string_content
98
- @text_content
99
- end
100
- end
101
-
102
- # A real TreeHaver::Node that wraps a MockInnerNode.
103
- #
104
- # This gives us full TreeHaver::Node behavior (#text, #type, #source_position, etc.)
105
- # while allowing us to control the underlying data for testing.
106
- #
107
- # TestableNode is designed for testing code that works with TreeHaver nodes
108
- # without requiring an actual parser backend. It creates real TreeHaver::Node
109
- # instances with controlled, predictable data.
110
- #
111
- # @example Creating a testable node
112
- # node = TreeHaver::RSpec::TestableNode.create(
113
- # type: :heading,
114
- # text: "## My Heading",
115
- # start_line: 1
116
- # )
117
- # node.text # => "## My Heading"
118
- # node.type # => "heading"
119
- # node.start_line # => 1
120
- #
121
- # @example Creating with children
122
- # parent = TreeHaver::RSpec::TestableNode.create(
123
- # type: :document,
124
- # text: "# Title\n\nParagraph",
125
- # children: [
126
- # { type: :heading, text: "# Title", start_line: 1 },
127
- # { type: :paragraph, text: "Paragraph", start_line: 3 },
128
- # ]
129
- # )
130
- #
131
- # @example Using the convenience constant
132
- # # After requiring tree_haver/rspec/testable_node, you can use:
133
- # node = TestableNode.create(type: :paragraph, text: "Hello")
134
- #
135
- class TestableNode < TreeHaver::Node
136
- class << self
137
- # Create a TestableNode with the given attributes.
138
- #
139
- # @param type [Symbol, String] Node type (e.g., :heading, :paragraph)
140
- # @param text [String] The text content of the node
141
- # @param start_line [Integer] 1-based start line number (default: 1)
142
- # @param end_line [Integer, nil] 1-based end line number (default: calculated from text)
143
- # @param start_column [Integer] 0-based start column (default: 0)
144
- # @param end_column [Integer, nil] 0-based end column (default: calculated from text)
145
- # @param start_byte [Integer] Start byte offset (default: 0)
146
- # @param end_byte [Integer, nil] End byte offset (default: calculated from text)
147
- # @param children [Array<Hash>] Child node specifications
148
- # @param source [String, nil] Full source text (default: uses text param)
149
- # @return [TestableNode]
150
- def create(
151
- type:,
152
- text: "",
153
- start_line: 1,
154
- end_line: nil,
155
- start_column: 0,
156
- end_column: nil,
157
- start_byte: 0,
158
- end_byte: nil,
159
- children: [],
160
- source: nil
161
- )
162
- # Convert 1-based line to 0-based row
163
- start_row = start_line - 1
164
- end_row = end_line ? end_line - 1 : start_row + text.count("\n")
165
-
166
- # Calculate end_column if not provided
167
- if end_column.nil?
168
- lines = text.split("\n", -1)
169
- end_column = lines.last&.length || 0
170
- end
171
-
172
- # Build children as MockInnerNodes
173
- child_nodes = children.map do |child_spec|
174
- MockInnerNode.new(**child_spec)
175
- end
176
-
177
- inner = MockInnerNode.new(
178
- type: type,
179
- text: text,
180
- start_byte: start_byte,
181
- end_byte: end_byte,
182
- start_row: start_row,
183
- start_column: start_column,
184
- end_row: end_row,
185
- end_column: end_column,
186
- children: child_nodes,
187
- )
188
-
189
- # Create a real TreeHaver::Node wrapping our mock
190
- # Pass source so TreeHaver::Node can extract text if needed
191
- new(inner, source: source || text)
192
- end
193
-
194
- # Create multiple nodes from an array of specifications.
195
- #
196
- # @param specs [Array<Hash>] Array of node specifications
197
- # @return [Array<TestableNode>]
198
- def create_list(*specs)
199
- specs.flatten.map { |spec| create(**spec) }
200
- end
201
- end
202
-
203
- # Additional test helper methods
204
-
205
- # Check if this is a testable node (for test assertions)
206
- #
207
- # @return [Boolean] true
208
- def testable?
209
- true
210
- end
211
- end
212
- end
213
- end
214
-
215
- # Make TestableNode available at top level for convenience in specs.
216
- # This allows specs to use `TestableNode.create(...)` without the full namespace.
217
- TestableNode = TreeHaver::RSpec::TestableNode