aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuan Fu2022-05-13 13:47:41 -0700
committerYuan Fu2022-05-13 14:37:24 -0700
commitd94c7076dfcb35037e77fc12e48d07d65c2005cf (patch)
tree577e96931e0c050345c77654f295d6b7a8dab963
parent78df03329d1e942d5617c0f09f264792e24a063d (diff)
downloademacs-d94c7076dfcb35037e77fc12e48d07d65c2005cf.tar.gz
emacs-d94c7076dfcb35037e77fc12e48d07d65c2005cf.zip
New node traversal functions
* lisp/treesit.el (treesit-traverse-parent): New alias. (treesit-traverse-depth-first, treesit--traverse-breadth-first-1, treesit-traverse-breadth-first, treesit-next-sibling-or-up, treesit-traverse-forward-depth-first): New functions. * test/src/treesit-tests.el (treesit-node-supplemental): Add reminders for tests.
-rw-r--r--lisp/treesit.el107
-rw-r--r--test/src/treesit-tests.el4
2 files changed, 111 insertions, 0 deletions
diff --git a/lisp/treesit.el b/lisp/treesit.el
index dbbe0e409a8..0fe3a8ed244 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -227,6 +227,113 @@ one argument, the parent node."
227 node (treesit-node-parent node))) 227 node (treesit-node-parent node)))
228 last)) 228 last))
229 229
230(defalias 'treesit-traverse-parent #'treesit-parent-until)
231
232(defun treesit-traverse-depth-first (node pred &optional step)
233 "Traverse the subtree of NODE depth-first.
234
235Traverse starting from NODE (i.e., NODE is passed to PRED). For
236each node traversed, call PRED with the node, stop and return the
237node if PRED returns non-nil. If STEP >= 0 or nil, go forward,
238if STEP < 0, go backward. If no node satisfies PRED, return
239nil."
240 (if (funcall pred node)
241 node
242 (cl-loop for child in (if (or (null step) (>= step 0))
243 (treesit-node-children node)
244 (nreverse (treesit-node-children node)))
245 if (treesit-traverse-depth-first child pred step)
246 return child)))
247
248(defun treesit--traverse-breadth-first-1 (pred step queue tail)
249 "The work horse for `treesit-traverse-breadth-first'.
250PRED and STEP are the same as in
251`treesit-traverse-breadth-first'. This function simply runes BFS
252on QUEUE: pops an element from QUEUE, append children to QUEUE,
253process the element, and next iteration. TAIL is the pointer to
254the last cons in QUEUE, used for appending elements."
255 (cl-loop while queue
256 if (funcall pred (car queue)) return (car queue)
257 else do
258 (let ((children (if (or (null step) (>= step 0))
259 (treesit-node-children (car queue))
260 (nreverse (treesit-node-children (car queue))))))
261 ;; Append children to the end.
262 (setcdr tail children)
263 (setq tail (last tail))
264 ;; Pop the head off.
265 (setq queue (cdr queue)))
266 finally return nil))
267
268(defun treesit-traverse-breadth-first (node pred &optional step)
269 "Traverse the subtree of NODE breadth-first.
270
271Traverse starting from NODE (i.e., NODE is passed to PRED). For
272each node traversed, call PRED with the node, stop and return the
273node if PRED returns non-nil. If STEP >= 0 or nil, go forward,
274if STEP < 0, go backward. If no node satisfies PRED, return
275nil."
276 ;; Traverse with a queue.
277 (let* ((queue (list node))
278 (tail (last queue)))
279 (treesit--traverse-breadth-first-1 pred step queue tail)))
280
281(defun treesit-next-sibling-or-up (node step)
282 "Return the next sibling of NODE.
283
284If there is no next sibling of NODE but NODE has a parent, return
285the parent. If there is no parent, return nil. If STEP >= 0 or
286nil, return the next sibling, if STEP < 0, return the previous
287one.
288
289Return either ('sibling node) or ('parent node)."
290 ;; First deplete siblings.
291 (if-let ((sibling (if (or (null step) (>= step 0))
292 (treesit-node-next-sibling node)
293 (treesit-node-prev-sibling node))))
294 (list 'sibling sibling)
295 ;; When siblings depleted, go up one level.
296 (when (treesit-node-parent node)
297 (list 'parent (treesit-node-parent node)))))
298
299(defun treesit-traverse-forward-depth-first (node pred &optional step)
300 "Traverse the whole tree forward from NODE depth-first.
301
302Traverse starting from NODE (i.e., NODE is passed to PRED). For
303each node traversed, call PRED with the node, stop and return the
304node if PRED returns non-nil. If STEP >= 0 or nil, go forward,
305if STEP < 0, go backward. If no node satisfies PRED, return
306nil.
307
308Traversing forward depth-first means, for a tree like the below
309where NODE is marked 1, traverse as numbered:
310
311 16
312 |
313 3--------4-----------8
314 | | |
315 o--o-+--1 5--+--6 9---+-----12
316 | | | | | |
317 o o 2 7 +-+-+ +--+--+
318 | | | | |
319 10 11 13 14 15"
320 ;; First try NODE's subtree.
321 (or (treesit-traverse-depth-first node pred step)
322 ;; If no match, try the next node: next sibling, or parent if no
323 ;; next sibling exists.
324 (catch 'match
325 (let ((next (list nil node)))
326 ;; If NEXT is parent, call PRED on it and keep going.
327 (while (and (setq next (treesit-next-sibling-or-up
328 (cadr next) step))
329 (eq (car next) 'parent))
330 (when (funcall pred (cadr next))
331 (throw 'match (cadr next))))
332 (when next
333 ;; If NEXT is non-nil, it must be ('sibling node).
334 (treesit-traverse-forward-depth-first
335 (cadr next) pred step))))))
336
230(defun treesit-node-children (node &optional named) 337(defun treesit-node-children (node &optional named)
231 "Return a list of NODE's children. 338 "Return a list of NODE's children.
232If NAMED is non-nil, collect named child only." 339If NAMED is non-nil, collect named child only."
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index c995542a2ae..429e12088f7 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -360,6 +360,10 @@
360 ;; `treesit-parent-while' 360 ;; `treesit-parent-while'
361 ;; `treesit-node-children' 361 ;; `treesit-node-children'
362 ;; `treesit-node-field-name' 362 ;; `treesit-node-field-name'
363 ;; `treesit-next-sibling-or-up'
364 ;; `treesit-traverse-depth-first'
365 ;; `treesit-traverse-breadth-first'
366 ;; `treesit-traverse-forward-depth-first'
363 )) 367 ))
364 368
365;; TODO 369;; TODO