-
Notifications
You must be signed in to change notification settings - Fork 29.3k
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
Make hidden fold ranges independent of range provider, add manual fol… #139779
Make hidden fold ranges independent of range provider, add manual fol… #139779
Conversation
Martin, a note in case you are concerned about added cpu load for the changes I proposed. The added cpu time for these changes is very small relative to the total cpu time spent on folding. Excluding the range provider, i.e. considering only the code which executes after the range provider returns its results, I think we could reduce the cpu time related to folding to be well below 1/5th of what it is now with a bit of work. If that is something you'd like to see from me I'd be happy to propose code changes for that. |
Now added to this PR: A configurable folding regions limit and a user notification when the limit on number of folding regions is exceeded. |
Thanks @PieterBranderhorst, that's nice. Only problem: merging multiple features in our PR makes this quite difficult for me to review. Any chance you can separate them? |
Hi Martin. No problem, now I know. I've reverted the configurable folding regions limit out of this PR. (Didn't think it was safe to delete commits so I used revert.) And submitted a new PR, #140480, with just the configurable limit in that one. |
I merged current main into this to handle conflicts in folding.ts. |
Martin, any news on this? I don't want it to get stale. I see there are now merge conflicts. I can deal with those if you are ready to handle this after I check that in. |
Yes, sorry for the wait. I'll review it next week. |
Ok, thanks Martin. I have updated the branch to match current main branch. |
I started reviewing and testing. I like the approach. I investigated the following issues: class A {
foo(p: A) {
const foo = 9;
console.log('fff' + p + foo);
if (true) {
console.log('fff');
console.log('fff');
console.log('fff');
}
console.log('fff');
}
}
The decoration currently only covers the first line. I think we need to change to cover the full range to correctly preserve the folded range. |
Thanks very much Martin. And thank you for finding those issues. My apologies. I'm embarrassed by the first one. As you say, the problem is in
by
The second problem is tougher. Typing into a collapsed range seems wrong and I could argue either way for whether the new text should remain visible or not. Maybe a third choice, should the range automatically unfold when a user does that? It gets nastier. Suppose that the first line of a folded region, the one which remains visible, is something like I played around with making the decoration region include a larger block of the text and things are still problematic that way. The best overall result seems to come from starting the decoration at the last character + 1 of the region and ending it at the first character of the line following the region. But it isn't perfect, especially when considering the cases mentioned above. And I'm not sure about possible consequences to other parts of the editor from having large decoration ranges hanging around. I want to mull this over a bit. Maybe there's a simpler way. By the way, do you recall the reason for the "isBlocked" test? I.e. under what conditions there can end up being an active selection point inside a hidden range when "update" is called? |
I've explored the code which handles decorations. It is very nice. I don't think that increasing folding region decorations to cover their full range will trouble it at all. I think you were right about that as the way to go. I've ended up using the from start line's last column to the end line's last column + 1. That seems to work well in many cases. I've tested with adding text at end of first line and start of last line. Destructive backspace from start of end line un-collapses the fold. Global replace of something inside a hidden region which contains an end of line mostly works well. There are cases which misbehave, e.g. global replace which spans the start or end of a hidden region. That could be fixed by adding local (to folding) code which processes onchange events. And that might be desirable as part of an optimization effort. But for now it seems an acceptable oddity. With this fix, all edits done in the visible line above and below a hidden region remain visible until the hidden region is unfolded by the user. To me that makes the best sense. It could be argued that some typed changes should disappear into the hidden region but I think that's just too disconcerting. There are three places changed in foldingModel.ts for these fixes. Can you compare and pick them up from this copy? Or is there another way I should post it? |
Thanks @PieterBranderhorst ! I haven't yet tried out your latest, so just to reply. What you write is all correct.
What would be good to have some unit tests. The very least for the merge function. |
I didn't understand how github would handle my push at this point in the process. All is well. I am working on adding some unit tests. I expect to finish and push them tomorrow. |
Thanks @PieterBranderhorst Another test case is to press enter right before the |
I have experimented with combinations of where a range starts and ends and using AlwaysGrowsWhenTypingAtEdges instead of the current setting. I don't think any combination works better than the current one. The underlying problem is that we would like to unambiguously determine whether a region returned by a range provider is the same as a region which was previously collapsed. Even the previous code was imperfect in this area. I was able to make it misbehave in a few ways: Changing the first line of a hidden region would sometimes cause the region to unfold and at other times would not; changing the first line could cause the hidden region to expand to include more than it did previously; destructive backspacing into a hidden region would delete text invisibly. And it seemed confusing to me in that when it works it can make things I type "auto-disappear". Though that can be a correct result it nonetheless seems undesirable to me when it happens. I don't think a perfect fix covering all situations is possible without tracking the specific start and end markers for a range. That would be a daunting task even before considering that some markers are "variable", e.g. line indent. The current result seems an ok compromise to me. It is an attempt to keep all changes made before or after a collapsed region visible; to not change what part is hidden until it is unfolded. It does now automatically unfold if the user destructive backspaces into it. I can't think of rules which will have a better result. We could add a flag to track regions which are "manually" folded (either by user or due to re-opening a file without a working range provider.) With that flag we could do as well as the previous code did in most cases but we'd still have some problems as mentioned above. We could then fix some of those problems by further refining some code. I think there would always remain some issues which would be more effort to address than they'd ever be worth. Can you think of a good set of rules to address these issues? If not, would you like me to add code to address as many as possible? (By adding a "manual" flag per region and adding code to handle some specific cases, and then probably give up on other cases and just un-fold regions we can't reconcile from one range provider return to the next.) |
Hi Martin. Is this PR stuck because of the failed automatic check? I see that it failed in "Run Monaco Editor Checks". The files it seems to have a problem with aren't related to anything I modified. And when I try running " yarn monaco-compile-check" on my checkout of same thing it runs fine. I don't know what to do next. |
@PieterBranderhorst I was on vacation last week. Sorry that this is not going quicker, but I also in regular work weeks I don't get much time to look at it. Please be patient. |
I'm not wild about that approach. Two problems:
I would prefer to further modify the current branch to recreate the existing behaviour when people edit the code at either end of a collapsed region (which is approach 1 above) or take approach 2 or 3. Much of those modifications (e.g. identifying the type of each hidden range as manual vs. provided) is code which would be needed anyway if doing only fold-selected. |
No problem for me if you want to continue to modify the current PR code and not breaking the task up. But are you ok with havening a setting that let's users to choose between the keeping collapsed regions or discard them if they are no longer reported? As said, when I tested I was not a big fan of having collapsed regions that no longer match a language construct. But I'm interested to see more users try this and report feedback. |
Ok, I will continue via the current code. Making it match previous behaviour as much as possible. When done I think there may be just two cases where behaviour while the user is editing will be different. Let's see how close I can get it and then decide whether you still want that setting added. This branch is adding a bunch of unnecessary merge bulk to the repository. When I have the next version ready should I rebase this, create a new branch so that this is killable, or just carry on in this branch? |
Yes, rebase (and squash) would be good at some point. Either create a new branch or force-push to the existing branch. Both work. |
4d40de6
to
1643b89
Compare
I've made some fixes, squashed all commits in this branch, and rebased it on current source. I haven't added an experimental flag. I am hoping you'll agree that where this version behaves differently from the current code it is an improvement. I've added use of a different icon in the margin for regions which were manually folded. Please let me know what you think of it. Situations I know of where this code behaves differently from the current code are:
I hope we're getting close to something satisfactory now :) |
Excellent improvement, much better now! The new icon is a good idea but needs a better symbol. I could imagine something like the ellipses |
You're right, it needed a better icon. I didn't want to add one (right chevron with some added glyph seemed ideal) and I hadn't considered ellipsis. I think it works well. And yes, auto-expanding when using <del> at end of start line auto-expanding makes more sense. It is more consistent with the rest of the behaviour. I've checked in both of those changes. |
I'm trying to understand of that's intended or not:
|
Yes, that's intended. This is the one deliberate odd behaviour. When the folding provider no longer returns a region which it previously returned and which is collapsed the foldingModel logic can't easily distinguish between three possible reasons:
Since the foldingModel can't readily distinguish between these cases I decided to treat it as case (1) if only one hidden region is affected and unfold it, and treat it as case (2) or (3) if more than one hidden region is affected. My reasoning is that even if it is wrong in treating a situation as the first case, as it is in your example, only one fold is affected and that isn't a big hardship to the user. I expect it will nearly never be wrong when treating something the other way. |
Thanks @PieterBranderhorst. That took a long time, sorry for that. Let's see how users like it. |
@aeschli Hi Martin. I'm glad you got this done, I'd given up on it. Please ensure that the release notes mention ctrl-K ctrl-. By the way, I've given up on my parameterized folding provider. I got it working as an extension but there are too many difficulties with integrating it with patterns provided by other extensions and as a result I don't think it would get used a lot. It really should be built-in to work well. If you ever want to reconsider it I could bring it up to date and resubmit, just let me know. It:
Also, if you become interested in this area again, I have an approach sketched which would highlight things as a user moves the mouse along the gutter containing the folding icons. It would highlight the code to be folded when over a collapse icon and would temporarily expand and highlight the folded code for expand icons. Please send me a note if this becomes something you'd like to pursue. Ditto if you'd like a cleanup pass over the folding code. I think the hiddenRangeModel.ts code is almost all unnecessary at this point and could be removed, and efficiency could be improved for all of it by restructuring the internals a bit. |
Hi @PieterBranderhorst, yes sorry for letting this lie around so long. The fold selection feature is a major motivation to get it out of the door. Also I wanted to get some user feedback so it's not just my opinions that shape this.
You mention that we want to keep ranges that in a comment or a string literal. We could get that that information ( On the other discussion:
|
Hi @aeschli, I will watch Issues for feedback on this. Regarding having manually created folding ranges persist: I do want to see how much that's wanted. It would be a bit tricky and require some thinking about handling detection of edits which should modify or remove it and about manual removal. It might be less wanted if we implement the highlight and temporary unfold when hovering over a fold icon. If we add that then users could quickly view the content of a manually folded range without unfolding it. One other future feature on my list which I forgot to mention above: Ability to fold/unfold all regions of a specified type. This already exists but is minimal. It works for comments and "regions". But the folding provider interface, the settings related to "regions", and the "folding imports by default" weren't thought out as a generalizable subject so it would take a bit of rework. To allow language specific concepts like "functions" or "chapters" to be defined and used. I understand you're not keen on the parameterized folding thing. But I think it would be used a lot. There are a huge number of syntax highlighting extensions for vscode. For many (most I think) of those languages it isn't worth the effort to create language providers nor folding providers. And some people would write custom ones for things like event logs. I think that even for Python a folding range provider was only added recently, and it had teething problems. If I continue working in this area this is the part I'd really like to do next. |
@PieterBranderhorst I started working on #155302 to simplify the merging rules. |
These changes accomplish two things:
1a. When a folding range provider misbehaves or responds slowly.
1b. When ranges temporarily disappear due to changes such as removing the end marker of a comment block.
The saved view state for an edited file includes the currently hidden ranges. To prevent these from being incorrectly applied when the file is changed via an external process and then reopened in vscode, a checksum of each folded range has been added to the saved view state.
This command wasn't possible before change (1) above. It is added now because it is possible, seems very desirable, and is useful in testing (1) above.
Testing: All previous fold functionality should remain the same. Except when a user manually folds a group of lines which overlap a fold group defined by the range provider. In that situation the code now removes the overlapping region(s) from the range provider until the user unfolds the overlapping manual region. The new functionality can be tested by using the new ctrl+k ctrl_. command and/or creating open-ended comments which surround previously folded regions.