Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*.gemfile
*.lock
*.log
*.o
*.so

logs
Expand All @@ -15,3 +16,9 @@ logs
/tree-sitter
/tree-sitter-parsers
/vendor

# Build artifacts in ext/tree_sitter/
/ext/tree_sitter/Makefile
/ext/tree_sitter/extconf.h
/ext/tree_sitter/tree-sitter-*/

9 changes: 3 additions & 6 deletions .justfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
GEM_NAME := 'tree_sitter'
LIB := 'lib'
LIB_FILE := LIB / GEM_NAME + '.rb'
VERSION_FILE := LIB / GEM_NAME / 'version.rb'
VERSION := shell("ruby -r ./" + VERSION_FILE + " -e 'puts TreeSitter::VERSION'")
TS_PARSER_VERSION := '4.8'

default: check
Expand All @@ -22,7 +17,9 @@ compile *args:

[group('tree-sitter')]
dl-parsers platform:
curl -o tree-sitter-parsers.zip -L https://github.com/Faveod/tree-sitter-parsers/releases/download/v{{TS_PARSER_VERSION}}/tree-sitter-parsers-{{TS_PARSER_VERSION}}-{{platform}}.zip
curl \
-o tree-sitter-parsers.zip \
-L https://github.com/Faveod/tree-sitter-parsers/releases/download/v{{TS_PARSER_VERSION}}/tree-sitter-parsers-{{TS_PARSER_VERSION}}-{{platform}}.zip
unzip tree-sitter-parsers.zip

[group('doc')]
Expand Down
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ Style/BlockDelimiters:
Style/DoubleNegation:
Enabled: false

