From 591759d508024a666a863cf918287744e72b1267 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Sun, 23 Oct 2022 18:05:10 -0700 Subject: [PATCH] Make treesit-node-child and fiends accept negative index * doc/lispref/parsing.texi (Retrieving Node): Update manual. * src/treesit.c (Ftreesit_node_child) (Ftreesit_node_field_name_for_child): Accept and process negative index. --- doc/lispref/parsing.texi | 21 +++++++++++++------ src/treesit.c | 45 ++++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index e26090be1d7..3a19b1b7caa 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi @@ -567,11 +567,16 @@ This function returns the immediate parent of @var{node}. @defun treesit-node-child node n &optional named This function returns the @var{n}'th child of @var{node}. If @var{named} is non-@code{nil}, it counts only named nodes -(@pxref{tree-sitter named node, named node}). For example, in a node -that represents a string @code{"text"}, there are three children -nodes: the opening quote @code{"}, the string text @code{text}, and -the closing quote @code{"}. Among these nodes, the first child is the -opening quote @code{"}, and the first named child is the string text. +(@pxref{tree-sitter named node, named node}). + +For example, in a node that represents a string @code{"text"}, there +are three children nodes: the opening quote @code{"}, the string text +@code{text}, and the closing quote @code{"}. Among these nodes, the +first child is the opening quote @code{"}, and the first named child +is the string text. + +This function returns @code{nil} if there is no @var{n}'th child. +@var{n} could be negative, e.g., -1 represents the last child. @end defun @defun treesit-node-children node &optional named @@ -880,7 +885,11 @@ of @var{node} as a child of its parent. @defun treesit-node-field-name-for-child node n This function returns the field name of the @var{n}'th child of -@var{node}. +@var{node}. It returns @code{nil} if there is no @var{n}'th child, or +the @var{n}'th child doesn't have a field name. + +Note that @var{n} counts both named and anonymous child. And @var{n} +could be negative, e.g., -1 represents the last child. @end defun @defun treesit-child-count node &optional named diff --git a/src/treesit.c b/src/treesit.c index b0e7d0e211a..3ac0a0c7b2e 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -1529,20 +1529,36 @@ DEFUN ("treesit-node-child", Ftreesit_node_child, Streesit_node_child, 2, 3, 0, doc: /* Return the Nth child of NODE. -Return nil if there is no Nth child. If NAMED is non-nil, look for named -child only. NAMED defaults to nil. If NODE is nil, return nil. */) +Return nil if there is no Nth child. If NAMED is non-nil, look for +named child only. NAMED defaults to nil. If NODE is nil, return +nil. + +N could be negative, e.g., -1 represents the last child. */) (Lisp_Object node, Lisp_Object n, Lisp_Object named) { if (NILP (node)) return Qnil; treesit_check_node (node); - treesit_check_positive_integer (n); + CHECK_INTEGER (n); EMACS_INT idx = XFIXNUM (n); - if (idx > UINT32_MAX) - xsignal1 (Qargs_out_of_range, n); + treesit_initialize (); TSNode treesit_node = XTS_NODE (node)->node; TSNode child; + + /* Process negative index. */ + if (idx < 0) + { + if (NILP (named)) + idx = ts_node_child_count (treesit_node) + idx; + else + idx = ts_node_named_child_count (treesit_node) + idx; + } + if (idx < 0) + return Qnil; + if (idx > UINT32_MAX) + xsignal1 (Qargs_out_of_range, n); + if (NILP (named)) child = ts_node_child (treesit_node, (uint32_t) idx); else @@ -1606,19 +1622,30 @@ DEFUN ("treesit-node-field-name-for-child", doc: /* Return the field name of the Nth child of NODE. Return nil if there's no Nth child, or if it has no field. -If NODE is nil, return nil. */) +If NODE is nil, return nil. + +N counts all children, i.e., named ones and anonymous ones. + +N could be negative, e.g., -1 represents the last child. */) (Lisp_Object node, Lisp_Object n) { if (NILP (node)) return Qnil; treesit_check_node (node); - treesit_check_positive_integer (n); + CHECK_INTEGER (n); EMACS_INT idx = XFIXNUM (n); - if (idx > UINT32_MAX) - xsignal1 (Qargs_out_of_range, n); treesit_initialize (); TSNode treesit_node = XTS_NODE (node)->node; + + /* Process negative index. */ + if (idx < 0) + idx = ts_node_child_count (treesit_node) + idx; + if (idx < 0) + return Qnil; + if (idx > UINT32_MAX) + xsignal1 (Qargs_out_of_range, n); + const char *name = ts_node_field_name_for_child (treesit_node, (uint32_t) idx); -- 2.39.2