ruby_tree_sitter 1.1.0-arm64-darwin-22 → 1.2.0-arm64-darwin-22
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
 - data/README.md +11 -7
 - data/ext/tree_sitter/language.c +1 -1
 - data/ext/tree_sitter/logger.c +2 -2
 - data/ext/tree_sitter/node.c +9 -4
 - data/ext/tree_sitter/parser.c +1 -1
 - data/lib/tree_sitter/node.rb +19 -51
 - data/lib/tree_sitter/tree_sitter.bundle +0 -0
 - data/lib/tree_sitter/version.rb +1 -1
 - data/lib/tree_stand/config.rb +8 -2
 - data/lib/tree_stand/node.rb +153 -18
 - data/lib/tree_stand/parser.rb +161 -5
 - metadata +2 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: be2da28a281bf2e2a9bce961b1d4462e3e676875759b93d729f843d96c3532fb
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: bde982ab0ec44b99dfd07ae8689c27f81c3bd09796bcf2dc709ef7d6cd25600b
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 6053ddef8804ba39a0fa7b1a22997656294a0433f64622132cdc64e35574f1287dad847167043456697a44130f4439ef1f4613f3eda034f4c0c014412dced730
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 693a7b319cf763e0be126478cc4ebd8ebe00307e9fd78939d6d68c96f8001abb0cf6dc20a9d2f671fadfbaa38513bdd7b8ebf17afee5defcaa6c29a3ff34e6dc
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,6 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Ruby tree-sitter bindings
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            [](https://faveod.github.io/ruby-tree-sitter)
         
     | 
| 
      
 4 
     | 
    
         
            +
            [](https://rubygems.org/gems/ruby_tree_sitter)
         
     | 
| 
      
 5 
     | 
    
         
            +
            [](https://github.com/Faveod/ruby-tree-sitter/actions/workflows/ci.yml)
         
     | 
| 
      
 6 
     | 
    
         
            +
            <!--
         
     | 
| 
      
 7 
     | 
    
         
            +
            [](https://github.com/Faveod/ruby-tree-sitter/actions/workflows/valgrind.yml)
         
     | 
| 
      
 8 
     | 
    
         
            +
            [](https://github.com/Faveod/ruby-tree-sitter/actions/workflows/asan.yml)
         
     | 
| 
      
 9 
     | 
    
         
            +
            -->
         
     | 
| 
       4 
10 
     | 
    
         | 
| 
       5 
11 
     | 
    
         
             
            Ruby bindings for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
         
     | 
| 
       6 
12 
     | 
    
         | 
| 
         @@ -63,8 +69,7 @@ This gem is a binding for `tree-sitter`. It doesn't have a version of 
     | 
|
| 
       63 
69 
     | 
    
         | 
| 
       64 
70 
     | 
    
         
             
            You must install `tree-sitter` and make sure that their dynamic library is
         
     | 
| 
       65 
71 
     | 
    
         
             
            accessible from `$PATH`, or build the gem with `--disable-sys-libs`, which will
         
     | 
| 
       66 
     | 
    
         
            -
            download the latest tagged `tree-sitter` and build against it (see [Build from
         
     | 
| 
       67 
     | 
    
         
            -
            source](docs/Contributing.md#build-from-source) .)
         
     | 
| 
      
 72 
     | 
    
         
            +
            download the latest tagged `tree-sitter` and build against it (see [Build from source](docs/Contributing.md#build-from-source) .)
         
     | 
| 
       68 
73 
     | 
    
         | 
| 
       69 
74 
     | 
    
         
             
            You can either install `tree-sitter` from source or through your go-to package manager.
         
     | 
| 
       70 
75 
     | 
    
         | 
| 
         @@ -133,8 +138,7 @@ bundle config set build.ruby_tree_sitter --disable-sys-libs 
     | 
|
| 
       133 
138 
     | 
    
         
             
            If you don't want to install from `rubygems`, `git`, or if you don't want to
         
     | 
| 
       134 
139 
     | 
    
         
             
            compile on install, then download a native gem from this repository's
         
     | 
| 
       135 
140 
     | 
    
         
             
            [releases](https://github.com/Faveod/ruby-tree-sitter/releases), or you can
         
     | 
| 
       136 
     | 
    
         
            -
            compile it yourself (see [Build from
         
     | 
| 
       137 
     | 
    
         
            -
            source](docs/Contributing.md#build-from-source) .)
         
     | 
| 
      
 141 
     | 
    
         
            +
            compile it yourself (see [Build from source](docs/Contributing.md#build-from-source) .)
         
     | 
| 
       138 
142 
     | 
    
         | 
| 
       139 
143 
     | 
    
         
             
            In that case, you'd have to point your `Gemfile` to the `gem` as such:
         
     | 
| 
       140 
144 
     | 
    
         | 
| 
         @@ -171,7 +175,7 @@ See `examples` directory. 
     | 
|
| 
       171 
175 
     | 
    
         | 
| 
       172 
176 
     | 
    
         
             
            ## Development
         
     | 
| 
       173 
177 
     | 
    
         | 
| 
       174 
     | 
    
         
            -
            See [`docs/ 
     | 
| 
      
 178 
     | 
    
         
            +
            See [`docs/Contributing.md`](docs/Contributing.md).
         
     | 
| 
       175 
179 
     | 
    
         | 
| 
       176 
180 
     | 
    
         
             
            ## 🚧 👷♀️ Notes 👷 🚧
         
     | 
| 
       177 
181 
     | 
    
         | 
| 
         @@ -186,7 +190,7 @@ don't copy them left and right, and then expect them to work without 
     | 
|
| 
       186 
190 
     | 
    
         
             
            `SEGFAULT`ing and creating a black-hole in your living-room.  Assume that you
         
     | 
| 
       187 
191 
     | 
    
         
             
            have to work locally with them. If you get a `SEGFAULT`, you can debug the
         
     | 
| 
       188 
192 
     | 
    
         
             
            native `C` code using `gdb`.  You can read more on `SEGFAULT`s
         
     | 
| 
       189 
     | 
    
         
            -
            [here](docs/SIGSEGV.md), and debugging [here](docs/Contributing.md 
     | 
| 
      
 193 
     | 
    
         
            +
            [here](docs/SIGSEGV.md), and debugging [here](docs/Contributing#Debugging.md).
         
     | 
| 
       190 
194 
     | 
    
         | 
| 
       191 
195 
     | 
    
         
             
            That said, we do aim at providing an idiomatic `Ruby` interface.  It should also
         
     | 
| 
       192 
196 
     | 
    
         
             
            provide a _safer_ interface, where you don't have to worry about when and how
         
     | 
    
        data/ext/tree_sitter/language.c
    CHANGED
    
    | 
         @@ -32,7 +32,7 @@ VALUE new_language(const TSLanguage *language) { 
     | 
|
| 
       32 
32 
     | 
    
         
             
             * with this gem.
         
     | 
| 
       33 
33 
     | 
    
         
             
             *
         
     | 
| 
       34 
34 
     | 
    
         
             
             * @param name [String] the parser's name.
         
     | 
| 
       35 
     | 
    
         
            -
             * @param path [String] the parser's shared library (so, dylib) path on disk.
         
     | 
| 
      
 35 
     | 
    
         
            +
             * @param path [String, Pathname] the parser's shared library (so, dylib) path on disk.
         
     | 
| 
       36 
36 
     | 
    
         
             
             *
         
     | 
| 
       37 
37 
     | 
    
         
             
             * @return [Language]
         
     | 
| 
       38 
38 
     | 
    
         
             
             */
         
     | 
    
        data/ext/tree_sitter/logger.c
    CHANGED
    
    | 
         @@ -154,9 +154,9 @@ static void logger_initialize_stderr(logger_t *logger) { 
     | 
|
| 
       154 
154 
     | 
    
         
             
             *
         
     | 
| 
       155 
155 
     | 
    
         
             
             * You can provide your proper backend. You have to make sure that it
         
     | 
| 
       156 
156 
     | 
    
         
             
             * exposes a +printf+, +puts+, or +write+ (lookup is done in that specific
         
     | 
| 
       157 
     | 
    
         
            -
             * order). {StringIO} is a perfect candidate.
         
     | 
| 
      
 157 
     | 
    
         
            +
             * order). {::StringIO} is a perfect candidate.
         
     | 
| 
       158 
158 
     | 
    
         
             
             *
         
     | 
| 
       159 
     | 
    
         
            -
             * You can also provide a format ({String}) if your backend supports a +printf+.
         
     | 
| 
      
 159 
     | 
    
         
            +
             * You can also provide a format ({::String}) if your backend supports a +printf+.
         
     | 
| 
       160 
160 
     | 
    
         
             
             *
         
     | 
| 
       161 
161 
     | 
    
         
             
             * @example
         
     | 
| 
       162 
162 
     | 
    
         
             
             *   backend = StringIO.new
         
     | 
    
        data/ext/tree_sitter/node.c
    CHANGED
    
    | 
         @@ -166,14 +166,19 @@ static VALUE node_child_by_field_id(VALUE self, VALUE field_id) { 
     | 
|
| 
       166 
166 
     | 
    
         
             
            /**
         
     | 
| 
       167 
167 
     | 
    
         
             
             * Get the node's child with the given field name.
         
     | 
| 
       168 
168 
     | 
    
         
             
             *
         
     | 
| 
       169 
     | 
    
         
            -
             * @param field_name [String]
         
     | 
| 
      
 169 
     | 
    
         
            +
             * @param field_name [String, Symbol]
         
     | 
| 
       170 
170 
     | 
    
         
             
             *
         
     | 
| 
       171 
171 
     | 
    
         
             
             * @return [Node]
         
     | 
| 
       172 
172 
     | 
    
         
             
             */
         
     | 
| 
       173 
173 
     | 
    
         
             
            static VALUE node_child_by_field_name(VALUE self, VALUE field_name) {
         
     | 
| 
       174 
     | 
    
         
            -
               
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
      
 174 
     | 
    
         
            +
              if (Qtrue == rb_funcall(self, rb_intern("field?"), 1, field_name)) {
         
     | 
| 
      
 175 
     | 
    
         
            +
                VALUE field_str = rb_funcall(field_name, rb_intern("to_s"), 0);
         
     | 
| 
      
 176 
     | 
    
         
            +
                const char *name = StringValuePtr(field_str);
         
     | 
| 
      
 177 
     | 
    
         
            +
                uint32_t length = (uint32_t)RSTRING_LEN(field_str);
         
     | 
| 
      
 178 
     | 
    
         
            +
                return new_node_by_val(ts_node_child_by_field_name(SELF, name, length));
         
     | 
| 
      
 179 
     | 
    
         
            +
              } else {
         
     | 
| 
      
 180 
     | 
    
         
            +
                return Qnil;
         
     | 
| 
      
 181 
     | 
    
         
            +
              }
         
     | 
| 
       177 
182 
     | 
    
         
             
            }
         
     | 
| 
       178 
183 
     | 
    
         | 
| 
       179 
184 
     | 
    
         
             
            /**
         
     | 
    
        data/ext/tree_sitter/parser.c
    CHANGED
    
    | 
         @@ -208,7 +208,7 @@ static VALUE parser_set_logger(VALUE self, VALUE logger) { 
     | 
|
| 
       208 
208 
     | 
    
         
             
             *    same arguments. Or you can start parsing from scratch by first calling
         
     | 
| 
       209 
209 
     | 
    
         
             
             *    {Parser#reset}.
         
     | 
| 
       210 
210 
     | 
    
         
             
             * 3. Parsing was cancelled using a cancellation flag that was set by an
         
     | 
| 
       211 
     | 
    
         
            -
             *    earlier call to { 
     | 
| 
      
 211 
     | 
    
         
            +
             *    earlier call to {Parser#cancellation_flag=}. You can resume parsing
         
     | 
| 
       212 
212 
     | 
    
         
             
             *    from where the parser left out by calling {Parser#parse} again with
         
     | 
| 
       213 
213 
     | 
    
         
             
             *    the same arguments.
         
     | 
| 
       214 
214 
     | 
    
         
             
             *
         
     | 
    
        data/lib/tree_sitter/node.rb
    CHANGED
    
    | 
         @@ -13,28 +13,17 @@ module TreeSitter 
     | 
|
| 
       13 
13 
     | 
    
         
             
                    @fields << name.to_sym if name
         
     | 
| 
       14 
14 
     | 
    
         
             
                  end
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                  @fields
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @fields.to_a
         
     | 
| 
       17 
17 
     | 
    
         
             
                end
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
                # @param field [String, Symbol]
         
     | 
| 
       19 
20 
     | 
    
         
             
                def field?(field)
         
     | 
| 
       20 
     | 
    
         
            -
                  fields.include?(field)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  fields.include?(field.to_sym)
         
     | 
| 
       21 
22 
     | 
    
         
             
                end
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                # FIXME: These APIs (`[]` and `fetch`) need absolute fixing.
         
     | 
| 
       24 
     | 
    
         
            -
                # 1. The documentation with the table doesn't work.
         
     | 
| 
       25 
     | 
    
         
            -
                # 1. The APIs are very confusing! Make them act similarly to Hash's
         
     | 
| 
       26 
     | 
    
         
            -
                #    `fetch` and `[]`.
         
     | 
| 
       27 
     | 
    
         
            -
                #    1. `[]` should take a single input and return nil if nothing found
         
     | 
| 
       28 
     | 
    
         
            -
                #       (no exceptions).
         
     | 
| 
       29 
     | 
    
         
            -
                #    1. `fetch` should should accept a single argument, potentially a
         
     | 
| 
       30 
     | 
    
         
            -
                #       default, and raise exception if no default was provided.
         
     | 
| 
       31 
     | 
    
         
            -
                #       Also allow for the `all:` kwarg.
         
     | 
| 
       32 
     | 
    
         
            -
                #    1. `values_at` takes many arguments.
         
     | 
| 
       33 
     | 
    
         
            -
                # And I don't think we can move to 1.0 without adressing them.
         
     | 
| 
       34 
     | 
    
         
            -
                #
         
     | 
| 
       35 
24 
     | 
    
         
             
                # Access node's named children.
         
     | 
| 
       36 
25 
     | 
    
         
             
                #
         
     | 
| 
       37 
     | 
    
         
            -
                # It's similar to {#fetch}, but  
     | 
| 
      
 26 
     | 
    
         
            +
                # It's similar to {#fetch}, but differs in input type, return values, and
         
     | 
| 
       38 
27 
     | 
    
         
             
                # the internal implementation.
         
     | 
| 
       39 
28 
     | 
    
         
             
                #
         
     | 
| 
       40 
29 
     | 
    
         
             
                # Both of these methods exist for separate use cases, but also because
         
     | 
| 
         @@ -60,15 +49,15 @@ module TreeSitter 
     | 
|
| 
       60 
49 
     | 
    
         
             
                # @return [Node | Array<Node>]
         
     | 
| 
       61 
50 
     | 
    
         
             
                def [](*keys)
         
     | 
| 
       62 
51 
     | 
    
         
             
                  case keys.length
         
     | 
| 
       63 
     | 
    
         
            -
                  when 0 then raise "#{self.class.name}##{__method__} requires a key."
         
     | 
| 
      
 52 
     | 
    
         
            +
                  when 0 then raise ArgumentError, "#{self.class.name}##{__method__} requires a key."
         
     | 
| 
       64 
53 
     | 
    
         
             
                  when 1
         
     | 
| 
       65 
54 
     | 
    
         
             
                    case k = keys.first
         
     | 
| 
       66 
     | 
    
         
            -
                    when  
     | 
| 
      
 55 
     | 
    
         
            +
                    when Integer then named_child(k)
         
     | 
| 
       67 
56 
     | 
    
         
             
                    when String, Symbol
         
     | 
| 
       68 
     | 
    
         
            -
                      raise "Cannot find field #{k}" unless fields.include?(k.to_sym)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      raise IndexError, "Cannot find field #{k}. Available: #{fields}" unless fields.include?(k.to_sym)
         
     | 
| 
       69 
58 
     | 
    
         | 
| 
       70 
59 
     | 
    
         
             
                      child_by_field_name(k.to_s)
         
     | 
| 
       71 
     | 
    
         
            -
                    else raise <<~ERR
         
     | 
| 
      
 60 
     | 
    
         
            +
                    else raise ArgumentError, <<~ERR
         
     | 
| 
       72 
61 
     | 
    
         
             
                      #{self.class.name}##{__method__} accepts Integer and returns named child at given index,
         
     | 
| 
       73 
62 
     | 
    
         
             
                          or a (String | Symbol) and returns the child by given field name.
         
     | 
| 
       74 
63 
     | 
    
         
             
                    ERR
         
     | 
| 
         @@ -139,7 +128,7 @@ module TreeSitter 
     | 
|
| 
       139 
128 
     | 
    
         | 
| 
       140 
129 
     | 
    
         
             
                # Access node's named children.
         
     | 
| 
       141 
130 
     | 
    
         
             
                #
         
     | 
| 
       142 
     | 
    
         
            -
                # It's similar to {# 
     | 
| 
      
 131 
     | 
    
         
            +
                # It's similar to {#[]}, but differs in input type, return values, and
         
     | 
| 
       143 
132 
     | 
    
         
             
                # the internal implementation.
         
     | 
| 
       144 
133 
     | 
    
         
             
                #
         
     | 
| 
       145 
134 
     | 
    
         
             
                # Both of these methods exist for separate use cases, but also because
         
     | 
| 
         @@ -159,39 +148,18 @@ module TreeSitter 
     | 
|
| 
       159 
148 
     | 
    
         
             
                # uses         named_child                   | field_name_for_child
         
     | 
| 
       160 
149 
     | 
    
         
             
                #              child_by_field_name           |   via each_node
         
     | 
| 
       161 
150 
     | 
    
         
             
                #              ------------------------------+----------------------
         
     | 
| 
       162 
     | 
    
         
            -
                # 
     | 
| 
       163 
     | 
    
         
            -
                #  
     | 
| 
       164 
     | 
    
         
            -
                 
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
       168 
     | 
    
         
            -
                   
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
                    dict[k.to_s] = i
         
     | 
| 
       171 
     | 
    
         
            -
                  end
         
     | 
| 
      
 151 
     | 
    
         
            +
                #
         
     | 
| 
      
 152 
     | 
    
         
            +
                # See {#[]}.
         
     | 
| 
      
 153 
     | 
    
         
            +
                def fetch(*keys)
         
     | 
| 
      
 154 
     | 
    
         
            +
                  keys = keys.map(&:to_s)
         
     | 
| 
      
 155 
     | 
    
         
            +
                  key_set = keys.to_set
         
     | 
| 
      
 156 
     | 
    
         
            +
                  fields = {}
         
     | 
| 
      
 157 
     | 
    
         
            +
                  each_field do |f, _c|
         
     | 
| 
      
 158 
     | 
    
         
            +
                    fields[f] = self[f] if key_set.delete(f)
         
     | 
| 
       172 
159 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
                  each_field do |f, c|
         
     | 
| 
       175 
     | 
    
         
            -
                    if dict.key?(f)
         
     | 
| 
       176 
     | 
    
         
            -
                      res[f] = c
         
     | 
| 
       177 
     | 
    
         
            -
                      dict.delete(f)
         
     | 
| 
       178 
     | 
    
         
            -
                    end
         
     | 
| 
       179 
     | 
    
         
            -
                    break if dict.empty?
         
     | 
| 
      
 160 
     | 
    
         
            +
                    break if key_set.empty?
         
     | 
| 
       180 
161 
     | 
    
         
             
                  end
         
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
                  res = keys.uniq.map { |k| res[k.to_s] }
         
     | 
| 
       183 
     | 
    
         
            -
                  res = res.compact if !all
         
     | 
| 
       184 
     | 
    
         
            -
                  res
         
     | 
| 
       185 
     | 
    
         
            -
                end
         
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
     | 
    
         
            -
                # Access all named children of a node, returning `nil` for missing ones.
         
     | 
| 
       188 
     | 
    
         
            -
                #
         
     | 
| 
       189 
     | 
    
         
            -
                # Equivalent to `fetch(…, all: true)`.
         
     | 
| 
       190 
     | 
    
         
            -
                #
         
     | 
| 
       191 
     | 
    
         
            -
                # See {#fetch}.
         
     | 
| 
       192 
     | 
    
         
            -
                def fetch_all(*keys, **kwargs)
         
     | 
| 
       193 
     | 
    
         
            -
                  kwargs[:all] = true
         
     | 
| 
       194 
     | 
    
         
            -
                  fetch(*keys, **kwargs)
         
     | 
| 
      
 162 
     | 
    
         
            +
                  fields.values_at(*keys)
         
     | 
| 
       195 
163 
     | 
    
         
             
                end
         
     | 
| 
       196 
164 
     | 
    
         
             
              end
         
     | 
| 
       197 
165 
     | 
    
         
             
            end
         
     | 
| 
         Binary file 
     | 
    
        data/lib/tree_sitter/version.rb
    CHANGED
    
    
    
        data/lib/tree_stand/config.rb
    CHANGED
    
    | 
         @@ -1,13 +1,19 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            # typed: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
      
 4 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       4 
6 
     | 
    
         
             
            module TreeStand
         
     | 
| 
       5 
7 
     | 
    
         
             
              # Global configuration for the gem.
         
     | 
| 
       6 
8 
     | 
    
         
             
              # @api private
         
     | 
| 
       7 
9 
     | 
    
         
             
              class Config
         
     | 
| 
       8 
10 
     | 
    
         
             
                extend T::Sig
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                sig { returns( 
     | 
| 
       11 
     | 
    
         
            -
                 
     | 
| 
      
 12 
     | 
    
         
            +
                sig { returns(T.nilable(Pathname)) }
         
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :parser_path
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def parser_path=(path)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @parser_path = Pathname(path)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
       12 
18 
     | 
    
         
             
              end
         
     | 
| 
       13 
19 
     | 
    
         
             
            end
         
     | 
    
        data/lib/tree_stand/node.rb
    CHANGED
    
    | 
         @@ -11,16 +11,60 @@ module TreeStand 
     | 
|
| 
       11 
11 
     | 
    
         
             
                extend Forwardable
         
     | 
| 
       12 
12 
     | 
    
         
             
                include Enumerable
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                # @!method changed?
         
     | 
| 
      
 15 
     | 
    
         
            +
                #   @return [Boolean] true if a syntax node has been edited.
         
     | 
| 
      
 16 
     | 
    
         
            +
                # @!method child_count
         
     | 
| 
      
 17 
     | 
    
         
            +
                #   @return [Integer] the number of child nodes.
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @!method extra?
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   @return [Boolean] true if the node is *extra* (e.g. comments).
         
     | 
| 
      
 20 
     | 
    
         
            +
                # @!method has_error?
         
     | 
| 
      
 21 
     | 
    
         
            +
                #   @return [Boolean] true if the node is a syntax error or contains any syntax errors.
         
     | 
| 
      
 22 
     | 
    
         
            +
                # @!method missing?
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   @return [Boolean] true if the parser inserted that node to recover from error.
         
     | 
| 
      
 24 
     | 
    
         
            +
                # @!method named?
         
     | 
| 
      
 25 
     | 
    
         
            +
                #   @return [Boolean] true if the node is not a literal in the grammar.
         
     | 
| 
      
 26 
     | 
    
         
            +
                # @!method named_child_count
         
     | 
| 
      
 27 
     | 
    
         
            +
                #   @return [Integer] the number of *named* children.
         
     | 
| 
       14 
28 
     | 
    
         
             
                # @!method type
         
     | 
| 
       15 
29 
     | 
    
         
             
                #   @return [Symbol] the type of the node in the tree-sitter grammar.
         
     | 
| 
       16 
30 
     | 
    
         
             
                # @!method error?
         
     | 
| 
       17 
31 
     | 
    
         
             
                #   @return [bool] true if the node is an error node.
         
     | 
| 
       18 
32 
     | 
    
         
             
                def_delegators(
         
     | 
| 
       19 
33 
     | 
    
         
             
                  :@ts_node,
         
     | 
| 
       20 
     | 
    
         
            -
                  : 
     | 
| 
      
 34 
     | 
    
         
            +
                  :changed?,
         
     | 
| 
      
 35 
     | 
    
         
            +
                  :child_count,
         
     | 
| 
       21 
36 
     | 
    
         
             
                  :error?,
         
     | 
| 
      
 37 
     | 
    
         
            +
                  :extra?,
         
     | 
| 
      
 38 
     | 
    
         
            +
                  :has_error?,
         
     | 
| 
      
 39 
     | 
    
         
            +
                  :missing?,
         
     | 
| 
      
 40 
     | 
    
         
            +
                  :named?,
         
     | 
| 
      
 41 
     | 
    
         
            +
                  :named_child_count,
         
     | 
| 
      
 42 
     | 
    
         
            +
                  :type,
         
     | 
| 
       22 
43 
     | 
    
         
             
                )
         
     | 
| 
       23 
44 
     | 
    
         | 
| 
      
 45 
     | 
    
         
            +
                # These are methods defined in {TreeStand::Node} but map to something
         
     | 
| 
      
 46 
     | 
    
         
            +
                # in {TreeSitter::Node}, because we want a more idiomatic API.
         
     | 
| 
      
 47 
     | 
    
         
            +
                THINLY_REMAPPED_METHODS = {
         
     | 
| 
      
 48 
     | 
    
         
            +
                  '[]': :[],
         
     | 
| 
      
 49 
     | 
    
         
            +
                  fetch: :fetch,
         
     | 
| 
      
 50 
     | 
    
         
            +
                  field: :child_by_field_name,
         
     | 
| 
      
 51 
     | 
    
         
            +
                  next: :next_sibling,
         
     | 
| 
      
 52 
     | 
    
         
            +
                  prev: :prev_sibling,
         
     | 
| 
      
 53 
     | 
    
         
            +
                  next_named: :next_named_sibling,
         
     | 
| 
      
 54 
     | 
    
         
            +
                  prev_named: :prev_named_sibling,
         
     | 
| 
      
 55 
     | 
    
         
            +
                  field_names: :fields,
         
     | 
| 
      
 56 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                # These are methods from {TreeSitter} that are thinly wrapped to create
         
     | 
| 
      
 59 
     | 
    
         
            +
                # {TreeStand::Node} instead.
         
     | 
| 
      
 60 
     | 
    
         
            +
                THINLY_WRAPPED_METHODS = (
         
     | 
| 
      
 61 
     | 
    
         
            +
                  %i[
         
     | 
| 
      
 62 
     | 
    
         
            +
                    child
         
     | 
| 
      
 63 
     | 
    
         
            +
                    named_child
         
     | 
| 
      
 64 
     | 
    
         
            +
                    parent
         
     | 
| 
      
 65 
     | 
    
         
            +
                  ] + THINLY_REMAPPED_METHODS.keys
         
     | 
| 
      
 66 
     | 
    
         
            +
                ).freeze
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
       24 
68 
     | 
    
         
             
                sig { returns(TreeStand::Tree) }
         
     | 
| 
       25 
69 
     | 
    
         
             
                attr_reader :tree
         
     | 
| 
       26 
70 
     | 
    
         | 
| 
         @@ -32,7 +76,6 @@ module TreeStand 
     | 
|
| 
       32 
76 
     | 
    
         
             
                def initialize(tree, ts_node)
         
     | 
| 
       33 
77 
     | 
    
         
             
                  @tree = tree
         
     | 
| 
       34 
78 
     | 
    
         
             
                  @ts_node = ts_node
         
     | 
| 
       35 
     | 
    
         
            -
                  @fields = @ts_node.each_field.to_a.map(&:first)
         
     | 
| 
       36 
79 
     | 
    
         
             
                end
         
     | 
| 
       37 
80 
     | 
    
         | 
| 
       38 
81 
     | 
    
         
             
                # TreeSitter uses a `TreeSitter::Cursor` to iterate over matches by calling
         
     | 
| 
         @@ -131,6 +174,74 @@ module TreeStand 
     | 
|
| 
       131 
174 
     | 
    
         
             
                  enumerator
         
     | 
| 
       132 
175 
     | 
    
         
             
                end
         
     | 
| 
       133 
176 
     | 
    
         | 
| 
      
 177 
     | 
    
         
            +
                # Enumerate named children.
         
     | 
| 
      
 178 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 179 
     | 
    
         
            +
                #   node.text # => "3 * 4"
         
     | 
| 
      
 180 
     | 
    
         
            +
                #
         
     | 
| 
      
 181 
     | 
    
         
            +
                # @example Iterate over the child nodes
         
     | 
| 
      
 182 
     | 
    
         
            +
                #   node.each_named do |child|
         
     | 
| 
      
 183 
     | 
    
         
            +
                #     print child.text
         
     | 
| 
      
 184 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 185 
     | 
    
         
            +
                #   # prints: 34
         
     | 
| 
      
 186 
     | 
    
         
            +
                #
         
     | 
| 
      
 187 
     | 
    
         
            +
                # @example Enumerable methods
         
     | 
| 
      
 188 
     | 
    
         
            +
                #   node.each_named.map(&:text) # => ["3", "4"]
         
     | 
| 
      
 189 
     | 
    
         
            +
                #
         
     | 
| 
      
 190 
     | 
    
         
            +
                # @yieldparam child [TreeStand::Node]
         
     | 
| 
      
 191 
     | 
    
         
            +
                sig do
         
     | 
| 
      
 192 
     | 
    
         
            +
                  params(block: T.nilable(T.proc.params(node: TreeStand::Node).returns(BasicObject)))
         
     | 
| 
      
 193 
     | 
    
         
            +
                    .returns(T::Enumerator[TreeStand::Node])
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
                def each_named(&block)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  enumerator = Enumerator.new do |yielder|
         
     | 
| 
      
 197 
     | 
    
         
            +
                    @ts_node.each_named do |child|
         
     | 
| 
      
 198 
     | 
    
         
            +
                      yielder << TreeStand::Node.new(@tree, child)
         
     | 
| 
      
 199 
     | 
    
         
            +
                    end
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                  enumerator.each(&block) if block_given?
         
     | 
| 
      
 202 
     | 
    
         
            +
                  enumerator
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                # Iterate of (field, child).
         
     | 
| 
      
 206 
     | 
    
         
            +
                #
         
     | 
| 
      
 207 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 208 
     | 
    
         
            +
                #   node.text # => "3 * 4"
         
     | 
| 
      
 209 
     | 
    
         
            +
                #
         
     | 
| 
      
 210 
     | 
    
         
            +
                # @example Iterate over the child nodes
         
     | 
| 
      
 211 
     | 
    
         
            +
                #   node.each_field do |field, child|
         
     | 
| 
      
 212 
     | 
    
         
            +
                #     puts "#{field}: #{child.text}"
         
     | 
| 
      
 213 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 214 
     | 
    
         
            +
                #   # prints:
         
     | 
| 
      
 215 
     | 
    
         
            +
                #   #  left: 3
         
     | 
| 
      
 216 
     | 
    
         
            +
                #   #  right: 4
         
     | 
| 
      
 217 
     | 
    
         
            +
                #
         
     | 
| 
      
 218 
     | 
    
         
            +
                # @example Enumerable methods
         
     | 
| 
      
 219 
     | 
    
         
            +
                #   node.each_field.map { |f, c| "#{f}: #{c}" } # => ["left: 3", "right: 4"]
         
     | 
| 
      
 220 
     | 
    
         
            +
                #
         
     | 
| 
      
 221 
     | 
    
         
            +
                # @yieldparam field [Symbol]
         
     | 
| 
      
 222 
     | 
    
         
            +
                # @yieldparam child [TreeStand::Node]
         
     | 
| 
      
 223 
     | 
    
         
            +
                sig do
         
     | 
| 
      
 224 
     | 
    
         
            +
                  params(block: T.nilable(T.proc.params(node: TreeStand::Node).returns(BasicObject)))
         
     | 
| 
      
 225 
     | 
    
         
            +
                    .returns(T::Enumerator[[Symbol, TreeStand::Node]])
         
     | 
| 
      
 226 
     | 
    
         
            +
                end
         
     | 
| 
      
 227 
     | 
    
         
            +
                def each_field(&block)
         
     | 
| 
      
 228 
     | 
    
         
            +
                  enumerator = Enumerator.new do |yielder|
         
     | 
| 
      
 229 
     | 
    
         
            +
                    @ts_node.each_field do |field, child|
         
     | 
| 
      
 230 
     | 
    
         
            +
                      yielder << [field.to_sym, TreeStand::Node.new(@tree, child)]
         
     | 
| 
      
 231 
     | 
    
         
            +
                    end
         
     | 
| 
      
 232 
     | 
    
         
            +
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
                  enumerator.each(&block) if block_given?
         
     | 
| 
      
 234 
     | 
    
         
            +
                  enumerator
         
     | 
| 
      
 235 
     | 
    
         
            +
                end
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
                # @example Enumerable methods
         
     | 
| 
      
 238 
     | 
    
         
            +
                #   node.named.map(&:text) # => ["3", "4"]
         
     | 
| 
      
 239 
     | 
    
         
            +
                alias_method :named, :each_named
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                # @example Enumerable methods
         
     | 
| 
      
 242 
     | 
    
         
            +
                #   node.fields.map { |f, c| "#{f}: #{c}" } # => ["left: 3", "right: 4"]
         
     | 
| 
      
 243 
     | 
    
         
            +
                alias_method :fields, :each_field
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
       134 
245 
     | 
    
         
             
                # (see TreeStand::Visitors::TreeWalker)
         
     | 
| 
       135 
246 
     | 
    
         
             
                # Backed by {TreeStand::Visitors::TreeWalker}.
         
     | 
| 
       136 
247 
     | 
    
         
             
                #
         
     | 
| 
         @@ -154,15 +265,6 @@ module TreeStand 
     | 
|
| 
       154 
265 
     | 
    
         
             
                  enumerator
         
     | 
| 
       155 
266 
     | 
    
         
             
                end
         
     | 
| 
       156 
267 
     | 
    
         | 
| 
       157 
     | 
    
         
            -
                # @example
         
     | 
| 
       158 
     | 
    
         
            -
                #   node.text # => "4"
         
     | 
| 
       159 
     | 
    
         
            -
                #   node.parent.text # => "3 * 4"
         
     | 
| 
       160 
     | 
    
         
            -
                #   node.parent.parent.text # => "1 + 3 * 4"
         
     | 
| 
       161 
     | 
    
         
            -
                sig { returns(TreeStand::Node) }
         
     | 
| 
       162 
     | 
    
         
            -
                def parent
         
     | 
| 
       163 
     | 
    
         
            -
                  TreeStand::Node.new(@tree, @ts_node.parent)
         
     | 
| 
       164 
     | 
    
         
            -
                end
         
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
268 
     | 
    
         
             
                # @example
         
     | 
| 
       167 
269 
     | 
    
         
             
                #   node.text # => "3 * 4"
         
     | 
| 
       168 
270 
     | 
    
         
             
                #   node.to_a.map(&:text) # => ["3", "*", "4"]
         
     | 
| 
         @@ -174,7 +276,7 @@ module TreeStand 
     | 
|
| 
       174 
276 
     | 
    
         
             
                # wraps the parent {TreeStand::Tree #tree} and has access to the source document.
         
     | 
| 
       175 
277 
     | 
    
         
             
                sig { returns(String) }
         
     | 
| 
       176 
278 
     | 
    
         
             
                def text
         
     | 
| 
       177 
     | 
    
         
            -
                  T.must(@tree.document 
     | 
| 
      
 279 
     | 
    
         
            +
                  T.must(@tree.document.byteslice(@ts_node.start_byte...@ts_node.end_byte))
         
     | 
| 
       178 
280 
     | 
    
         
             
                end
         
     | 
| 
       179 
281 
     | 
    
         | 
| 
       180 
282 
     | 
    
         
             
                # This class overrides the `method_missing` method to delegate to the
         
     | 
| 
         @@ -193,10 +295,20 @@ module TreeStand 
     | 
|
| 
       193 
295 
     | 
    
         
             
                #
         
     | 
| 
       194 
296 
     | 
    
         
             
                # @overload method_missing(method_name, *args, &block)
         
     | 
| 
       195 
297 
     | 
    
         
             
                #   @raise [NoMethodError]
         
     | 
| 
       196 
     | 
    
         
            -
                def method_missing(method, *args, &block)
         
     | 
| 
       197 
     | 
    
         
            -
                   
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
      
 298 
     | 
    
         
            +
                def method_missing(method, *args, **kwargs, &block)
         
     | 
| 
      
 299 
     | 
    
         
            +
                  if thinly_wrapped?(method)
         
     | 
| 
      
 300 
     | 
    
         
            +
                    from(
         
     | 
| 
      
 301 
     | 
    
         
            +
                      T.unsafe(@ts_node)
         
     | 
| 
      
 302 
     | 
    
         
            +
                        .public_send(
         
     | 
| 
      
 303 
     | 
    
         
            +
                          THINLY_REMAPPED_METHODS[method] || method,
         
     | 
| 
      
 304 
     | 
    
         
            +
                          *args,
         
     | 
| 
      
 305 
     | 
    
         
            +
                          **kwargs,
         
     | 
| 
      
 306 
     | 
    
         
            +
                          &block
         
     | 
| 
      
 307 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 308 
     | 
    
         
            +
                    )
         
     | 
| 
      
 309 
     | 
    
         
            +
                  else
         
     | 
| 
      
 310 
     | 
    
         
            +
                    super
         
     | 
| 
      
 311 
     | 
    
         
            +
                  end
         
     | 
| 
       200 
312 
     | 
    
         
             
                end
         
     | 
| 
       201 
313 
     | 
    
         | 
| 
       202 
314 
     | 
    
         
             
                sig { params(other: Object).returns(T::Boolean) }
         
     | 
| 
         @@ -217,8 +329,31 @@ module TreeStand 
     | 
|
| 
       217 
329 
     | 
    
         | 
| 
       218 
330 
     | 
    
         
             
                private
         
     | 
| 
       219 
331 
     | 
    
         | 
| 
       220 
     | 
    
         
            -
                def respond_to_missing?(method, *)
         
     | 
| 
       221 
     | 
    
         
            -
                   
     | 
| 
      
 332 
     | 
    
         
            +
                def respond_to_missing?(method, *_args, **_kwargs)
         
     | 
| 
      
 333 
     | 
    
         
            +
                  thinly_wrapped?(method) || super
         
     | 
| 
      
 334 
     | 
    
         
            +
                end
         
     | 
| 
      
 335 
     | 
    
         
            +
             
     | 
| 
      
 336 
     | 
    
         
            +
                def thinly_wrapped?(method)
         
     | 
| 
      
 337 
     | 
    
         
            +
                  @ts_node.fields.include?(method) || THINLY_WRAPPED_METHODS.include?(method)
         
     | 
| 
      
 338 
     | 
    
         
            +
                end
         
     | 
| 
      
 339 
     | 
    
         
            +
             
     | 
| 
      
 340 
     | 
    
         
            +
                # FIXME: Make more generic if needed in other classes.
         
     | 
| 
      
 341 
     | 
    
         
            +
             
     | 
| 
      
 342 
     | 
    
         
            +
                # 1 instance of {TreeStand} from a {TreeSitter} equivalent.
         
     | 
| 
      
 343 
     | 
    
         
            +
                def from_a(node)
         
     | 
| 
      
 344 
     | 
    
         
            +
                  node.is_a?(TreeSitter::Node) ? TreeStand::Node.new(@tree, node) : node
         
     | 
| 
      
 345 
     | 
    
         
            +
                end
         
     | 
| 
      
 346 
     | 
    
         
            +
             
     | 
| 
      
 347 
     | 
    
         
            +
                # {TreeSitter} instance, or a collection ({Array, Hash})
         
     | 
| 
      
 348 
     | 
    
         
            +
                def from(obj)
         
     | 
| 
      
 349 
     | 
    
         
            +
                  case obj
         
     | 
| 
      
 350 
     | 
    
         
            +
                  when Array
         
     | 
| 
      
 351 
     | 
    
         
            +
                    obj.map { |n| from(n) }
         
     | 
| 
      
 352 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 353 
     | 
    
         
            +
                    obj.to_h { |k, v| [from(k), from(v)] }
         
     | 
| 
      
 354 
     | 
    
         
            +
                  else
         
     | 
| 
      
 355 
     | 
    
         
            +
                    from_a(obj)
         
     | 
| 
      
 356 
     | 
    
         
            +
                  end
         
     | 
| 
       222 
357 
     | 
    
         
             
                end
         
     | 
| 
       223 
358 
     | 
    
         
             
              end
         
     | 
| 
       224 
359 
     | 
    
         
             
            end
         
     | 
    
        data/lib/tree_stand/parser.rb
    CHANGED
    
    | 
         @@ -1,6 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            # typed: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
      
 4 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       4 
6 
     | 
    
         
             
            module TreeStand
         
     | 
| 
       5 
7 
     | 
    
         
             
              # Wrapper around the TreeSitter parser. It looks up the parser by filename in
         
     | 
| 
       6 
8 
     | 
    
         
             
              # the configured parsers directory.
         
     | 
| 
         @@ -14,6 +16,14 @@ module TreeStand 
     | 
|
| 
       14 
16 
     | 
    
         
             
              #
         
     | 
| 
       15 
17 
     | 
    
         
             
              #   # Looks for a parser in `path/to/parser/folder/ruby.{so,dylib}`
         
     | 
| 
       16 
18 
     | 
    
         
             
              #   ruby_parser = TreeStand::Parser.new("ruby")
         
     | 
| 
      
 19 
     | 
    
         
            +
              #
         
     | 
| 
      
 20 
     | 
    
         
            +
              # If no {TreeStand::Config#parser_path} is setup, {TreeStand} will lookup in a
         
     | 
| 
      
 21 
     | 
    
         
            +
              # set of default paths.  You can always override any configuration by passing
         
     | 
| 
      
 22 
     | 
    
         
            +
              # the environment variable `TREE_SITTER_PARSERS` (colon-separated).
         
     | 
| 
      
 23 
     | 
    
         
            +
              #
         
     | 
| 
      
 24 
     | 
    
         
            +
              # @see language
         
     | 
| 
      
 25 
     | 
    
         
            +
              # @see search_for_lib
         
     | 
| 
      
 26 
     | 
    
         
            +
              # @see LIBDIRS
         
     | 
| 
       17 
27 
     | 
    
         
             
              class Parser
         
     | 
| 
       18 
28 
     | 
    
         
             
                extend T::Sig
         
     | 
| 
       19 
29 
     | 
    
         | 
| 
         @@ -23,14 +33,160 @@ module TreeStand 
     | 
|
| 
       23 
33 
     | 
    
         
             
                sig { returns(TreeSitter::Parser) }
         
     | 
| 
       24 
34 
     | 
    
         
             
                attr_reader :ts_parser
         
     | 
| 
       25 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
                # A colon-seprated list of paths pointing to directories that can contain parsers.
         
     | 
| 
      
 37 
     | 
    
         
            +
                # Order matters.
         
     | 
| 
      
 38 
     | 
    
         
            +
                # Takes precedence over default lookup paths.
         
     | 
| 
      
 39 
     | 
    
         
            +
                ENV_PARSERS =
         
     | 
| 
      
 40 
     | 
    
         
            +
                  ENV['TREE_SITTER_PARSERS']
         
     | 
| 
      
 41 
     | 
    
         
            +
                    &.split(':')
         
     | 
| 
      
 42 
     | 
    
         
            +
                    &.map { |v| Pathname(v) }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    .freeze
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                # The default paths we use to lookup parsers when no specific
         
     | 
| 
      
 46 
     | 
    
         
            +
                # {TreeStand::Config#parser_path} is {nil}.
         
     | 
| 
      
 47 
     | 
    
         
            +
                # Order matters.
         
     | 
| 
      
 48 
     | 
    
         
            +
                LIBDIRS = [
         
     | 
| 
      
 49 
     | 
    
         
            +
                  'tree-sitter-parsers',
         
     | 
| 
      
 50 
     | 
    
         
            +
                  '/opt/local/lib',
         
     | 
| 
      
 51 
     | 
    
         
            +
                  '/opt/lib',
         
     | 
| 
      
 52 
     | 
    
         
            +
                  '/usr/local/lib',
         
     | 
| 
      
 53 
     | 
    
         
            +
                  '/usr/lib',
         
     | 
| 
      
 54 
     | 
    
         
            +
                ].map { |p| Pathname(p) }.freeze
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # The library directories we need to look into.
         
     | 
| 
      
 57 
     | 
    
         
            +
                #
         
     | 
| 
      
 58 
     | 
    
         
            +
                # @return [Array<Pathname>] the list of candidate places to use when searching for parsers.
         
     | 
| 
      
 59 
     | 
    
         
            +
                #
         
     | 
| 
      
 60 
     | 
    
         
            +
                # @see ENV_PARSERS
         
     | 
| 
      
 61 
     | 
    
         
            +
                # @see LIBDIRS
         
     | 
| 
      
 62 
     | 
    
         
            +
                sig { returns(T::Array[Pathname]) }
         
     | 
| 
      
 63 
     | 
    
         
            +
                def self.lib_dirs = [
         
     | 
| 
      
 64 
     | 
    
         
            +
                  *ENV_PARSERS,
         
     | 
| 
      
 65 
     | 
    
         
            +
                  *(TreeStand.config.parser_path ? [TreeStand.config.parser_path] : LIBDIRS),
         
     | 
| 
      
 66 
     | 
    
         
            +
                ].compact
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                # The platform-specific extension of the parser.
         
     | 
| 
      
 69 
     | 
    
         
            +
                # @return [String] `dylib` or `so` for mac or linux.
         
     | 
| 
      
 70 
     | 
    
         
            +
                sig { returns(String) }
         
     | 
| 
      
 71 
     | 
    
         
            +
                def self.ext
         
     | 
| 
      
 72 
     | 
    
         
            +
                  case Gem::Platform.local.os
         
     | 
| 
      
 73 
     | 
    
         
            +
                  in   /darwin/ then 'dylib'
         
     | 
| 
      
 74 
     | 
    
         
            +
                  else               'so'
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                # Lookup a parser by name.
         
     | 
| 
      
 79 
     | 
    
         
            +
                #
         
     | 
| 
      
 80 
     | 
    
         
            +
                # Precedence:
         
     | 
| 
      
 81 
     | 
    
         
            +
                # 1. `Env['TREE_SITTER_PARSERS]`
         
     | 
| 
      
 82 
     | 
    
         
            +
                # 2. {TreeStand::Config#parser_path}
         
     | 
| 
      
 83 
     | 
    
         
            +
                # 3. {LIBDIRS}
         
     | 
| 
      
 84 
     | 
    
         
            +
                #
         
     | 
| 
      
 85 
     | 
    
         
            +
                # If a {TreeStand::Config#parser_path} is `nil`, {LIBDIRS} is used.
         
     | 
| 
      
 86 
     | 
    
         
            +
                # If a {TreeStand::Config#parser_path} is a {::Pathname}, {LIBDIRS} is ignored.
         
     | 
| 
      
 87 
     | 
    
         
            +
                sig { params(name: String).returns(T.nilable(Pathname)) }
         
     | 
| 
      
 88 
     | 
    
         
            +
                def self.search_for_lib(name)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  files = [
         
     | 
| 
      
 90 
     | 
    
         
            +
                    name,
         
     | 
| 
      
 91 
     | 
    
         
            +
                    "tree-sitter-#{name}",
         
     | 
| 
      
 92 
     | 
    
         
            +
                    "libtree-sitter-#{name}",
         
     | 
| 
      
 93 
     | 
    
         
            +
                  ].map { |v| "#{v}.#{ext}" }
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  res = lib_dirs
         
     | 
| 
      
 96 
     | 
    
         
            +
                    .product(files)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    .find do |dir, so|
         
     | 
| 
      
 98 
     | 
    
         
            +
                      path = dir / so
         
     | 
| 
      
 99 
     | 
    
         
            +
                      path = dir / name / so if !path.exist?
         
     | 
| 
      
 100 
     | 
    
         
            +
                      break path.expand_path if path.exist?
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
                  res.is_a?(Array) ? nil : res
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                # Generates a string message on where parser lookup happens.
         
     | 
| 
      
 106 
     | 
    
         
            +
                #
         
     | 
| 
      
 107 
     | 
    
         
            +
                # @return [String] A pretty message.
         
     | 
| 
      
 108 
     | 
    
         
            +
                sig { returns(String) }
         
     | 
| 
      
 109 
     | 
    
         
            +
                def self.search_lib_message
         
     | 
| 
      
 110 
     | 
    
         
            +
                  indent = 2
         
     | 
| 
      
 111 
     | 
    
         
            +
                  pretty = ->(arr) {
         
     | 
| 
      
 112 
     | 
    
         
            +
                    if arr
         
     | 
| 
      
 113 
     | 
    
         
            +
                      arr
         
     | 
| 
      
 114 
     | 
    
         
            +
                        .compact
         
     | 
| 
      
 115 
     | 
    
         
            +
                        .map { |v| "#{' ' * indent}#{v.expand_path}" }
         
     | 
| 
      
 116 
     | 
    
         
            +
                        .join("\n")
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
                  }
         
     | 
| 
      
 119 
     | 
    
         
            +
                  <<~MSG
         
     | 
| 
      
 120 
     | 
    
         
            +
                    From ENV['TREE_SITTER_PARSERS']:
         
     | 
| 
      
 121 
     | 
    
         
            +
                    #{pretty.call(ENV_PARSERS)}
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    From TreeStand.config.parser_path:
         
     | 
| 
      
 124 
     | 
    
         
            +
                    #{pretty.call([TreeStand.config.parser_path])}
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    From Defaults:
         
     | 
| 
      
 127 
     | 
    
         
            +
                    #{pretty.call(LIBDIRS)}
         
     | 
| 
      
 128 
     | 
    
         
            +
                  MSG
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                # Load a language from configuration or default lookup paths.
         
     | 
| 
      
 132 
     | 
    
         
            +
                #
         
     | 
| 
      
 133 
     | 
    
         
            +
                # @example Load java from default paths
         
     | 
| 
      
 134 
     | 
    
         
            +
                #    # This will look for:
         
     | 
| 
      
 135 
     | 
    
         
            +
                #    #
         
     | 
| 
      
 136 
     | 
    
         
            +
                #    #   tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 137 
     | 
    
         
            +
                #    #   /opt/local/lib/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 138 
     | 
    
         
            +
                #    #   /opt/lib/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 139 
     | 
    
         
            +
                #    #   /usr/local/lib/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 140 
     | 
    
         
            +
                #    #   /usr/lib/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 141 
     | 
    
         
            +
                #    #
         
     | 
| 
      
 142 
     | 
    
         
            +
                #    java = TreeStand::Parser.language('java')
         
     | 
| 
      
 143 
     | 
    
         
            +
                #
         
     | 
| 
      
 144 
     | 
    
         
            +
                # @example Load java from a configured path
         
     | 
| 
      
 145 
     | 
    
         
            +
                #    # This will look for:
         
     | 
| 
      
 146 
     | 
    
         
            +
                #    #
         
     | 
| 
      
 147 
     | 
    
         
            +
                #    #   /my/path/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 148 
     | 
    
         
            +
                #    #
         
     | 
| 
      
 149 
     | 
    
         
            +
                #    TreeStand.config.parser_path = '/my/path'
         
     | 
| 
      
 150 
     | 
    
         
            +
                #    java = TreeStand::Parser.language('java')
         
     | 
| 
      
 151 
     | 
    
         
            +
                #
         
     | 
| 
      
 152 
     | 
    
         
            +
                # @example Load java from environment variables
         
     | 
| 
      
 153 
     | 
    
         
            +
                #    # This will look for:
         
     | 
| 
      
 154 
     | 
    
         
            +
                #    #
         
     | 
| 
      
 155 
     | 
    
         
            +
                #    #   /my/forced/env/path/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 156 
     | 
    
         
            +
                #    #   /my/path/(java/)?(libtree-sitter-)?java.{ext}
         
     | 
| 
      
 157 
     | 
    
         
            +
                #    #
         
     | 
| 
      
 158 
     | 
    
         
            +
                #    # … and the same works for the default paths if `TreeStand.config.parser_path`
         
     | 
| 
      
 159 
     | 
    
         
            +
                #    # was `nil`
         
     | 
| 
      
 160 
     | 
    
         
            +
                #    ENV['TREE_SITTER_PARSERS'] = '/my/forced/env/path'
         
     | 
| 
      
 161 
     | 
    
         
            +
                #    TreeStand.config.parser_path = '/my/path'
         
     | 
| 
      
 162 
     | 
    
         
            +
                #    java = TreeStand::Parser.language('java')
         
     | 
| 
      
 163 
     | 
    
         
            +
                #
         
     | 
| 
      
 164 
     | 
    
         
            +
                # @param name [String] the name of the parser.
         
     | 
| 
      
 165 
     | 
    
         
            +
                #   This name is used to load the symbol from the compiled parser, replacing `-` with `_`.
         
     | 
| 
      
 166 
     | 
    
         
            +
                # @return [TreeSitter:language] a language object to use in your parsers.
         
     | 
| 
      
 167 
     | 
    
         
            +
                # @raise [RuntimeError] if the parser was not found.
         
     | 
| 
      
 168 
     | 
    
         
            +
                # @see search_for_lib
         
     | 
| 
      
 169 
     | 
    
         
            +
                sig { params(name: String).returns(TreeSitter::Language) }
         
     | 
| 
      
 170 
     | 
    
         
            +
                def self.language(name)
         
     | 
| 
      
 171 
     | 
    
         
            +
                  lib = search_for_lib(name)
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                  if lib.nil?
         
     | 
| 
      
 174 
     | 
    
         
            +
                    raise <<~MSG
         
     | 
| 
      
 175 
     | 
    
         
            +
                      Failed to load a parser for #{name}.
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                      #{search_lib_message}
         
     | 
| 
      
 178 
     | 
    
         
            +
                    MSG
         
     | 
| 
      
 179 
     | 
    
         
            +
                  end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                  # We know that the bindings will accept `lib`, but I don't know how to tell sorbet
         
     | 
| 
      
 182 
     | 
    
         
            +
                  # the types in ext/tree_sitter where `load` is defined.
         
     | 
| 
      
 183 
     | 
    
         
            +
                  TreeSitter::Language.load(name.gsub(/-/, '_'), T.unsafe(lib))
         
     | 
| 
      
 184 
     | 
    
         
            +
                end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
       26 
186 
     | 
    
         
             
                # @param language [String]
         
     | 
| 
       27 
187 
     | 
    
         
             
                sig { params(language: String).void }
         
     | 
| 
       28 
188 
     | 
    
         
             
                def initialize(language)
         
     | 
| 
       29 
     | 
    
         
            -
                  @ 
     | 
| 
       30 
     | 
    
         
            -
                  @ts_language = TreeSitter::Language.load(
         
     | 
| 
       31 
     | 
    
         
            -
                    language,
         
     | 
| 
       32 
     | 
    
         
            -
                    "#{TreeStand.config.parser_path}/#{language}.so",
         
     | 
| 
       33 
     | 
    
         
            -
                  )
         
     | 
| 
      
 189 
     | 
    
         
            +
                  @ts_language = Parser.language(language)
         
     | 
| 
       34 
190 
     | 
    
         
             
                  @ts_parser = TreeSitter::Parser.new.tap do |parser|
         
     | 
| 
       35 
191 
     | 
    
         
             
                    parser.language = @ts_language
         
     | 
| 
       36 
192 
     | 
    
         
             
                  end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: ruby_tree_sitter
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.2.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: arm64-darwin-22
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Firas al-Khalil
         
     | 
| 
         @@ -9,7 +9,7 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire:
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2024-05- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2024-05-27 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: sorbet-runtime
         
     |