ruby_tree_sitter 0.20.8.1 → 0.20.8.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94cb2ce80adc91e98ee79fb48b89a8f35b750b57b38272d0f1256a2641b36434
4
- data.tar.gz: a84e39a7e69d457f187ae20627a03a19476a6956ba05fe53d3088cd4ae899e22
3
+ metadata.gz: f897961a83176c564dd214e3b8c893880302aa9552cac65b9f0c2e06e396ba7c
4
+ data.tar.gz: 0ba1af8010641341f95bab2bc7182faa94181da5f862a4224ef5d87c44d1eff9
5
5
  SHA512:
6
- metadata.gz: c9d6f4150314194818f1d61030324b32ff763d2d0c799a76373ec6e4d3d1fc85b660c8848df867c73eafb3903ac426f684010465cedcbd1e1a11d2cfbc11efdc
7
- data.tar.gz: aeed7a952d3b640d8f9bc2c35abf73e05f3c0d921f13776ca938b6dbdaf21eaec97f617ee6984f399ab320c2066b383cc081aee287a3ac2123c6e0185ff5df74
6
+ metadata.gz: a3ff40e54f3d0b87984d14217f66a16f1c61a8c59c6a41d996e1792c513b2a8a3937f44839253d994d876bcbd6c1551b6c657b1cb6b6f3a0c84d752cf226da88
7
+ data.tar.gz: fac9fc60614e9ebd14e3181a4d2519cb4da9d170ea51326585204d3b65265349b3cdf964967c57d381bf795023c7b5e839ab406ba536ee5f971228efac9d3a5e
data/README.md CHANGED
@@ -85,24 +85,47 @@ brew install tree-sitter
85
85
 
86
86
  ## Install
87
87
 
