1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-05-10 15:25:04 -07:00

treesit-explore-mode usability improvements (bug#80935)

Improve the usability of treesit-explore-mode.

- Prompt for the primary parser first, if there is one, rather
than the first in the list reported by 'treesit-parser-list'.
Previously, in a multi-parser buffer like 'markdown-ts-mode', one
had to hunt for the primary parser.

- Kill the tree buffer and its window if the source buffer is
killed or 'treesit-explore-mode'.  Previously, when
'treesit-explore-mode' is disabled in the source buffer, its
companion explorer tree buffer was left dangling and window
open (with an unrelated buffer).

- Improve 'treesit--explorer-refresh-1' to recenter the window
around the selected nodes when the selected region in the source
buffer changes.  Previously, one had to navigate manually to find
the corresponding highlighted node in the tree window which may
be far away from that the source buffer's region represents.

- Disable 'treesit-explore-mode' in the source buffer if its
companion tree buffer is killed.  Previously,
'treesit-explore-mode' remained active in the source buffer in
an effectively unusable state.

- Disable 'treesit-explore-mode' if the user quits
'completing-read' in 'treesit-explorer-switch-parser' when
enabling the mode.  Previously, 'treesit-explore-mode' was left
enabled after quit.

- New command to switch back and forth between the source buffer
and tree buffer windows to make navigating more convenient.
Previously, in a multi-window frame, one had to navigate to/from
these two related windows in a more cumbersome way.

- New command to quit 'treesit-explore-mode' and
'treesit--explorer-tree-mode' and handle buffer and window
cleanup.

* lisp/treesit.el (treesit--explorer-refresh-1): Recenter the
window, if amenable, to the node selected in the source buffer.
(treesit--explorer-kill-explorer-buffer): Remove function.
(treesit--explorer-generate-parser-alist): Prioritize the
primary parser, if there is one.
(treesit--explorer-tree-mode-cleanup): New defun.
(treesit-explore-quit): New command.
(treesit-explorer-tree-window): New defun.
(treesit-explorer-source-buffer-window): New defun.
(treesit-explore-mode-map): Revise key bindings.
(treesit--explorer-tree-mode-map): Revise key bindings.
(treesit--explorer-tree-mode): New keymap.
(treesit-explorer-switch-parser): Add a default to
completing-read.
(treesit-explore-mode): Guard completing read quit.  Wire up the
new cleanup functions.
This commit is contained in:
Stéphane Marks 2026-04-30 15:34:23 -04:00 committed by Yuan Fu
parent 48b064a2aa
commit 060451d6e0
No known key found for this signature in database
GPG key ID: 56E19BC57664A442

View file

@ -5035,6 +5035,10 @@ in the region."
(while (and (null (pos-visible-in-window-p pos window))
(= (forward-line 4) 0))
(set-window-start window (point))))
;; Recenter if amenable.
(when (< scroll-conservatively 101)
(with-selected-window window
(recenter)))
(set-window-point window pos)))))))
(defun treesit--explorer-refresh ()
@ -5197,11 +5201,6 @@ leaves point at the end of the last line of NODE."
(when (not named)
(overlay-put ov 'face 'treesit-explorer-anonymous-node)))))
(defun treesit--explorer-kill-explorer-buffer ()
"Kill the explorer buffer of this buffer."
(when (buffer-live-p treesit--explorer-buffer)
(kill-buffer treesit--explorer-buffer)))
(defun treesit--explorer-generate-parser-alist ()
"Return an alist of (PARSER-NAME . PARSER) for relevant parsers.
Relevant parsers include all global parsers and local parsers that
@ -5210,7 +5209,12 @@ covers point. PARSER-NAME are unique."
(local-parsers-at-point
(treesit-local-parsers-at (point)))
res)
(dolist (parser (treesit-parser-list nil nil t))
;; Add `treesit-primary-parser' first in the list, if populated.
(dolist (parser (delete-dups
(delq nil
(append
(list treesit-primary-parser)
(treesit-parser-list nil nil t)))))
;; Exclude local parsers that doesn't cover point.
(when (or (memq parser local-parsers-at-point)
(not (memq parser local-parsers)))
@ -5230,20 +5234,68 @@ covers point. PARSER-NAME are unique."
res)))
(nreverse res)))
(defun treesit--explorer-tree-mode-cleanup ()
"Clean up `treesit--explorer-tree-mode'.
If called from the source buffer, quit the tree buffer window and kill
the explorer buffer.
If called from the explorer tree buffer, disable `treesit-explore-mode'
in the source buffer, quit the tree window and kill its buffer."
(cond
;; Called from the source buffer.
((buffer-live-p treesit--explorer-buffer)
(when (window-live-p (get-buffer-window treesit--explorer-buffer))
(let ((buf treesit--explorer-buffer))
(with-selected-window (get-buffer-window treesit--explorer-buffer)
(quit-window))
(kill-buffer buf))))
;; Called from the tree buffer.
((buffer-live-p treesit--explorer-source-buffer)
(with-current-buffer treesit--explorer-source-buffer
(treesit-explore-mode -1))
(when (window-live-p (get-buffer-window (current-buffer)))
(with-selected-window (get-buffer-window (current-buffer))
(quit-window 'kill))))))
(defun treesit-explorer-tree-window-select ()
"Select the `treesit--explorer-buffer' window.
Invoke this command from the source window."
(interactive)
(if (buffer-live-p treesit--explorer-buffer)
(select-window (get-buffer-window treesit--explorer-buffer))
(user-error "The `treesit-explorer-mode' tree buffer does not exist")))
(defun treesit-explorer-source-buffer-window-select ()
"Select the `treesit--explorer-buffer' window.
Invoke this command from the tree window."
(interactive)
(if (buffer-live-p treesit--explorer-source-buffer)
(select-window (get-buffer-window treesit--explorer-source-buffer))
(user-error "The `treesit-explorer-mode' source buffer does not exist")))
(defvar-keymap treesit-explore-mode-map
:doc "Keymap for the treesit explore mode."
"C-c C-t o" #'treesit-explorer-tree-window-select
"C-c C-t q" #'treesit-explore-quit)
(defvar-keymap treesit--explorer-tree-mode-map
:doc "Keymap for the treesit tree explorer.
Navigates from button to button."
:parent special-mode-map
"n" #'forward-button
"p" #'backward-button
"TAB" #'forward-button
"<backtab>" #'backward-button)
"n" #'forward-button
"p" #'backward-button
"q" #'treesit-explore-quit
"TAB" #'forward-button
"<backtab>" #'backward-button
"C-c C-t o" #'treesit-explorer-source-buffer-window-select
"C-c C-t q" #'treesit-explore-quit)
(define-derived-mode treesit--explorer-tree-mode special-mode
"TS Explorer"
"Mode for displaying syntax trees for `treesit-explore-mode'."
nil)
;; Clean up `treesit--explorer-tree-mode' when the tree buffer is
;; killed.
(add-hook 'kill-buffer-hook
#'treesit--explorer-tree-mode-cleanup 0 t))
(defun treesit-explorer-switch-parser (parser)
"Switch explorer to use PARSER."
@ -5252,8 +5304,14 @@ Navigates from button to button."
(treesit--explorer-generate-parser-alist))
(parser-name (if (= (length parser-alist) 1)
(car parser-alist)
;; Default to the first parser in the
;; list which we hope is
;; `treesit-primary-parser'.
(completing-read
"Parser: " (mapcar #'car parser-alist)))))
"Parser: "
(mapcar #'car parser-alist)
nil t nil nil
(caar parser-alist)))))
(alist-get parser-name parser-alist
nil nil #'equal))))
(unless treesit-explore-mode
@ -5262,7 +5320,9 @@ Navigates from button to button."
(display-buffer treesit--explorer-buffer
(cons nil '((inhibit-same-window . t))))
(setq-local treesit--explorer-last-node nil)
(treesit--explorer-refresh))
(treesit--explorer-refresh)
;; Signal that `completing-read' did not quit.
t)
(define-minor-mode treesit-explore-mode
"Enable exploring the current buffer's syntax tree.
@ -5281,33 +5341,41 @@ window."
(buffer-name))))
(with-current-buffer treesit--explorer-buffer
(treesit--explorer-tree-mode)))
;; Select parser.
(call-interactively #'treesit-explorer-switch-parser)
;; Set up variables and hooks.
(add-hook 'post-command-hook
#'treesit--explorer-post-command 0 t)
(add-hook 'kill-buffer-hook
#'treesit--explorer-kill-explorer-buffer 0 t)
;; Tell `desktop-save' to not save explorer buffers.
(when (boundp 'desktop-modes-not-to-save)
(unless (memq 'treesit--explorer-tree-mode
desktop-modes-not-to-save)
(push 'treesit--explorer-tree-mode
desktop-modes-not-to-save)))
;; Tell `desktop-save' to not save this minor mode
;; that might disrupt loading the desktop
;; with the prompt to select a parser.
(when (boundp 'desktop-minor-mode-table)
(unless (member '(treesit-explore-mode nil)
desktop-minor-mode-table)
(push '(treesit-explore-mode nil)
desktop-minor-mode-table))))
;; Select parser. `treesit-explorer-switch-parser' will return
;; t if its `completing-read' did not quit.
(if (not (condition-case _
(call-interactively #'treesit-explorer-switch-parser)
(quit)))
(setq treesit-explore-mode nil)
;; Track the `treesit--explorer-source-buffer' active region.
(add-hook 'post-command-hook
#'treesit--explorer-post-command 0 t)
;; Clean up when the `treesit-explore-mode' buffer is killed.
(add-hook 'kill-buffer-hook
#'treesit--explorer-tree-mode-cleanup 0 t)
;; Tell `desktop-save' to not save explorer buffers.
(when (boundp 'desktop-modes-not-to-save)
(unless (memq 'treesit--explorer-tree-mode
desktop-modes-not-to-save)
(push 'treesit--explorer-tree-mode
desktop-modes-not-to-save)))
;; Tell `desktop-save' to not save this minor mode
;; that might disrupt loading the desktop
;; with the prompt to select a parser.
(when (boundp 'desktop-minor-mode-table)
(unless (member '(treesit-explore-mode nil)
desktop-minor-mode-table)
(push '(treesit-explore-mode nil)
desktop-minor-mode-table)))))
;; Turn off explore mode.
(remove-hook 'post-command-hook
#'treesit--explorer-post-command t)
(remove-hook 'kill-buffer-hook
#'treesit--explorer-kill-explorer-buffer t)
(treesit--explorer-kill-explorer-buffer)))
#'treesit--explorer-tree-mode-cleanup t)
;; Clean up if the user disables `treesit-explore-mode' interactively; e.g.,
;; via M-x while leaving the source buffer alive.
(when (called-interactively-p 'any)
(treesit--explorer-tree-mode-cleanup))))
(defun treesit-explore ()
"Show the explorer."
@ -5317,6 +5385,15 @@ window."
(display-buffer treesit--explorer-buffer '(nil (inhibit-same-window . t)))
(treesit-explore-mode)))
(defun treesit-explore-quit ()
"Quit and clean up `treesit-explore-mode'.
Invoke this command from the source buffer or its tree buffer."
(interactive)
;; Called from the source buffer.
(when (buffer-live-p treesit--explorer-buffer)
(treesit-explore-mode -1))
(treesit--explorer-tree-mode-cleanup))
;;; Install & build language grammar
(defvar treesit-language-source-alist nil