Fix filling around lists in markdown-ts-mode (bug#81712)

* lisp/textmodes/markdown-ts-mode.el
(markdown-ts--node-containing): New function.
(markdown-ts--list-item-at-point, markdown-ts--adaptive-fill)
(markdown-ts--fill-forward-paragraph): Use it, stop at the list
item's last non-blank line, and clamp the query end to
point-max.
This commit is contained in:
Rahul Martim Juliato 2026-08-28 09:29:03 -03:00 committed by Eli Zaretskii
parent debf9d86a9
commit 84e2bb9a5d

View file

@ -2174,36 +2174,16 @@ a line's leading whitespace resolves to the item on that line,
not to a preceding item whose node spans the whitespace.
Inside block quotes, also try from the content position past
the `>' markers."
(when-let* ((pos (save-excursion (back-to-indentation) (point)))
(node (treesit-node-at pos 'markdown))
(bol (line-beginning-position))
(eol (line-end-position)))
(or (let ((item (treesit-parent-until
node (lambda (n)
(equal (treesit-node-type n) "list_item")))))
;; Verify the current line is within the item's range.
;; `treesit-node-at' can return a node inside a list_item
;; even when point is on a preceding line.
(when (and item
(<= (treesit-node-start item) eol)
(>= (treesit-node-end item) bol))
item))
(let ((pos (save-excursion (back-to-indentation) (point)))
;; When back-to-indentation lands on block quote markers,
;; skip past them and try from the content position.
(let ((content-pos (save-excursion
(beginning-of-line)
(skip-chars-forward "> \t")
(point))))
(when (> content-pos pos)
(when-let* ((cnode (treesit-node-at content-pos 'markdown)))
(let ((item (treesit-parent-until
cnode
(lambda (n)
(equal (treesit-node-type n) "list_item")))))
(when (and item
(<= (treesit-node-start item) eol)
(>= (treesit-node-end item) bol))
item))))))))
(content-pos (save-excursion
(beginning-of-line)
(skip-chars-forward "> \t")
(point))))
(or (markdown-ts--node-containing pos "\\`list_item\\'")
(and (> content-pos pos)
(markdown-ts--node-containing content-pos "\\`list_item\\'")))))
(defun markdown-ts--list-marker-width (item)
"Return the width of ITEM's list marker including trailing space."
@ -2609,14 +2589,24 @@ of the item's text. JUSTIFY is as in `fill-paragraph'."
;; as per the contract of `fill-paragraph-function'.
t))
(defun markdown-ts--node-containing (pos type)
"Return the innermost node matching TYPE that contains POS, or nil.
TYPE is as in `treesit-parent-until'. When no leaf node covers POS,
`treesit-node-at' falls back to one that merely ends at POS, which does
not contain POS and must not be reported here."
(and-let* ((node (treesit-node-at pos 'markdown))
(parent (treesit-parent-until node type))
((<= (treesit-node-start parent) pos))
((< pos (treesit-node-end parent))))
parent))
(defun markdown-ts--adaptive-fill ()
"Return the fill prefix for the current line in Markdown.
When inside a list item, return spaces matching the column where
the item's text starts."
(and-let* ((node (treesit-node-at
(and-let* ((item (markdown-ts--node-containing
(save-excursion (back-to-indentation) (point))
'markdown))
(item (treesit-parent-until node "\\`list_item\\'")))
"\\`list_item\\'")))
(make-string (markdown-ts--list-item-text-column item) ?\s)))
(defun markdown-ts--fill-forward-paragraph (arg)
@ -2640,21 +2630,21 @@ unfilled."
(block (car (treesit-query-capture
(treesit-buffer-root-node 'markdown)
markdown-ts--fill-unfillable-block-query
pos (1+ pos))))
pos (min (1+ pos) (point-max)))))
(indented-pos (save-excursion
(goto-char pos)
(back-to-indentation)
(point)))
(node (treesit-node-at indented-pos 'markdown))
(item (treesit-parent-until node "\\`list_item\\'")))
(item (markdown-ts--node-containing
indented-pos "\\`list_item\\'")))
;; When moving forward from whitespace between list items,
;; skip to the next non-blank position and check again.
(when (and (not item) (not block) (> direction 0))
(let ((next-pos (save-excursion
(skip-chars-forward " \t\n")
(point))))
(setq node (treesit-node-at next-pos 'markdown))
(setq item (treesit-parent-until node "\\`list_item\\'"))))
(setq item (markdown-ts--node-containing
next-pos "\\`list_item\\'"))))
(cond
;; Inside an unfillable block: skip over it entirely.
(block
@ -2662,9 +2652,24 @@ unfilled."
(setq moved (1+ moved)))
;; Inside a list item: treat as paragraph boundary.
(item
(if (> direction 0)
(goto-char (treesit-node-end item))
(goto-char (treesit-node-start item)))
(let ((target (if (< direction 0)
(treesit-node-start item)
;; The item node extends over the blank line
;; separating the list from the block that
;; follows it; stop after the item's last
;; non-blank line, or filling deletes that
;; blank line (bug#81712).
(save-excursion
(goto-char (treesit-node-end item))
(skip-chars-backward
" \t\n" (treesit-node-start item))
(line-beginning-position 2)))))
;; Move to the item boundary only when that makes progress;
;; reporting a move we did not make loops `fill-region'
;; forever (bug#81712).
(if (if (> direction 0) (> target (point)) (< target (point)))
(goto-char target)
(forward-paragraph direction)))
(setq moved (1+ moved)))
;; Default: use standard paragraph motion.
(t