diff options
| author | Yuan Fu | 2022-05-13 13:47:41 -0700 |
|---|---|---|
| committer | Yuan Fu | 2022-05-13 14:37:24 -0700 |
| commit | d94c7076dfcb35037e77fc12e48d07d65c2005cf (patch) | |
| tree | 577e96931e0c050345c77654f295d6b7a8dab963 | |
| parent | 78df03329d1e942d5617c0f09f264792e24a063d (diff) | |
| download | emacs-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.el | 107 | ||||
| -rw-r--r-- | test/src/treesit-tests.el | 4 |
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 | |||
| 235 | Traverse starting from NODE (i.e., NODE is passed to PRED). For | ||
| 236 | each node traversed, call PRED with the node, stop and return the | ||
| 237 | node if PRED returns non-nil. If STEP >= 0 or nil, go forward, | ||
| 238 | if STEP < 0, go backward. If no node satisfies PRED, return | ||
| 239 | nil." | ||
| 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'. | ||
| 250 | PRED and STEP are the same as in | ||
| 251 | `treesit-traverse-breadth-first'. This function simply runes BFS | ||
| 252 | on QUEUE: pops an element from QUEUE, append children to QUEUE, | ||
| 253 | process the element, and next iteration. TAIL is the pointer to | ||
| 254 | the 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 | |||
| 271 | Traverse starting from NODE (i.e., NODE is passed to PRED). For | ||
| 272 | each node traversed, call PRED with the node, stop and return the | ||
| 273 | node if PRED returns non-nil. If STEP >= 0 or nil, go forward, | ||
| 274 | if STEP < 0, go backward. If no node satisfies PRED, return | ||
| 275 | nil." | ||
| 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 | |||
| 284 | If there is no next sibling of NODE but NODE has a parent, return | ||
| 285 | the parent. If there is no parent, return nil. If STEP >= 0 or | ||
| 286 | nil, return the next sibling, if STEP < 0, return the previous | ||
| 287 | one. | ||
| 288 | |||
| 289 | Return 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 | |||
| 302 | Traverse starting from NODE (i.e., NODE is passed to PRED). For | ||
| 303 | each node traversed, call PRED with the node, stop and return the | ||
| 304 | node if PRED returns non-nil. If STEP >= 0 or nil, go forward, | ||
| 305 | if STEP < 0, go backward. If no node satisfies PRED, return | ||
| 306 | nil. | ||
| 307 | |||
| 308 | Traversing forward depth-first means, for a tree like the below | ||
| 309 | where 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. |
| 232 | If NAMED is non-nil, collect named child only." | 339 | If 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 |