Style/ArgumentsForwarding:
Exclude:
- lib/tree_stand.rb
- lib/tree_stand/*

Style/FormatString:
Enabled: false

Expand Down
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ source 'https://rubygems.org'
gemspec

group :development, :test do
gem 'bundler', '~> 2.3'
gem 'debug', '~> 1.9', require: false
gem 'gemstash', require: false
gem 'ostruct'
gem 'rake', '~> 13.2'
gem 'rake-compiler', '~> 1.2'
gem 'rake-compiler-dock', '~> 1.5'
gem 'rubocop', '~> 1.39.0', require: false
gem 'rubocop'
gem 'ruby-lsp', '~> 0.17', require: false
gem 'ruby_memcheck', '~> 1.3'
gem 'tapioca', '~> 0.11', require: false
Expand Down
18 changes: 17 additions & 1 deletion News.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# News

# [unreleaseed]
# [unreleased]

## API Changes for tree-sitter 0.26.3 compatibility

- Updated to tree-sitter v0.26.3
- **Breaking Changes in tree-sitter 0.26.x API**:
- `ts_language_version` renamed to `ts_language_abi_version`
- `TSInputEncodingUTF16` split into `TSInputEncodingUTF16LE` and `TSInputEncodingUTF16BE`
(now using UTF16LE as default for backward compatibility)
- Cancellation flag API (`ts_parser_cancellation_flag`, `ts_parser_set_cancellation_flag`) removed
- `Parser#cancellation_flag` and `Parser#cancellation_flag=` are now no-ops for backward compatibility
- Timeout API (`ts_parser_timeout_micros`, `ts_parser_set_timeout_micros`) removed
- `Parser#timeout_micros` and `Parser#timeout_micros=` are now no-ops for backward compatibility
- Use `TSParseOptions` with `progress_callback` for cancellation/timeout functionality in 0.26+
- `TREE_SITTER_LANGUAGE_VERSION` is now 15 (was 14)
- `TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION` is now 13 (was 6)
- Grammar files (.so) must be built against tree-sitter 0.26+ to work with this version

# v2.0.0 (22-01-2025)

Expand Down
3 changes: 2 additions & 1 deletion ext/tree_sitter/encoding.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ TSInputEncoding value_to_encoding(VALUE encoding) {

// NOTE: should we emit a warning instead of defaulting to UTF8?
if (enc == u16) {
return TSInputEncodingUTF16;
// tree-sitter 0.26+ split UTF16 into UTF16LE and UTF16BE
return TSInputEncodingUTF16LE;
} else {
return TSInputEncodingUTF8;
}
Expand Down
36 changes: 19 additions & 17 deletions ext/tree_sitter/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,25 +121,27 @@ def env_var_on?(var)
$warnflags.gsub!(/#{r}/, '')
end

cflags.concat %W[
-fms-extensions
-fdeclspec
-fsanitize=#{sanitizers}
-fsanitize-blacklist=../../../../.asanignore
-fsanitize-recover=#{sanitizers}
-fno-sanitize-recover=all
-fno-sanitize=null
-fno-sanitize=alignment
-fno-omit-frame-pointer
]

ldflags.concat %W[
-fsanitize=#{sanitizers}
-dynamic-libasan
]
cflags.push(*
%W[
-fms-extensions
-fdeclspec
-fsanitize=#{sanitizers}
-fsanitize-blacklist=../../../../.asanignore
-fsanitize-recover=#{sanitizers}
-fno-sanitize-recover=all
-fno-sanitize=null
-fno-sanitize=alignment
-fno-omit-frame-pointer
])

ldflags.push(*
%W[
-fsanitize=#{sanitizers}
-dynamic-libasan
])
end

cflags.concat %w[-std=c99 -fPIC -Wall]
cflags.push(*%w[-std=c99 -fPIC -Wall])

append_cflags(cflags)
append_ldflags(ldflags)
Expand Down
32 changes: 21 additions & 11 deletions ext/tree_sitter/language.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
#include <stdint.h>
#include <stdio.h>

typedef TSLanguage *(tree_sitter_lang)(void);
// tree-sitter 0.26+ returns const TSLanguage*
typedef const TSLanguage *(tree_sitter_lang)(void);
const char *tree_sitter_prefix = "tree_sitter_";

extern VALUE mTreeSitter;
Expand Down Expand Up @@ -40,26 +41,32 @@ static VALUE language_load(VALUE self, VALUE name, VALUE path) {
VALUE path_s = rb_funcall(path, rb_intern("to_s"), 0);
char *path_cstr = StringValueCStr(path_s);
void *lib = dlopen(path_cstr, RTLD_NOW);
const char *err = dlerror();
if (err != NULL) {
if (lib == NULL) {
const char *err = dlerror();
VALUE parser_not_found = rb_const_get(mTreeSitter, rb_intern("ParserNotFoundError"));
rb_raise(parser_not_found,
"Could not load shared library `%s'.\nReason: %s", path_cstr, err);
"Could not load shared library `%s'.\nReason: %s", path_cstr, err ? err : "unknown error");
}

char buf[256];
snprintf(buf, sizeof(buf), "tree_sitter_%s", StringValueCStr(name));

// Clear any previous error before dlsym (POSIX requirement)
dlerror();

tree_sitter_lang *make_ts_language = dlsym(lib, buf);
err = dlerror();
if (err != NULL) {

// Only check dlerror if dlsym returned NULL
if (make_ts_language == NULL) {
const char *err = dlerror();
dlclose(lib);
VALUE symbol_not_found = rb_const_get(mTreeSitter, rb_intern("SymbolNotFoundError"));
rb_raise(symbol_not_found,
"Could not load symbol `%s' from library `%s'.\nReason:%s",
StringValueCStr(name), path_cstr, err);
"Could not load symbol `%s' from library `%s'.\nReason: %s",
buf, path_cstr, err ? err : "symbol not found");
}

TSLanguage *lang = make_ts_language();
const TSLanguage *lang = make_ts_language();
if (lang == NULL) {
dlclose(lib);
VALUE language_load_error = rb_const_get(mTreeSitter, rb_intern("LanguageLoadError"));
Expand All @@ -69,7 +76,8 @@ static VALUE language_load(VALUE self, VALUE name, VALUE path) {
StringValueCStr(name), path_cstr);
}

uint32_t version = ts_language_version(lang);
// tree-sitter 0.26+ renamed ts_language_version to ts_language_abi_version
uint32_t version = ts_language_abi_version(lang);
if (version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) {
VALUE version_error = rb_const_get(mTreeSitter, rb_intern("ParserVersionError"));
rb_raise(version_error,
Expand Down Expand Up @@ -194,7 +202,8 @@ static VALUE language_symbol_type(VALUE self, VALUE symbol) {
* @see Parser#language=
*/
static VALUE language_version(VALUE self) {
return UINT2NUM(ts_language_version(SELF));
// tree-sitter 0.26+ renamed ts_language_version to ts_language_abi_version
return UINT2NUM(ts_language_abi_version(SELF));
}

