-
Notifications
You must be signed in to change notification settings - Fork 17
Collect sections in calls and emit them as document symbols #867
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocking so we can at least discuss my comment above together before merging
@@ -177,12 +177,12 @@ fn collect_symbols( | |||
) -> anyhow::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I guess you view a function call as a kind of "reset" of the nesting level right?
I know you mentioned an example like this in your description, but explicitly compare positron and rstudio here:


At first glance I thought RStudio made more sense here, as it seems to only rely on the number of #
to determine how the Outline works.
But then I did this, and I think I like how Positron works here more


It's like you're saying:
"The call()
implicitly starts an H2 ##
nesting level under level 2
, but when you write sections in call()
, you can "start over" with a single #
"
I don't know how to precisely describe that feature. It kind of feels right, but it's also fairly weird to see # level 1-a
and ## this
show up at the same level in the outline, because they have a differing number of leading #
. It makes it kind of hard to predict?
I also wonder if differing from RStudio too much here is going to make the outline annoying when coming from RStudio with a big {targets} script...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I take it back that I like Positron more in the second example.
I think having # level 1-a
show up under # level 2
is too confusing.
I really think the number of #
should drive the nesting level, as that is very easily predictable (possibly it is the only thing driving the nesting level?)
i.e. I think letting the number of #
drive the nesting level would lean into the principle of least surprise here.
It also seems like it would align with RStudio more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think we should have behavior more similar to RStudio for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could maybe see how it's technically possible to close nesting syntax from a section header, at the expense of making the implementation convoluted and confusing.
But let's think through the UX implications of this. We could be arbitrarily deep in a syntax tree through layers of calls, function definitions, and blocks. E.g. deep in an R6 method, inside an if branch inside lapply. Then someone adds a section down there that starts with #
by mistake and all of a sudden the rest of the tree, including all following R6 methods, is pulled at top level? This feels confusing and hard to use to me. It really goes against the nesting nature of syntax and tree invariants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your argument will be more convincing if you can provide a video in RStudio of what would confuse you, because I'm having trouble following
Ok @juliasilge, @lionel- and I talked about this quite a bit and we now think we have a good mental model that describes how this should work. @lionel- if you could find some way to encode some of the theory into docs for this, that could be helpful for us in the future when thinking about this RStudio is not the best reference guideWe thought we understood how RStudio works, but we actually don't, and we don't think this behavior is right. It's very odd for ![]() ![]()
|
Thanks for this very clear summary @DavisVaughan! Key things:
I'll add that this is a big ergonomic advantage: While you're deep in the code, you can add sections without having to worry about the current markdown structure. I.e. let's say we have the hypothetical implementation where headers close sections across the syntax tree. In this example I'm adding nested section and need to double check the current heading structure to avoid resetting the outline. I see I need to add a level 2 section to nest it within the level 1 header: # Level 1 ----
...
imagine_a_deep_nesting({
## I'm adding a level 2 section here ----
...
}) If I then change the top-level structure and forget to adjust headers in nested elements, I'll unexpectedly get the confusing reset of outline in the middle of my code: # Level 1 ----
## New Level 2 ----
### New Level 3 ----
...
imagine_a_deep_nesting({
## Oh no! My section is now closing level 3 ----
...
}) With the approach implemented here, where each level of syntax nesting creates a new "document", I can start a new hierarchy and have it consistently nested, no matter what actual header hierarchy is at top-level. I can copy-paste the code anywhere and the outline will still work the same. # Level 1 ----
## Level 2 ----
imagine_a_deep_nesting({
# This section is nested in top-level headers no matter what ----
...
}) |
120984c
to
ee55fe4
Compare
Thank you for the detailed explanation on this! ❤️ I agree that the targets use case is the most important one to make sure we get right here, based on how people are talking about using this feature. I'm a little uncomfortable straying from the RStudio behavior given that we (RStudio/Posit) basically made up this feature so if there is any norm to align with, that would be it, but I am happy to defer here as long as we get those targets users supported. |
ce75104
to
40e9c0f
Compare
498e2d1
to
b0ff1ff
Compare
b0ff1ff
to
37ec682
Compare
Branched from #866.
Addresses posit-dev/positron#8402.
You can now add sections in function calls too, between arguments.
The implementation is shared with the section logic for
{}
blocks. This works a little differently than in RStudio: The sections are nested in the call and can't affect the section hierarchy outside the call. If you have:The "level 1" sections in the block and call do not close the top-level "level 2" section. This difference is necessary because the LSP outline is more powerful and includes syntactic elements like function definitions and assigned variables in the outline. Allowing sections to close from inside a nested element would make things complicated and I'd argue respecting the nesting of the code makes sense from a user pespective too.
QA Notes
Sections in calls:
now work the same way as sections in blocks:
Screen.Recording.2025-07-07.at.15.31.07.mov