Skip to content

Commit

Permalink
feat!: update core library
Browse files Browse the repository at this point in the history
  • Loading branch information
amaanq committed Oct 26, 2024
1 parent 367418b commit 2df090b
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 25 deletions.
4 changes: 4 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
],
"cflags": [
"-std=c11"
],
"defines": [
"_POSIX_C_SOURCE=200112L",
"_DEFAULT_SOURCE",
]
}
],
Expand Down
36 changes: 27 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ class SyntaxNode {
return NodeMethods.fieldNameForChild(this.tree, childIndex);
}

fieldNameForNamedChild(namedChildIndex) {
marshalNode(this);
return NodeMethods.fieldNameForNamedChild(this.tree, namedChildIndex);
}

childrenForFieldName(fieldName) {
marshalNode(this);
return unmarshalNodes(NodeMethods.childrenForFieldName(this.tree, fieldName), this.tree);
Expand All @@ -281,6 +286,11 @@ class SyntaxNode {
return unmarshalNode(NodeMethods.firstNamedChildForIndex(this.tree, index), this.tree);
}

childWithDescendant(descendant) {
marshalNodes([this, descendant]);
return unmarshalNode(NodeMethods.childWithDescendant(this.tree, descendant.tree), this.tree);
}

namedDescendantForIndex(start, end) {
marshalNode(this);
if (end == null) end = start;
Expand Down Expand Up @@ -545,7 +555,7 @@ Query.prototype._init = function() {
return matchAll
? nodes.every(text => test(text, isPositive))
: nodes.some(text => test(text, isPositive))
});
});
break;