88
- We haven't released the gem on `Rubygems` as of yet, but we'e planning on doing so.
88
+ From [rubygems](https://rubygems.org/gems/ruby_tree_sitter), in your `Gemfile`:
89
89
 
90
- Meanwhile, please install from `git` source, which will compile on installation.
90
+ ```ruby
91
+ gem 'ruby_tree_sitter', '~> 0.20.8.1'
92
+ ```
91
93
 
92
- If you don't want to install from `git`, or if you don't want to compile on
93
- install, download a native gem from this repository's
94
- [releases](https://github.com/Faveod/ruby-tree-sitter/releases), or you can
95
- compile it yourself (see [Build from
96
- source](docs/Development.md#build-from-source) .)
94
+ Or manually:
95
+
96
+ ```sh
97
+ gem install ruby_tree_sitter
98
+ ```
97
99
 
98
- ### Gemfile
100
+ Or from `git` sources, which will compile on installation:
99
101
 
100
102
  ```ruby
101
- gem 'tree_sitter', git: 'https://github.com/Faveod/ruby-tree-sitter'
103
+ gem 'ruby_tree_sitter', git: 'https://github.com/Faveod/ruby-tree-sitter'
104
+ ```
105
+
106
+ ### Disable system libraries
107
+
108
+ To install with `--disable-sys-lib`, you can either:
109
+
110
+ ```sh
111
+ gem install ruby_tree_sitter -- --disable-sys-libs
102
112
  ```
103
113
 
104
- If you chose to install a native gem, then you'd have to download it somewhere
105
- and then specify `path` as such:
114
+ Or via bundle:
115
+
116
+ ```sh
117
+ bundle config set build.ruby_tree_sitter --disable-sys-libs
118
+ ```
119
+
120
+ ### No compilation
121
+
122
+ If you don't want to install from `rubygems`, `git`, or if you don't want to
123
+ compile on install, then download a native gem from this repository's
124
+ [releases](https://github.com/Faveod/ruby-tree-sitter/releases), or you can
125
+ compile it yourself (see [Build from
126
+ source](docs/Development.md#build-from-source) .)
127
+
128
+ In that case, you'd have to point your `Gemfile` to the `gem` as such:
106
129
 
107
130
  ``` ruby
108
131
  gem 'tree_sitter', path: 'path/to/native/tree_sitter.gem'
@@ -118,6 +141,16 @@ You will have to install parsers yourself, either by:
118
141
  [Faveod/tree-sitter-parsers](https://github.com/Faveod/tree-sitter-parsers)
119
142
  which supports numerous architectures.
120
143
 
144
+ ### A note on static vs dynamic linking
145
+
146
+ This extension will statically link against a downloaded version of
147
+ `tree-sitter` when you use the `--disable-sys-lib`. So any installed version of
148
+ `tree-sitter` will not be loaded.
149
+
150
+ The native gems are also statically linked.
151
+
152
+ All other methods will dynamically link against the installed `tree-sitter`.
153
+
121
154
  ## Examples
122
155
 
123
156
  See `examples` directory.
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mkmf'
2
4
  require 'pathname'
3
5
 
6
+ require_relative 'repo'
7
+
4
8
  # ################################## #
5
9
  # Some helpers #
6
10
  # ################################## #
@@ -14,63 +18,39 @@ def system_tree_sitter?
14
18
  enable_config('sys-libs', true)
15
19
  end
16
20
 
17
- def sh cmd
18
- if !system(cmd)
19
- abort <<~MSG
20
-
21
- Failed to run: #{cmd}
22
-
23
- exiting…
24
-
25
- MSG
26
- end
27
- end
28
-
29
21
  def env_var_on?(var)
30
22
  %w[1 on true t yes y].include?(ENV.fetch(var, '').downcase)
31
23
  end
32
24
 
33
25
  # ################################## #
34
- # System lib #
35
- # #
36
- # OR #
37
- # #
38
- # Downloaded libs #
26
+ # System lib vs Downloaded lib #
39
27
  # ################################## #
40
28
 
41
29
  dir_include, dir_lib =
42
30
  if system_tree_sitter?
43
- [['/opt/include', '/opt/local/include', '/usr/include', '/usr/local/include'],
44
- ['/opt/lib', '/opt/local/lib', '/usr/lib', '/usr/local/lib']]
31
+ [
32
+ %w[/opt/include /opt/local/include /usr/include /usr/local/include],
33
+ %w[/opt/lib /opt/local/lib /usr/lib /usr/local/lib]
34
+ ]
45
35
  else
46
- src = Pathname.pwd / "tree-sitter-#{TreeSitter::VERSION}"
47
- if !Dir.exist? src
48
- if find_executable('git')
49
- sh "git clone https://github.com/tree-sitter/tree-sitter #{src}"
50
- sh "cd #{src} && git checkout tags/v#{TreeSitter::VERSION}"
51
- elsif find_executable('curl')
52
- if find_executable('tar')
53
- sh "curl -L https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{TreeSitter::VERSION}.tar.gz -o tree-sitter-v#{TreeSitter::VERSION}.tar.gz"
54
- sh "tar -xf tree-sitter-v#{TreeSitter::VERSION}.tar.gz"
55
- elsif find_executable('zip')
56
- sh "curl -L https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{TreeSitter::VERSION}.zip -o tree-sitter-v#{TreeSitter::VERSION}.zip"
57
- sh "unzip -q tree-sitter-v#{TreeSitter::VERSION}.zip"
58
- else
59
- abort('Could not find `tar` or `zip` (and `git` was not found!)')
60
- end
61
- else
62
- abort('Could not find `git` or `curl` to download tree-sitter and build from sources.')
63
- end
36
+ repo = TreeSitter::Repo.new
37
+ if !repo.download
38
+ msg = <<~MSG
39
+
40
+ Could not fetch tree-sitter sources:
41
+
42
+ #{repo.exe.map { |k, v| "#{k}: #{v}" }.join("\n")}
43
+
44
+ MSG
45
+ abort(msg)
64
46
  end
65
47
 
66
48
  # We need to make sure we're selecting the proper toolchain.
67
49
  # Especially needed for corss-compilation.
68
50
  ENV.store('CC', RbConfig::CONFIG['CC'])
69
- # We need to clean because the same folder is used over and over
70
- # by rake-compiler-dock
71
- sh "cd #{src} && make clean && make"
72
-
73
- [[src / 'lib' / 'include'], [src.to_s]]
51
+ repo.compile
52
+ repo.keep_static_lib
53
+ repo.include_and_lib_dirs
74
54
  end
75
55
 
76
56
  # ################################## #
@@ -78,7 +58,6 @@ dir_include, dir_lib =
78
58
  # ################################## #
79
59
 
80
60
  header = find_header('tree_sitter/api.h', *dir_include)
81
-
82
61
  library = find_library('tree-sitter', # libtree-sitter
83
62
  'ts_parser_new', # a symbol
84
63
  *dir_lib)
@@ -100,9 +79,7 @@ if !header || !library
100
79
  MSG
101
80
  end
102
81
 
103
- if env_var_on?('TREE_SITTER_PEDANTIC')
104
- cflags << '-Werror'
105
- end
82
+ cflags << '-Werror' if env_var_on?('TREE_SITTER_PEDANTIC')
106
83
 
107
84
  if env_var_on?('DEBUG')
108
85
  cflags << '-fbounds-check'
@@ -61,12 +61,12 @@ static void logger_payload_set(logger_t *logger, VALUE value) {
61
61
  if (rb_respond_to(logger->payload, rb_intern("printf"))) {
62
62
  logger->data.log = logger_log_printf;
63
63
  } else if (rb_respond_to(logger->payload, rb_intern("puts"))) {
64
- logger->data.log = &logger_log_puts;
64
+ logger->data.log = logger_log_puts;
65
65
  } else {
66
- logger->data.log = &logger_log_write;
66
+ logger->data.log = logger_log_write;
67
67
  }
68
68
  } else if (!NIL_P(logger->payload)) {
69
- logger->data.log = &logger_log_write;
69
+ logger->data.log = logger_log_write;
70
70
  }
71
71
  }
72
72
 
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/tree_sitter/version'
4
+
5
+ module TreeSitter
6
+ # Fetches tree-sitter sources.
7
+ class Repo
8
+ attr_reader :exe, :src, :url, :version
9
+
10
+ def initialize
11
+ @version = TREESITTER_VERSION
12
+
13
+ # `tree-sitter-@version` is the name produced by tagged releases of sources
14
+ # by git, so we use it everywhere, including when cloning from git.
15
+ @src = Pathname.pwd / "tree-sitter-#{@version}"
16
+
17
+ @url = {
18
+ git: 'https://github.com/tree-sitter/tree-sitter',
19
+ tar: "https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{@version}.tar.gz",
20
+ zip: "https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{@version}.zip"
21
+ }
22
+
23
+ @exe = {}
24
+ %i[curl git tar wget zip].each do |cmd|
25
+ @exe[cmd] = find_executable(cmd.to_s)
26
+ end
27
+ end
28
+
29
+ def compile
30
+ # We need to clean because the same folder is used over and over
31
+ # by rake-compiler-dock
32
+ sh "cd #{src} && make clean && make"
33
+ end
34
+
35
+ def exe?(name)
36
+ @exe[name]
37
+ end
38
+
39
+ def extract?
40
+ !exe.filter { |k, v| %i[tar zip].include?(k) && v }.empty?
41
+ end
42
+
43
+ def download
44
+ # TODO: should we force re-download? Maybe with a flag?
45
+ return true if Dir.exist? src
46
+
47
+ res = false
48
+ %w[git curl wget].each do |cmd|
49
+ res =
50
+ if find_executable(cmd)
51
+ send("sources_from_#{cmd}")
52
+ else
53
+ false
54
+ end
55
+ break if res
56
+ end
57
+
58
+ res
59
+ end
60
+
61
+ def include_and_lib_dirs
62
+ [[src / 'lib' / 'include'], [src.to_s]]
63
+ end
64
+
65
+ def keep_static_lib
66
+ src
67
+ .children
68
+ .filter { |f| /\.(dylib|so)/ =~ f.basename.to_s }
69
+ .each(&:unlink)
70
+ end
71
+
72
+ def sh(cmd)
73
+ return if system(cmd)
74
+
75
+ abort <<~MSG
76
+
77
+ Failed to run: #{cmd}
78
+
79
+ exiting …
80
+
81
+ MSG
82
+ end
83
+
84
+ def sources_from_curl
85
+ return false if !exe?(:curl) || !extract?
86
+
87
+ if exe?(:tar)
88
+ sh "curl -L #{url[:tar]} -o tree-sitter-v#{version}.tar.gz"
89
+ sh "tar -xf tree-sitter-v#{version}.tar.gz"
90
+ elsif exe?(:zip)
91
+ sh "curl -L #{url[:zip]} -o tree-sitter-v#{version}.zip"
92
+ sh "unzip -q tree-sitter-v#{version}.zip"
93
+ end
94
+
95
+ true
96
+ end
97
+
98
+ def sources_from_git
99
+ return false if !exe?(:git)
100
+
101
+ sh "git clone #{url[:git]} #{src}"
102
+ sh "cd #{src} && git checkout tags/v#{version}"
103
+
104
+ true
105
+ end
106
+
107
+ def sources_from_wget
108
+ return false if !exe?(:wget) || !extract?
109
+
110
+ if exe?(:tar)
111
+ sh "wget #{url[:tar]} -O tree-sitter-v#{version}.tar.gz"
112
+ sh "tar -xf tree-sitter-v#{version}.tar.gz"
113
+ elsif exe?(:zip)
114
+ sh "wget #{url[:zip]} -O tree-sitter-v#{version}.zip"
115
+ sh "unzip -q tree-sitter-v#{version}.zip"
116
+ end
117
+
118
+ true
119
+ end
120
+ end
121
+ end
@@ -76,7 +76,6 @@ module TreeSitter
76
76
  end
77
77
 
78
78
  def respond_to_missing?(*args)
79
- init_fields
80
79
  args.length == 1 && fields.include?(args[0])
81
80
  end
82
81
 
@@ -143,7 +142,12 @@ module TreeSitter
143
142
  # uses named_child | field_name_for_child
144
143
  # child_by_field_name | via each_node
145
144
  # ------------------------------+----------------------
146
- def fetch(*keys)
145
+ # @param all [Boolean] If `true`, return an array of nodes for all the
146
+ # demanded keys, putting `nil` for missing ones. If `false`, return the
147
+ # same array after calling `compact`. Defaults to `false`.
148
+ #
149
+ # See {#fetch_all}.
150
+ def fetch(*keys, all: false, **_kwargs)
147
151
  dict = {}
148
152
  keys.each.with_index do |k, i|
149
153
  dict[k.to_s] = i
@@ -152,13 +156,25 @@ module TreeSitter
152
156
  res = {}
153
157
  each_field do |f, c|
154
158
  if dict.key?(f)
155
- res[dict[f]] = c
159
+ res[f] = c
156
160
  dict.delete(f)
157
161
  end
158
162
  break if dict.empty?
159
163
  end
160
164
 
161
- res.sort.map { |_, v| v }
165
+ res = keys.uniq.map { |k| res[k.to_s] }
166
+ res = res.compact if !all
167
+ res
168
+ end
169
+
170
+ # Access all named children of a node, returning `nil` for missing ones.
171
+ #
172
+ # Equivalent to `fetch(…, all: true)`.
173
+ #
174
+ # See {#fetch}.
175
+ def fetch_all(*keys, **kwargs)
176
+ kwargs[:all] = true
177
+ fetch(*keys, **kwargs)
162
178
  end
163
179
  end
164
180
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TreeSitter
4
- VERSION = '0.20.8.1'
4
+ TREESITTER_VERSION = '0.20.8'
5
+ VERSION = "#{TREESITTER_VERSION}.3".freeze
5
6
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../test_helper'
4
+
5
+ js = TreeSitter.lang('javascript')
6
+ parser = TreeSitter::Parser.new
7
+ parser.language = js
8
+
9
+ program = <<~JS
10
+ let a = 42;
11
+ JS
12
+
13
+ describe 'loading a language' do
14
+ before do
15
+ parser.reset
16
+ end
17
+
18
+ it 'must set/get the same language' do
19
+ parser.language = js
20
+ assert_equal js, parser.language
21
+ end
22
+ end
23
+
24
+ describe 'parse_string' do
25
+ before do
26
+ parser.reset
27
+ end
28
+
29
+ it 'must parse nil' do
30
+ res = parser.parse_string(nil, nil)
31
+ assert_nil res
32
+ end
33
+
34
+ [
35
+ ['empty', '', 0],
36
+ ['valid', program, 1],
37
+ # ['invalid', margorp, 3]
38
+ ].each do |q, p, c|
39
+ it "must parse #{q} programs" do
40
+ res = parser.parse_string(nil, p)
41
+ assert_instance_of TreeSitter::Tree, res
42
+
43
+ root = res.root_node
44
+ assert_instance_of TreeSitter::Node, root
45
+ assert_equal c, root.child_count
46
+ end
47
+ end
48
+ end
@@ -27,7 +27,12 @@ describe 'language' do
27
27
  if p = ENV.fetch('TREE_SITTER_PARSERS', nil)
28
28
  Pathname(p) / "libtree-sitter-ruby.#{TreeSitter.ext}"
29
29
  else
30
- Pathname('tree-sitter-parsers') / 'ruby' / "libtree-sitter-ruby.#{TreeSitter.ext}"
30
+ downloaded = Pathname('tree-sitter-parsers') / "libtree-sitter-ruby.#{TreeSitter.ext}"
31
+ if !downloaded.exist?
32
+ Pathname('tree-sitter-parsers') / "ruby" / "libtree-sitter-ruby.#{TreeSitter.ext}"
33
+ else
34
+ downloaded
35
+ end
31
36
  end
32
37
  ll = TreeSitter::Language.load('ruby', path)
33
38
  assert ll.field_count.positive?
@@ -62,6 +62,7 @@ describe 'logging' do
62
62
  backend = StringIO.new
63
63
  parser.logger = TreeSitter::Logger.new(backend, "%s#{delim}%s")
64
64
  parser.parse_string(nil, program)
65
+ refute_empty backend.string, 'the backend should be filled with logs'
65
66
  backend.each_line do |l|
66
67
  assert (/#{delim}/ =~ l), 'delimiter must be in every single line'
67
68
  end
@@ -352,4 +352,60 @@ describe 'fetch' do
352
352
  assert_equal 1, m.length
353
353
  assert_equal method, m.first
354
354
  end
355
+
356
+ it 'should return an empty array when asked for non-existent fields' do
357
+ b = @child.fetch(:a)
358
+ assert_empty b
359
+
360
+ b = @child.fetch(:b, :focus)
361
+ assert_empty b
362
+ end
363
+
364
+ it 'should return an array of `nil` values when asked for non-existent fields with `all: true`' do
365
+ b = @child.fetch(:d, all: true)
366
+ refute_empty b
367
+ assert b.all?(&:nil?)
368
+
369
+ b = @child.fetch(:e, :f, all: true)
370
+ refute_empty b
371
+ assert b.all?(&:nil?)
372
+ end
373
+
374
+ it 'should return values, even if `nil`, for all keys when `all: true`' do
375
+ method = @child.child(0)
376
+ arguments = @child.child(1)
377
+
378
+ m, a, f = @child.fetch(:method, :arguments, :fake, all: true)
379
+
380
+ assert_equal method, m
381
+ assert_equal arguments, a
382
+ assert_nil f
383
+ end
384
+ end
385
+
386
+ describe 'fetch_all' do
387
+ before do
388
+ @child = root.child(0).child(4)
389
+ end
390
+
391
+ it 'should return an array of `nil` values when asked for non-existent fields' do
392
+ b = @child.fetch_all(:d)
393
+ refute_empty b
394
+ assert b.all?(&:nil?)
395
+
396
+ b = @child.fetch_all(:e, :f)
397
+ refute_empty b
398
+ assert b.all?(&:nil?)
399
+ end
400
+
401
+ it 'should return values, even if `nil`, for all keys' do
402
+ method = @child.child(0)
403
+ arguments = @child.child(1)
404
+
405
+ m, a, f = @child.fetch_all(:method, :arguments, :fake)
406
+
407
+ assert_equal method, m
408
+ assert_equal arguments, a
409
+ assert_nil f
410
+ end
355
411
  end
data/tree_sitter.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ $LOAD_PATH.unshift(lib) if !$LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'tree_sitter/version'
7
7
 
@@ -17,8 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.version = TreeSitter::VERSION
18
18
 
19
19
  spec.extensions = %(ext/tree_sitter/extconf.rb)
20
- spec.files = %w(LICENSE README.md tree_sitter.gemspec)
21
- spec.files += Dir.glob('ext/**/*.[ch]')
20
+ spec.files = %w[LICENSE README.md tree_sitter.gemspec]
21
+ spec.files += Dir.glob('ext/**/*.{c,h,rb}')
22
22
  spec.files += Dir.glob('lib/**/*.rb')
23
23
  spec.test_files = Dir.glob('test/**/*')
24
24
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_tree_sitter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.8.1
4
+ version: 0.20.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Firas al-Khalil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-20 00:00:00.000000000 Z
11
+ date: 2023-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -136,6 +136,7 @@ files:
136
136
  - ext/tree_sitter/query_match.c
137
137
  - ext/tree_sitter/query_predicate_step.c
138
138
  - ext/tree_sitter/range.c
139
+ - ext/tree_sitter/repo.rb
139
140
  - ext/tree_sitter/symbol_type.c
140
141
  - ext/tree_sitter/tree.c
141
142
  - ext/tree_sitter/tree_cursor.c
@@ -146,6 +147,7 @@ files:
146
147
  - lib/tree_sitter/version.rb
147
148
  - test/README.md
148
149
  - test/test_helper.rb
150
+ - test/tree_sitter/js_test.rb
149
151
  - test/tree_sitter/language_test.rb
150
152
  - test/tree_sitter/logger_test.rb
151
153
  - test/tree_sitter/node_test.rb
@@ -180,6 +182,7 @@ summary: Ruby bindings for Tree-Sitter
180
182
  test_files:
181
183
  - test/README.md
182
184
  - test/test_helper.rb
185
+ - test/tree_sitter/js_test.rb
183
186
  - test/tree_sitter/language_test.rb
184
187
  - test/tree_sitter/logger_test.rb
185
188
  - test/tree_sitter/node_test.rb