void init_language(void) {
Expand All @@ -220,4 +229,5 @@ void init_language(void) {
rb_define_method(cLanguage, "symbol_name", language_symbol_name, 1);
rb_define_method(cLanguage, "symbol_type", language_symbol_type, 1);
rb_define_method(cLanguage, "version", language_version, 0);
rb_define_method(cLanguage, "abi_version", language_version, 0);
}
41 changes: 10 additions & 31 deletions ext/tree_sitter/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,14 @@ static VALUE parser_allocate(VALUE klass) {
* Get the parser's current cancellation flag pointer.
*
* @return [Integer]
*
* @note DEPRECATED in tree-sitter 0.26+. This API was removed.
* Use TSParseOptions with progress_callback instead.
*/
static VALUE parser_get_cancellation_flag(VALUE self) {
return SIZET2NUM(*ts_parser_cancellation_flag(SELF));
// tree-sitter 0.26+ removed cancellation_flag API
// Return the stored value for backward compatibility
return SIZET2NUM(unwrap(self)->cancellation_flag);
}

/**
Expand All @@ -58,13 +63,15 @@ static VALUE parser_get_cancellation_flag(VALUE self) {
*
* @see parse
*
* @note This is not well-tested in the bindings.
* @note DEPRECATED in tree-sitter 0.26+. This API was removed.
* Use TSParseOptions with progress_callback instead.
*
* @return nil
*/
static VALUE parser_set_cancellation_flag(VALUE self, VALUE flag) {
// tree-sitter 0.26+ removed cancellation_flag API
// Store the value for backward compatibility but it won't affect parsing
unwrap(self)->cancellation_flag = NUM2SIZET(flag);
ts_parser_set_cancellation_flag(SELF, &unwrap(self)->cancellation_flag);
return Qnil;
}

Expand Down Expand Up @@ -345,32 +352,6 @@ static VALUE parser_reset(VALUE self) {
return Qnil;
}

/**
* Get the duration in microseconds that parsing is allowed to take.
*
* @return [Integer]
*/
static VALUE parser_get_timeout_micros(VALUE self) {
return ULL2NUM(ts_parser_timeout_micros(SELF));
}

/**
* Set the maximum duration in microseconds that parsing should be allowed to
* take before halting.
*
* If parsing takes longer than this, it will halt early, returning +nil+.
*
* @see parse
*
* @param timeout [Integer]
*
* @return [nil]
*/
static VALUE parser_set_timeout_micros(VALUE self, VALUE timeout) {
ts_parser_set_timeout_micros(SELF, NUM2ULL(timeout));
return Qnil;
}

void init_parser(void) {
cParser = rb_define_class_under(mTreeSitter, "Parser", rb_cObject);

Expand All @@ -393,6 +374,4 @@ void init_parser(void) {
parser_parse_string_encoding, 3);
rb_define_method(cParser, "print_dot_graphs", parser_print_dot_graphs, 1);
rb_define_method(cParser, "reset", parser_reset, 0);
rb_define_method(cParser, "timeout_micros", parser_get_timeout_micros, 0);
rb_define_method(cParser, "timeout_micros=", parser_set_timeout_micros, 1);
}
10 changes: 5 additions & 5 deletions ext/tree_sitter/repo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def exe?(name)
end

def extract?
!exe.filter { |k, v| %i[tar zip].include?(k) && v }.empty?
exe.any? { |k, v| %i[tar zip].include?(k) && v }
end

def download
Expand All @@ -50,7 +50,7 @@ def download
%w[git curl wget].each do |cmd|
res =
if find_executable(cmd)
send("sources_from_#{cmd}")
send("sources_from_#{cmd}?")
else
false
end
Expand All @@ -76,7 +76,7 @@ def sh(cmd)
MSG
end

def sources_from_curl
def sources_from_curl?
return false if !exe?(:curl) || !extract?

if exe?(:tar)
Expand All @@ -90,7 +90,7 @@ def sources_from_curl
true
end

def sources_from_git
def sources_from_git?
return false if !exe?(:git)

sh <<~SHELL.chomp
Expand All @@ -104,7 +104,7 @@ def sources_from_git
true
end

def sources_from_wget
def sources_from_wget?
return false if !exe?(:wget) || !extract?

if exe?(:tar)
Expand Down
6 changes: 1 addition & 5 deletions lib/tree_sitter/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ def process(source) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexi
pattern_count.times do |i| # rubocop:disable Metrics/BlockLength
predicate_steps = predicates_for_pattern(i)
byte_offset = start_byte_for_pattern(i)
row =
source.chars.map.with_index
.take_while { |_, i| i < byte_offset } # rubocop:disable Lint/ShadowingOuterLocalVariable
.filter { |c, _| c == "\n" }
.size
row = source[0...byte_offset].count("\n")
text_predicates = []
property_predicates = []
property_settings = []
Expand Down
2 changes: 1 addition & 1 deletion lib/tree_sitter/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module TreeSitter
# The version of the tree-sitter library.
TREESITTER_VERSION = '0.24.7'
TREESITTER_VERSION = '0.26.3'
# The current version of the gem.
VERSION = '2.0.0'
end
2 changes: 1 addition & 1 deletion parsers.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
build-dir = "vendor/parsers/build"
out-dir = "vendor/parsers"
tree-sitter-version = "0.24.7"
tree-sitter-version = "0.26.3"

[parsers]
embedded-template = "0.23.2"
Expand Down
3 changes: 1 addition & 2 deletions test/tree_sitter/language_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ def mul(a, b)
end

it 'must be of correct version' do
assert ruby.version <= TreeSitter::LANGUAGE_VERSION \
&& ruby.version >= TreeSitter::MIN_COMPATIBLE_LANGUAGE_VERSION
assert ruby.version.between?(TreeSitter::MIN_COMPATIBLE_LANGUAGE_VERSION, TreeSitter::LANGUAGE_VERSION)
end
end
7 changes: 0 additions & 7 deletions test/tree_sitter/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,6 @@ def mul(a, b)
end
end

describe 'timeout_micros' do
it 'must get/set timeout_micros' do
parser.timeout_micros = 1
assert_equal 1, parser.timeout_micros
end
end

# TODO: included_ranges for parsing partial documents.

# TODO: parsing with non-nil tree.
Expand Down
Loading
Loading