case 'set!':
Expand Down Expand Up @@ -619,14 +629,15 @@ Query.prototype.matches = function(
startIndex = 0,
endIndex = 0,
matchLimit = 0xFFFFFFFF,
maxStartDepth = 0xFFFFFFFF
maxStartDepth = 0xFFFFFFFF,
timeoutMicros = 0
} = {}
) {
marshalNode(node);
const [returnedMatches, returnedNodes] = _matches.call(this, node.tree,
startPosition.row, startPosition.column,
endPosition.row, endPosition.column,
startIndex, endIndex, matchLimit, maxStartDepth
startIndex, endIndex, matchLimit, maxStartDepth, timeoutMicros
);
const nodes = unmarshalNodes(returnedNodes, node.tree);
const results = [];
Expand Down Expand Up @@ -668,14 +679,15 @@ Query.prototype.captures = function(
startIndex = 0,
endIndex = 0,
matchLimit = 0xFFFFFFFF,
maxStartDepth = 0xFFFFFFFF
maxStartDepth = 0xFFFFFFFF,
timeoutMicros = 0,
} = {}
) {
marshalNode(node);
const [returnedMatches, returnedNodes] = _captures.call(this, node.tree,
startPosition.row, startPosition.column,
endPosition.row, endPosition.column,
startIndex, endIndex, matchLimit, maxStartDepth
startIndex, endIndex, matchLimit, maxStartDepth, timeoutMicros
);
const nodes = unmarshalNodes(returnedNodes, node.tree);
const results = [];
Expand Down Expand Up @@ -810,13 +822,19 @@ function unmarshalNodes(nodes, tree) {
return nodes;
}

function marshalNode(node) {
if (!(node.tree instanceof Tree)){
function marshalNode(node, offset = 0) {
if (!(node.tree instanceof Tree)) {
throw new TypeError("SyntaxNode must belong to a Tree")
}
const {nodeTransferArray} = binding;
const { nodeTransferArray } = binding;
for (let i = 0; i < NODE_FIELD_COUNT; i++) {
nodeTransferArray[i] = node[i];
nodeTransferArray[offset * NODE_FIELD_COUNT + i] = node[i];
}
}

function marshalNodes(nodes) {
for (let i = 0, { length } = nodes; i < length; i++) {
marshalNode(nodes[i], i);
}
}

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"devDependencies": {
"@types/node": "^22.7.8",
"@types/tmp": "^0.2.6",
"glob": "^11.0.0",
"node-gyp": "^10.2.0",
"prebuildify": "^6.0.1",
"tmp": "^0.2.3",
Expand All @@ -44,6 +45,7 @@
"tree-sitter-java": "^0.23.2",
"tree-sitter-javascript": "^0.23.0",
"tree-sitter-json": "^0.23.0",
"tree-sitter-php": "^0.23.4",
"tree-sitter-python": "^0.23.2",
"tree-sitter-ruby": "^0.23.0",
"tree-sitter-rust": "^0.23.0"
Expand All @@ -52,7 +54,7 @@
"install": "node-gyp-build",
"build": "prebuildify --napi --strip",
"rebuild": "node-gyp rebuild",
"test": "node --test test/*.js"
"test": "node -e \"require('child_process').spawnSync('node', ['--test', ...require('glob').sync('test/**/*.js')], {stdio:'inherit'})\""
},
"publishConfig": {
"access": "public"
Expand Down
44 changes: 38 additions & 6 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,19 @@ Napi::Value GetMarshalNode(const Napi::CallbackInfo &info, const Tree *tree, TSN
return env.Null();
}

TSNode UnmarshalNode(Napi::Env env, const Tree *tree) {
TSNode UnmarshalNode(Napi::Env env, const Tree *tree, uint8_t offset) {
auto* data = env.GetInstanceData<AddonData>();
TSNode result = {{0, 0, 0, 0}, nullptr, nullptr};
result.tree = tree->tree_;
if (result.tree == nullptr) {
throw TypeError::New(env, "Argument must be a tree");
}

result.id = UnmarshalNodeId(&data->transfer_buffer[0]);
result.context[0] = data->transfer_buffer[2];
result.context[1] = data->transfer_buffer[3];
result.context[2] = data->transfer_buffer[4];
result.context[3] = data->transfer_buffer[5];
result.id = UnmarshalNodeId(&data->transfer_buffer[offset * FIELD_COUNT_PER_NODE]);
result.context[0] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 2];
result.context[1] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 3];
result.context[2] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 4];
result.context[3] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 5];
return result;
}

Expand Down Expand Up @@ -580,6 +580,24 @@ Napi::Value FieldNameForChild(const Napi::CallbackInfo &info) {
return env.Undefined();
}

Napi::Value FieldNameForNamedChild(const Napi::CallbackInfo &info) {
Env env = info.Env();
const Tree *tree = Tree::UnwrapTree(info[0]);
TSNode node = UnmarshalNode(env, tree);
if (node.id != nullptr) {
if (!info[1].IsNumber()) {
throw TypeError::New(env, "Second argument must be an integer");
}
uint32_t child_id = info[1].As<Number>().Uint32Value();
const char *field_name = ts_node_field_name_for_named_child(node, child_id);
if (field_name != nullptr) {
return String::New(env, field_name);
}
}
return env.Undefined();
}


Napi::Value Children(const Napi::CallbackInfo &info) {
Env env = info.Env();
auto* data = env.GetInstanceData<AddonData>();
Expand Down Expand Up @@ -768,6 +786,18 @@ Napi::Value Parent(const Napi::CallbackInfo &info) {
return MarshalNullNode(env);
}

Napi::Value ChildWithDescendant(const Napi::CallbackInfo &info) {
Env env = info.Env();
const Tree *tree = Tree::UnwrapTree(info[0]);
TSNode self = UnmarshalNode(env, tree);
const Tree *child_tree = Tree::UnwrapTree(info[1]);
TSNode child_node = UnmarshalNode(env, child_tree, 1);
if (self.id != nullptr && child_node.id != nullptr) {
return MarshalNode(info, tree, ts_node_child_with_descendant(self, child_node));
}
return MarshalNullNode(env);
}

Napi::Value NextSibling(const Napi::CallbackInfo &info) {
Env env = info.Env();
const Tree *tree = Tree::UnwrapTree(info[0]);
Expand Down Expand Up @@ -1015,11 +1045,13 @@ void Init(Napi::Env env, Napi::Object exports) {
{"childForFieldName", ChildForFieldName},
{"childForFieldId", ChildForFieldId},
{"fieldNameForChild", FieldNameForChild},
{"fieldNameForNamedChild", FieldNameForNamedChild},
{"children", Children},
{"namedChildren", NamedChildren},
{"childrenForFieldName", ChildrenForFieldName},
{"childrenForFieldId", ChildrenForFieldId},
{"parent", Parent},
{"childWithDescendant", ChildWithDescendant},
{"nextSibling", NextSibling},
{"previousSibling", PreviousSibling},
{"nextNamedSibling", NextNamedSibling},
Expand Down
2 changes: 1 addition & 1 deletion src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ void Init(Napi::Env env, Napi::Object exports);
Napi::Value MarshalNode(const Napi::CallbackInfo &info, const Tree *, TSNode);
Napi::Value GetMarshalNode(const Napi::CallbackInfo &info, const Tree *tree, TSNode node);
Napi::Value GetMarshalNodes(const Napi::CallbackInfo &info, const Tree *tree, const TSNode *nodes, uint32_t node_count);
TSNode UnmarshalNode(Napi::Env env, const Tree *tree);
TSNode UnmarshalNode(Napi::Env env, const Tree *tree, uint8_t offset = 0);

static inline const void *UnmarshalNodeId(const uint32_t *buffer) {
const void *result;
Expand Down
2 changes: 1 addition & 1 deletion src/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CallbackInput final {
TSInput Input() {
TSInput result;
result.payload = static_cast<void *>(this);
result.encoding = TSInputEncodingUTF16;
result.encoding = TSInputEncodingUTF16LE;
result.read = Read;
return result;
}
Expand Down
18 changes: 16 additions & 2 deletions src/query.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ void Query::Init(Napi::Env env, Napi::Object exports) {
InstanceMethod("isPatternRooted", &Query::IsPatternRooted, napi_default_method),
InstanceMethod("isPatternNonLocal", &Query::IsPatternNonLocal, napi_default_method),
InstanceMethod("startIndexForPattern", &Query::StartIndexForPattern, napi_default_method),
InstanceMethod("endIndexForPattern", &Query::EndIndexForPattern, napi_default_method),
InstanceMethod("didExceedMatchLimit", &Query::DidExceedMatchLimit, napi_default_method),
});

Expand Down Expand Up @@ -171,7 +172,7 @@ Napi::Value Query::Matches(const Napi::CallbackInfo &info) {
const Tree *tree = Tree::UnwrapTree(info[0]);

uint32_t start_row = 0, start_column = 0, end_row = 0, end_column = 0, start_index = 0, end_index = 0,
match_limit = UINT32_MAX, max_start_depth = UINT32_MAX;
match_limit = UINT32_MAX, max_start_depth = UINT32_MAX, timeout_micros = 0;

if (info.Length() > 1 && info[1].IsNumber()) {
start_row = info[1].As<Number>().Uint32Value();
Expand All @@ -197,6 +198,9 @@ Napi::Value Query::Matches(const Napi::CallbackInfo &info) {
if (info.Length() > 8 && info[8].IsNumber()) {
max_start_depth = info[8].As<Number>().Uint32Value();
}
if (info.Length() > 9 && info[9].IsNumber()) {
timeout_micros = info[9].As<Number>().Uint32Value();
}

if (query == nullptr) {
throw Error::New(env, "Missing argument query");
Expand All @@ -214,6 +218,7 @@ Napi::Value Query::Matches(const Napi::CallbackInfo &info) {
ts_query_cursor_set_byte_range(data->ts_query_cursor, start_index, end_index);
ts_query_cursor_set_match_limit(data->ts_query_cursor, match_limit);
ts_query_cursor_set_max_start_depth(data->ts_query_cursor, max_start_depth);
ts_query_cursor_set_timeout_micros(data->ts_query_cursor, timeout_micros);
ts_query_cursor_exec(data->ts_query_cursor, ts_query, root_node);

Array js_matches = Array::New(env);
Expand Down Expand Up @@ -254,7 +259,7 @@ Napi::Value Query::Captures(const Napi::CallbackInfo &info) {
const Tree *tree = Tree::UnwrapTree(info[0]);

uint32_t start_row = 0, start_column = 0, end_row = 0, end_column = 0, start_index = 0, end_index = 0,
match_limit = UINT32_MAX, max_start_depth = UINT32_MAX;
match_limit = UINT32_MAX, max_start_depth = UINT32_MAX, timeout_micros = 0;

if (info.Length() > 1 && info[1].IsNumber()) {
start_row = info[1].As<Number>().Uint32Value();
Expand All @@ -280,6 +285,9 @@ Napi::Value Query::Captures(const Napi::CallbackInfo &info) {
if (info.Length() > 8 && info[8].IsNumber()) {
max_start_depth = info[8].As<Number>().Uint32Value();
}
if (info.Length() > 9 && info[9].IsNumber()) {
timeout_micros = info[9].As<Number>().Uint32Value();
}

if (query == nullptr) {
throw Error::New(env, "Missing argument query");
Expand All @@ -297,6 +305,7 @@ Napi::Value Query::Captures(const Napi::CallbackInfo &info) {
ts_query_cursor_set_byte_range(data->ts_query_cursor, start_index, end_index);
ts_query_cursor_set_match_limit(data->ts_query_cursor, match_limit);
ts_query_cursor_set_max_start_depth(data->ts_query_cursor, max_start_depth);
ts_query_cursor_set_timeout_micros(data->ts_query_cursor, timeout_micros);
ts_query_cursor_exec(data->ts_query_cursor, ts_query, root_node);

Array js_matches = Array::New(env);
Expand Down Expand Up @@ -370,6 +379,11 @@ Napi::Value Query::StartIndexForPattern(const Napi::CallbackInfo &info) {
return Number::New(info.Env(), ts_query_start_byte_for_pattern(query_, pattern_index));
}

Napi::Value Query::EndIndexForPattern(const Napi::CallbackInfo &info) {
uint32_t pattern_index = info[0].As<Number>().Uint32Value();
return Number::New(info.Env(), ts_query_end_byte_for_pattern(query_, pattern_index));
}

Napi::Value Query::DidExceedMatchLimit(const Napi::CallbackInfo &info) {
auto *data = info.Env().GetInstanceData<AddonData>();
return Boolean::New(info.Env(), ts_query_cursor_did_exceed_match_limit(data->ts_query_cursor));
Expand Down
1 change: 1 addition & 0 deletions src/query.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Query final : public Napi::ObjectWrap<Query> {
Napi::Value IsPatternRooted(const Napi::CallbackInfo &);
Napi::Value IsPatternNonLocal(const Napi::CallbackInfo &);
Napi::Value StartIndexForPattern(const Napi::CallbackInfo &);
Napi::Value EndIndexForPattern(const Napi::CallbackInfo &);
Napi::Value DidExceedMatchLimit(const Napi::CallbackInfo &);
Napi::Value MatchLimit(const Napi::CallbackInfo &);
};
Expand Down
6 changes: 6 additions & 0 deletions test/node_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ describe("Node", () => {
assert.equal(objectNode.parent, arrayNode);
assert.equal(arrayNode.parent, tree.rootNode);
assert.equal(tree.rootNode.parent, null);

assert.equal(tree.rootNode.childWithDescendant(nullNode), arrayNode);
assert.equal(arrayNode.childWithDescendant(nullNode), objectNode);
assert.equal(objectNode.childWithDescendant(nullNode), pairNode);
assert.equal(pairNode.childWithDescendant(nullNode), nullNode);
assert.equal(nullNode.childWithDescendant(nullNode), null);
});
});

Expand Down
33 changes: 30 additions & 3 deletions test/tree_test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** @type {typeof import('tree-sitter')} */
const Parser = require("../index.js");
const PHP = require('tree-sitter-php').php;
const JavaScript = require('tree-sitter-javascript');
const Rust = require('tree-sitter-rust');
const assert = require('node:assert');
Expand Down Expand Up @@ -165,6 +166,31 @@ describe("Tree", () => {
})
});

describe('cursor on aliased root node with extra child', () => {
it('returns the correct node type', () => {
const source = `
fn main() {
C/* hi */::<D>::E;
}
`;
parser.setLanguage(Rust);
const tree = parser.parse(source);

const functionNode = tree.rootNode.firstChild;
const block = functionNode.lastChild;
const expressionStatement = block.child(1);
const scopedIdentifier = expressionStatement.firstChild;
const genericType = scopedIdentifier.firstChild;
assert.equal(genericType.type, "generic_type");

const cursor = genericType.walk();
assert(cursor.gotoFirstChild());
assert.equal(cursor.nodeType, "type_identifier");
assert(cursor.gotoNextSibling());
assert.equal(cursor.nodeType, "block_comment");
})
});

describe(".walk()", () => {
it("returns a cursor that can be used to walk the tree", () => {
parser.setLanguage(Rust);
Expand Down Expand Up @@ -317,12 +343,13 @@ describe("Tree", () => {
assert.equal(cursor.nodeType, "program");

// descend to expression statement
assert.equal(cursor.gotoFirstChildForPosition({ row: 6, column: 6 }), 0);
assert.equal(cursor.gotoFirstChildForPosition({ row: 6, column: 5 }), 0);
assert.equal(cursor.nodeType, "expression_statement");

// step into ';' and back up
assert.equal(cursor.gotoFirstChildForPosition({ row: 7, column: 0 }), null);
assert.equal(cursor.gotoFirstChildForPosition({ row: 6, column: 6 }), 1);
assert.equal(cursor.gotoFirstChildForPosition({ row: 6, column: 6 }), null);
assert.equal(cursor.gotoFirstChildForPosition({ row: 6, column: 5 }), 1);
assert.deepEqual(cursor.startPosition, { row: 6, column: 5 });
assert.equal(cursor.nodeType, ";");
assert(cursor.gotoParent());
Expand All @@ -349,7 +376,7 @@ describe("Tree", () => {
assert(cursor.gotoParent());

// step into first ',' and back up
assert.equal(cursor.gotoFirstChildForPosition({ row: 1, column: 12 }), 2);
assert.equal(cursor.gotoFirstChildForPosition({ row: 1, column: 11 }), 2);
assert.equal(cursor.nodeType, ",");
assert.deepEqual(cursor.startPosition, { row: 1, column: 11 });
assert(cursor.gotoParent());
Expand Down
Loading

0 comments on commit 2df090b

Please sign in to comment.