Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

textobjects for html/xml tags #3282

Closed
wants to merge 5 commits into from
Closed

Conversation

ff2400t
Copy link

@ff2400t ff2400t commented Aug 1, 2022

I bundled the html and jsx queries with this pr to show how it work in practice

mae - will select the element and all it's child element, this is can also be used to select self closing element like we see in jsx
mie - will select the all the child nodes of an element

@AlexanderBrevig
Copy link
Contributor

Please do not change the behavior of t as it's probably part of muscle memory for multiple people already (myself included).

If e is available, maybe use that instead as element?

@the-mikedavis
Copy link
Member

Yeah I don't see a compelling reason for a breaking change here: more languages have queryable test conventions than XML tags. We could use T for xml tags?

@the-mikedavis
Copy link
Member

There are also the navigation commands ]e/[e to add:

"[" => { "Left bracket"
"d" => goto_prev_diag,
"D" => goto_first_diag,
"f" => goto_prev_function,
"c" => goto_prev_class,
"a" => goto_prev_parameter,
"o" => goto_prev_comment,
"t" => goto_prev_test,
"p" => goto_prev_paragraph,
"space" => add_newline_above,
},
"]" => { "Right bracket"
"d" => goto_next_diag,
"D" => goto_last_diag,
"f" => goto_next_function,
"c" => goto_next_class,
"a" => goto_next_parameter,
"o" => goto_next_comment,
"t" => goto_next_test,
"p" => goto_next_paragraph,
"space" => add_newline_below,
},

@kirawi kirawi added the S-waiting-on-review Status: Awaiting review from a maintainer. label Sep 13, 2022
runtime/queries/html/textobjects.scm Outdated Show resolved Hide resolved
runtime/queries/html/textobjects.scm Outdated Show resolved Hide resolved
@the-mikedavis the-mikedavis added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from a maintainer. labels Oct 14, 2022
@the-mikedavis the-mikedavis added S-waiting-on-review Status: Awaiting review from a maintainer. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Nov 8, 2022
Copy link
Member

@the-mikedavis the-mikedavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XML has been added so we could add queries for that as well (#4518), or leave that for a separate PR

Comment on lines 1 to 5
(element
(start_tag)
(_)* @xml_element.inside
(end_tag)?
. ) @xml_element.around
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's some odd interaction here with the * vs the ? actually. I think we may need to split these into two separate stanzas so that the version with the end_tag takes precedence:

Suggested change
(element
(start_tag)
(_)* @xml_element.inside
(end_tag)?
. ) @xml_element.around
(element
(start_tag)
(_)* @xml_element.inside
(end_tag)
. ) @xml_element.around
(element
(start_tag)
(_)* @xml_element.inside
. ) @xml_element.around

Otherwise a snippet like:

    <a href="print.html" title="Print this book" aria-label="Print this book">
        <i id="print-button" class="fa fa-print"></i>
    </a>

If the cursor is on the whitespace at the start of line 2, mie will select the </a>

@the-mikedavis the-mikedavis added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from a maintainer. labels Dec 20, 2022
@the-mikedavis the-mikedavis linked an issue Apr 10, 2023 that may be closed by this pull request
@Ansimorph
Copy link

Thanks so much for this PR!

I tested the changes (rebased the current main) and it works reliably for around-queries.

For inside-queries it works when the cursor is already on the content nested in a tag.
When the cursor is resting on an html attribute such as class="sr-only", mie selects nothing.
When the cursor rests on the tag name of a closing tag, mie selects the tag name itself.

I would expect the element the cursor is in to be the context the selection is applied to:

<section>
  <p [CURSOR] class="sr-only"> text </p>
</section>

mie selects text
mae selects <p class="sr-only"> text </p>

Currently only mae works that way

@ff2400t
Copy link
Author

ff2400t commented Apr 26, 2023

The mei queries still don't work as I want them to.

<section>
  <p [CURSOR] class="sr-only"> text </p>
</section>

It would be ideal in the above case for mei to select the text inside the paragraph element. But for now it selects the whole paragraph element.
Hopefully someone who understand treesitter queries can help in that.

Furthermore, i think it would be great if the Text selection is trimmed of whitespace by default but that should be part of another PR

@the-mikedavis the-mikedavis added S-waiting-on-review Status: Awaiting review from a maintainer. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 26, 2023
@ff2400t
Copy link
Author

ff2400t commented May 1, 2023

So in the end i had to modify how textobjects are captured from rust to capture the element.inner as intended. This is also how nvim-treesitter-textobjects does the capture for these too. Hopefully some with better knowledge of rust can help me remove the extra QueryCursor allocation.

Comment on lines +270 to +303
let mut second_cursor = QueryCursor::new();

// // This is helpful in capturing inner nodes in html, jsx and other xml like
// // languages where it's diffucult to capture the *.inner textobjects.
// // e.g.: <p[CURSOR] att="val">this is a <a>link</a></p>
// // because of how treesitter s-exp work, when trying to capture the p inside of element
// // we capture the whole p element instead.
// // So, Here we Take the p element as the root node and then capture our inner element
let node = if better_capture && textobject == TextObject::Inside {
let capture_name = format!("{}.{}", object_name, TextObject::Around); // eg. function.inner
let outer_node = lang_config
.textobject_query()?
.capture_nodes(&capture_name, slice_tree, slice, &mut cursor)?
.filter(|node| node.byte_range().contains(&byte_pos))
.min_by_key(|node| node.byte_range().len())?;

match outer_node {
CapturedNode::Single(outer_node) => {
let capture_name = format!("{}.{}", object_name, TextObject::Inside); // eg. function.inner
lang_config
.textobject_query()?
.capture_nodes(&capture_name, outer_node, slice, &mut second_cursor)?
.next()?
}
_ => outer_node,
}
} else {
let capture_name = format!("{}.{}", object_name, textobject); // eg. function.inner
lang_config
.textobject_query()?
.capture_nodes(&capture_name, slice_tree, slice, &mut cursor)?
.filter(|node| node.byte_range().contains(&byte_pos))
.min_by_key(|node| node.byte_range().len())?
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the block comment explanation, however: There's now three almost identical format! calls and three chains of lang_config.textobject_query()?.capture_nodes. The // et. function.inner does not feel correct and in general the intent is kind of hard to get a grasp on (reading the code only).

Maybe you can refactor it a bit DRYer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh i would love make it DRY but this my first time writing rust so I don't know if making this a Closure is the rust way of Doing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I tried to extract that into a Closure, but i had issues returning an Iterators from the closure and then i had some lifetimes issues on top of that.
Extracting that to a separate function resulted in a similar lifetimes issue, which i don't understand

@@ -260,18 +260,47 @@ pub fn textobject_treesitter(
object_name: &str,
slice_tree: Node,
lang_config: &LanguageConfiguration,
better_capture: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should more closely express what's going on. Why would you ever not use the better_capture? Maybe even use an enum here, to make the call site more explicit/readable?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's Temporary name, I thought someone reviewing the PR could suggest a Better name

@ff2400t ff2400t changed the title html/xml tags can now be selected as text objects, added the queries for html and jsx textobjects for html/xml tags May 14, 2023
@pascalkuthe
Copy link
Member

closing this one out as stale, thank you for contributing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from a maintainer.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

tag textobject for working with html markup
6 participants