[language-html] Allow for folding of attribute lists…

…such as in multi-line self-closing tags and multi-line opening tags.

Also fix an issue in fold handling that made it impossible to use `@_IGNORE_` captures in `folds.scm`.
This commit is contained in:
Andrew Dupont 2024-02-09 14:20:34 -08:00
parent cc20d7055d
commit 89686ce03a
2 changed files with 73 additions and 2 deletions

View File

@ -1,6 +1,74 @@
; When dealing with a self-closing element that spans multiple lines, this lets
; us fold the attribute list.
;
; This query captures elements that happen to be self-closing but don't end
; with an XHTML-style ` />`. Because `tree-sitter-html` doesn't distinguish
; these from elements that can have content, we have to check the tag name to
; know how to treat these.
((element
(start_tag
(tag_name) @_IGNORE_) @fold)
(#match? @_IGNORE_ "^(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$")
)
; This one captures the XHTML-style nodes.
(self_closing_tag) @fold
; TODO: Right now, the fold cache doesn't work properly when a given range
; satisfies more than one fold. We should employ `ScopeResolver` to fix this.
; Fold up all of
;
; <div
; foo="bar"
; baz="thud">
;
; </div>
;
; with the fold indicator appearing on whichever line has the `>` that closes
; the opening tag.
;
; Usually this'll be the same line on which the tag opened; but when it isn't,
; this allows for the attribute list of the opening element to be folded
; separately from the element's contents.
;
(element
(start_tag
(tag_name) @_IGNORE_
">" @fold)
(#not-match? @_IGNORE_ "^(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$")
(#set! fold.endAt parent.parent.lastNamedChild.startPosition)
(#set! fold.adjustToEndOfPreviousRow true)
)
; When we have…
;
; <div
; foo="bar"
; baz="thud"
; >
;
; </div>
;
; …we can put a fold indicator on the line with `<div` and use it to fold up
; all of a start tag's attributes.
;
; We keep the end of the fold on a separate line because otherwise we lose the
; ability to independently toggle the folding of the element's contents.
;
(element
(start_tag
(tag_name) @_IGNORE_) @fold
(#not-match? @_IGNORE_ "^(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$")
(#set! fold.endAt lastChild.startPosition)
(#set! fold.adjustToEndOfPreviousRow true))
[
(element)
(script_element)
(style_element)
] @fold

View File

@ -2149,6 +2149,9 @@ class FoldResolver {
let boundaries = createTree(compareBoundaries);
let captures = this.layer.foldsQuery.captures(rootNode, start, end);
// TODO: When a node is captured more than once, we handle all captures. We
// should instead use `ScopeResolver` so that a single folds query can use
// `capture.final` and `capture.shy` to rule out other possible matches.
for (let capture of captures) {
if (capture.node.startPosition.row < start.row) { continue; }
if (capture.name === 'fold') {
@ -2156,7 +2159,7 @@ class FoldResolver {
position: capture.node.startPosition,
boundary: 'start'
}, capture);
} else {
} else if (capture.name.startsWith('fold.')) {
let key = this.keyForDividedFold(capture);
boundaries = boundaries.insert(key, capture);
}