Implement code folding feature in the editor#51
Conversation
|
Hi rakasha681, thank you so much for creating this feature. Someone requested this three years ago. I appreciate all the time you put into this. It will be a great addition to RepDev. Last night I took a quick look at it and it looks very nice. I did run into a problem though. I opened a RepGen, pressed Ctrl-Shift - to collapse all sections. Then I pressed Ctrl-Shift + to expand all sections and 3 random blocks of code were moved to the end of my RepGen. Have you ran into that? I was going to create a dummy RepGen so I can simulate it for you, but did not have the time. I'll try to get to that sometime this week. Thanks again for all the time and effort you put into this :) Bruce |
|
I hadn't run into that, but I didn't test the fold/unfold all heavily enough apparently, I was able to reproduce, and its definitely either a race or not quite accounting for line number shifts as its unfolding. I did it bottom up since that seemed deterministic, but it's not necessarily especially when there are nested folds. Let me see what I can do, I have a couple thoughts. |
… the folded-region state instead of expanding each fold one-by-one while line positions are shifting. (feat) Enhance folding functionality by updating line count calculations and displaying line numbers that account for the folded text
Give that a try now, instead of walking the folds, it now rebuilds the editor in one shot. Also accounts for those line numbers that are folded in the gutter |
|
Hi rakasha681, I just tried the new version and I am still getting blocks of code moved to the bottom. My RepGen is 750+ lines with 14 procedures. Bruce |
|
Hrm, I did a bunch of testing on one of mine that's 1700 lines and 30 procs, with a lot of nesting, and can't seem to reproduce now. I did make a few other tweaks, mainly adding line length guides, so just added those in case they help with that somehow, lol. My only other thought is something in how your formatting is that I didn't already catch as an edge case in any of mine. you can send me an example that does it if you have one you don't mind sharing. |
|
I just tried again and still the same. I'll try to get some generic code to fail and post it here for you to try, hopefully tomorrow. Thank you very much. Bruce |
|
I was able to get this snippet to fail. Give it a try and let me know if it fails for you too. |
|
oops, I just saw your commit for 04bc8d1. Use my test code to see if you can get it to fail. Thanks. |
|
Thanks for all the testing! I think I tracked it down, along with a few other things that would have eventually popped up. One difference in a lot of my test files is they have been touched by windows, which helpfully adds a newline at the end of files, *ix flavors don't, so editing things that were created in RepDev/Symitar that never saw windows would be missing that, which could cause line number counts to be off by 1 which would get an additional +1 for every fold, which would explain things ending at the bottom. Few other minor edge cases that could cause that too. I also noted it was re-parsing vars with every fold, which isn't too much of an issue with manual clicking one fold at a time, but when you start folding 30+ with fold-all, and it reparses for each one, that was getting out of hand, so now it batches the parse so there's only one done after the folding or unfolding is done. Using undo-redo could also cause issues because folding wasn't included in the history, so I fixed that, so if you edit something, then fold, then hit undo, it doesn't try to jump straight to undoing the edit which could have been inside that fold, it undoes the fold, then another undo undoes the edit.. makes a little more sense that way :D Added a couple console prints to surface any remaining failures hopefully! I did just try the test file and seems to be OK, but if you get any more unexpected unfolding, see if there is an error in the console. |
|
WOOHOOO, looks like this last fix worked. I will give it some thorough testing and let you know if I find anything else. I usually run the Beta versions for a couple of weeks to make sure there are no other issues before I push it out. Thanks for the quick patching, I appreciate it. Bruce |
|
Yup, I actually run my own special build too, that has some functions that other users will probably not use so I keep it to my self as well. A few months ago, someone had offered to migrate RepDev to Maven for me. There aren't many code contributors these days. I also do not like the idea of things being updated without me knowing about it (me being paranoid). My builds are primarily for users that are not able to build the binary themselves. They trust RepDev so I need to do my best to keep things simple and transparent. I know there are a lot of users out there that keeps their own build, too, and may pull in some updates/enhancements from here occasionally. I prefer not to create a separate branch because that would be more things to maintain. Thank you for the offer tho :) Just a quick update, I did a bit more testing and still looks pretty good. I do see one issue tho, and I don't think you can do much about it. If you have the option selected to show unused variables, it will give false hits when the sections are collapsed and you click Save. At some point, I will turn this into an "option" in settings so users can enable or disable. And within the Options screen I will alert the users that these two options should not be enabled at the same time due to the false hits. Or at the very lease let them know if the issue so they are aware. Next week, I will do more testing with the expand and collapse. Once I'm comfortable, I will merge it into my own build to use/test it daily. |
|
My first venture into computer programming was in the 80s, with a hand-me-down TRS-80 and '101 BASIC Computer Games' hardcover, which is infamous for typo's in the earlier editions as well as mixing multiple dialect without explanation, so often, even if you typed in the program exactly as it was on the page, it wouldn't run... so my first lesson in programming was "everything seems impossible until you figure out that everything is possible." :D So, much like SWT having no direct support for folding, anything is possible if you create it... the question is, is the effort worth the outcome? There are actually a few things it's affecting that I've been looking at ways to fix. Find/Replace, and Goto Definition also ignore folded text. There is basically three ways I've come up with to fix it.
I think #3 is the best fit, because #1 is too much debt for the payoff, and #2 is too flaky. Shouldn't take too long to knock that out. |
|
I started in the 80's as well. I spent my own $100 (a lot of money at that time for a HS kid) and bought a Timex Sinclair 1000. To my dismay, it went on sale a month or so later for like $50. I think it was around the time that they were discontinuing it and the IBM Clone were starting to come out. Even option 3 sounds like an expensive proposition. With RepDev the way it is, it may fubar everything. I think the first step is to get this rolled out as is and let the users know what to expect. BTW, I was reviewing the code and noticed your 3rd commit (428427b) is not related to this feature. When you get the chance, would you roll that back. I like to have 1 pull 1 feature to keep things clear. Thank you very much, I appreciate all you've done. Bruce |
…and batch processing
04bc8d1 to
2f50df8
Compare
…tionality and enhance parser to handle hidden text
|
Its actually not too bad, more comment than code changes. Shims are nice for isolating a behavior change without re-writing core flows.. basically controlled duplication (the double tokenization), which enables all existing user experience to remain the same, outside the additions of the code folding feature, everything else still works exactly as the current main does :) Did a bit of testing and looking good on all previous functionality with the new commit. I rebased and dropped the line guides, easy enough to PR a branch for that later if wanted. |
|
Thank you for the quick update. In general that works great. I did find scenarios where it moved the block of code. Here are the steps; Fold the DO above it The PROCEDURE OUTPUTHTMLBODY does not expand, but the code found is below it. BUT, if you collapse the whole PROCEDURE OUTPUTHTMLBODY, the Find/Replace does not move the code around. only if you leave some uncollapsed code within the collapsed code. Let me know if you have any questions. Thanks. |
… in the FoldingManager
|
thanks for the detailed step by step! Made it easy to reproduce and track down. |
|
Great. fix looks good. I will continue testing. Thanks. |
|
Just a quick update; things are looking pretty good so far. I've not experienced any more code moves so far. Two things I noticed. When the DEFINE section is collapsed, IntelliSense does not pick up the variables. Not sure if you can do anything about that. Maybe, at the very least, if the users selects Ctrl-Shift - (collapse all), collapse all except the DEFINE section. Second thing is that the syntax check does not show errors when the sections are collapsed. This is a bit of hit and miss tho. I've not been able to find a pattern. I press Ctrl-S to save the RepGen and make sure there are no errors, then go into a procedure and delete a character from one of the variables, collapse that procedure, sometimes I have to collapse all, then press Ctrl-S for save and I do not see any errors displayed. But if I press Ctrl-Shift +, expand all, and press Ctrl-S for save, then the error message shows up. I will keep testing. Thanks. |
|
Hey, yeah, it's because collapsed text is actually physically removed from the editor and stored as a snapshot in the buffer. The current approach physically mutates the StyledText buffer, which makes folding pervasive, every component that touches text has to learn about it independently, and each one has its own way of be fixed, so its been a game of whack-a-mole. I'm looking at finishing the model/view split, which after all the misc little fixes, is actually almost fully implemented with a little repurposing here and there. With that change, the model (full source) is canonical, view (StyledText) is a projection. Everything non-UI, like the parser, var table, error positions, IntelliSense, etc, will all read from the model. The FoldingManager owns a bidirectional offset mapping (modelOffset ↔ viewOffset) and a "visible?" predicate. With that change, the "folding broke feature X" bug class is gone completely because features can't accidentally inherit fold-awareness because they'd have to opt into view coordinates to even see folds at all, so no more whack-a-mole. Shouldnt take long, its mostly just callsite updated. |
…RepgenParser to utilize canonical source for variable management which fixes a lot of our current folding bugs (step 1 in model/view split, step 2 will follow with full implementation, which will prevent all similar bugs from happening going forward)
…hecks only run for connected sessions, enhancing feedback for local files, which is needed for using and testing code folding features while not connected to symitar
- Introduced ExtendedModifyListener in FoldingManager to handle text modifications and manage fold states correctly during user edits. - Implemented model-to-view and view-to-model offset translations to ensure accurate caret positioning and visibility of folded regions. - Updated SyntaxHighlighter to utilize model coordinates for styling tokens, ensuring correct visual representation even when parts of the text are folded. - Refactored task scanning in RepgenParser to derive line and column information from the canonical source, allowing for accurate task reporting regardless of folding state. - Enhanced error handling and navigation in MainShell to accommodate model coordinates, ensuring that error and task highlights remain functional with folded text. - Added methods in HiddenTextProvider interface for translating between model and view offsets, facilitating better integration with the folding mechanism. The parser layer (ltokens, lvars, section info, error positions, task positions) is anchored to the canonical (unfolded) source. Every offset stored there is a model offset and remains correct across fold/expand cycles. The view (StyledText buffer) is treated as a projection; UI-bound code paths translate at the boundary via three places only: FoldingManager's translation API (raw helpers), EditorComposite's tokenStart/EndView + gotoModel* (token/offset helpers), and SyntaxHighlighter.lineGetStyle (per-line styling translation). - IntelliSense sees DEFINE-folded variables. - Symitar error rows always reach the table, even while folded. - Click an error row while its line is inside a fold → fold auto-expands and caret lands on the correct line. - Goto definition into a folded var/procedure auto-expands. - Goto section / section navigation works against folded files. - Block matcher (Ctrl+Shift+P) finds the matching head/end even if one side is inside a fold. - Tasks (TODO / FIXME / etc.) discovered inside folded regions are listed and clickable.
…ectionParser to prevent out-of-bounds exceptions during background parsing, especially relevent on long files with sections folded while a user makes edits.
…callback in HiddenTextProvider and updating related classes to ensure accurate fold range computation after parsing edits. Fold-All now always unfolds all first so that it starts with a clean snapshot and never have stale ranges for manual folds/edits that shifted line positions since the last snapshot.
|
Thank you for all the work you've done. I was able to build it. I will continue testing. Bruce. |

I had to drop the sun bool imports for it to compile for me, can add them back in if needed.