nvim-ufo
nvim-ufo copied to clipboard
feat(treesitter): capture node range metadata as well
Hi there! I hope this isn't too drastic of a change.
Recently venturing into the beautiful yet frustrating world of tree-sitter I have found myself in a corner where I wanted to fold region directives in C#, i.e.:
#region Search
Context.SearchFields = new Dictionary<string, string>()
{
{ nameof(Response.Name), "Name" },
{ nameof(Response.Email), "Email" },
{ nameof(Response.DateOfBirth), "Date of Birth" },
{ nameof(Response.CountryID), "Country" },
{ nameof(Response.Address), "Address" },
};
var people = _peopleService.GetFiltered(searchBy, searchString);
#endregion
I then found out about make-range which seems to be supported but not at the same time? Either way, I started using it but found another pitfall: The named capture for these directives encapsulate the next line as well.
Finally I learned about out about the offset directive, which adds a range metadata to the captured node.
And here is where we're at - this PR which allows for the usage of the range metadata for nodes used in the make-range directive. I've tested with and without the metadata and it behaved normally, so I don't think it would break anything, please let me know otherwise!
There is one caveat with this though, the other place that calls for from_nodes I have left unchanged:
https://github.com/kevinhwang91/nvim-ufo/blob/81f5ffa6e8ba27c48403cf681d4b383d924e03e4/lua/ufo/provider/treesitter.lua?plain=1#L112
It theory it would benefit from this too and then line 111 would be unnecessary, however offsets cannot be applied to multiple nodes, which is a bit unfortunate.
Please show the difference between the examples before and after the patch.
Using the following fold query for c_sharp:
(
(preproc_region) @region_begin
.
[
(comment)
(declaration)
(statement)
(type_declaration)
]*
.
(preproc_endregion) @region_end (#offset! @region_end 0 0 -1 0)
(#make-range! "fold" @region_begin @region_end)
)
When nvim-ufo collects the folds in the snippet mentioned previously it folds the following:
#region Search // FOLD START
Context.SearchFields = new Dictionary<string, string>()
{
{ nameof(Response.Name), "Name" },
{ nameof(Response.Email), "Email" },
{ nameof(Response.DateOfBirth), "Date of Birth" },
{ nameof(Response.CountryID), "Country" },
{ nameof(Response.Address), "Address" },
};
var people = _peopleService.GetFiltered(searchBy, searchString);
#endregion
// FOLD END
With the adjustments of the PR it respects the offset directive changes:
#region Search // FOLD START
Context.SearchFields = new Dictionary<string, string>()
{
{ nameof(Response.Name), "Name" },
{ nameof(Response.Email), "Email" },
{ nameof(Response.DateOfBirth), "Date of Birth" },
{ nameof(Response.CountryID), "Country" },
{ nameof(Response.Address), "Address" },
};
var people = _peopleService.GetFiltered(searchBy, searchString);
#endregion // FOLD END
It looks like nothing happened after the patch. query:
body: [
(declaration_list)
(switch_body)
(enum_member_declaration_list)
] @fold
accessors: (accessor_list) @fold
initializer: (initializer_expression) @fold
[
(block)
(preproc_if)
(preproc_elif)
(preproc_else)
(using_directive)+
] @fold
(
(preproc_region) @region_begin
.
[
(comment)
(declaration)
(statement)
(type_declaration)
]*
.
(preproc_endregion) @region_end (#offset! @region_end 0 0 -1 0)
(#make-range! "fold" @region_begin @region_end)
)
demo:
class Test
{
#region Search
Context.SearchFields = new Dictionary<string, string>()
{
{ nameof(Response.Name), "Name" },
{ nameof(Response.Email), "Email" },
{ nameof(Response.DateOfBirth), "Date of Birth" },
{ nameof(Response.CountryID), "Country" },
{ nameof(Response.Address), "Address" },
};
var people = _peopleService.GetFiltered(searchBy, searchString);
#endregion
}
Add any code after that region and you will see that the fold extends one line more, i.e.
class Test
{
#region Search
Context.SearchFields = new Dictionary<string, string>()
{
{ nameof(Response.Name), "Name" },
{ nameof(Response.Email), "Email" },
{ nameof(Response.DateOfBirth), "Date of Birth" },
{ nameof(Response.CountryID), "Country" },
{ nameof(Response.Address), "Address" },
};
var people = _peopleService.GetFiltered(searchBy, searchString);
#endregion
public string SomeString { get; set; }
}
Without the offset it will wrap the empty line below it:
Then with the offset: