A Missing IDE Feature
matklad.github.io
matklad.github.io
"Fold Method Bodies by Default" is something I deactivate immediately. After two decades of reading code, I am optimized to read syntax-highlighted code pretty hard. This just hides the stuff I am looking for - I read code because I want to know what it does.
For an overview I look at the outline. With wide screens, I think it is a much better place for a structural overview. I would be more happy to see advancements there.
Automatic folding is something I almost always disable because I hate it when an editor tries to hide information from me. I strongly prefer to choose when I want things folded but by default to have things be as close as possible to the native text format just with syntax highlighting.
This is particularly true for PRs and diffs, where the details of implementation really matter and I would consider reviewing anything which was folded to be potentially extremely risky.
Sadly I believe this assumption is incorrect. VSCode supports folding, but not specifically method bodies (to my knowledge), and not by default (with or without a settings flag)
Whether this is on by default or not is not the issue. It's just getting "smart" folding in vscode that would be the feature win.
I find all kind of code folding massively throws off my spatial sense of the code. And if I'm browsing random code, then I often need to see the body too, to see if this is the place the work is done, or is it behind some abstraction down the line.
Perhaps I don't need it as much on c++ codebases, as the headers in a sense are that already. And Rust codebases I've dabbled in haven't been quite that big or overwhelming.
The outlineview/(the popup on top of buffer in many editors that lists the outline) is enough to finding my place in a file I feel lost. And if just looking how to use a lib, there is often some API documentation that helps along finding the right things (even if just barebones doxygen or rustdoc).
I'm sure this is as much of a habit thing as other editor preferences. Still I find it surprising how many people like code-folding :)
The default, as made clear in the article, would be to do nothing different than today. This describes an optional feature.
I think what this article is getting at but doesn't quiet explicitly say is people look at code for different reasons. If you're fixing a defect and you think you know where the problem lies, you may want everything to come up folded so you can get to the function you think you want to look at quickly. If you're trying to get a handle on what the code in the file does, you may want everything to come up unfolded. Or maybe you want to unfold all functions that take a particular data structure as a parameter.
The global settings at my installation for the LSE (Language Sensitive Editor) I mentioned earlier were set to fold all .c files. I initially found it annoying, but quickly learned the "unfold everything" key sequence. And then the "unfold this function once" which didn't unfold the contents of loops or ifs and the "unfold everything in this function." I was able to quickly get used to it and spent a year replicating the functionality in emacs.
But... I completely agree with people saying this should be a configurable setting.
And... if you implement this, please allow selective unfolding of different parts of the code.
There's also something to be said for the idea that code has a 2D shape to it and recognising parts visually can make navigating it a little easier if you're bouncing between a couple of areas for a particular change. I wouldn't want to obscure that, not by default anyway. But to each their own!
> I think it is pretty obvious how awesome this actually is. Code is read more often than written, and this is one of the best multipliers for readability. Most of the code is in method bodies, but most important code is in function signatures. Folding bodies auto-magically hide the 80% of boring code, leaving the most important 20%.
I see where they're coming from, especially for codebases you already understand. But my workflow for new codebases is to at least visually inspect the method implementation before deciding whether or not to hide it. Does it actually look like boilerplate? Does it invoke any functions I haven't seen before? If it seems nontrivial, read it! Does the computation basically match my English-language description of the signature, or is there something deeper I missed? If everything was hidden by default I would either waste time un-hiding everything, or make lazy mistakes by saying "eh I'm pretty sure that's boilerplate." Coding by gluing together function signatures without reading the implementation is a recipe for making business-logic bugs.
My problem is similar to why I don't like using LLMs: taking the 80-20 thing at face value, you don't know which 20% of methods have nontrivial internal semantics which aren't conveyed by the type system, unless you already have a solid understanding of the code. (Note that Rust's type system does actually evade mutability / concurrency issues. But not incorrect business logic: it is not yet Lean or Idris.) I just don't like rolling the dice on this stuff - business logic bugs are horrible! They are difficult to suss out, especially if you weren't fully diligent in understanding the logic in the first place. Why would you roll a d5 on that?
Part of my aversion is that I am lucky enough to not be in a hurry. At my last job - 70 hrs/week of R and Python, reading papers, and writing emails - I would have been much more tempted for these "multipliers for readability," especially ChatGPT. But when you're not in a hurry, you are more sensitive to these multipliers also multiplying laziness and misconceptions.
I don't think the example is all that good, because it's not the same code in both screenshots. The suggestion, as I read it, is that folding the "impl Body" shouldn't collapse the entire thing to impl Body { ... }, but instead collapse all the functions inside it, because that's actually more useful. So you'd get
impl Body {
fn new(db: &dyn DefDatabasse) { ... }
pub fn shrink_to_fit(&mut self) { ... }
}
It this was Python and you collapsed a class, then rather than getting class User(AbstractUser): ...
you'd get: class User(AbstractUser):
def is_manager(self): ...
def get_username(self): ...
It would perhaps even be a little neat if you could have a class collapsed and you'd be shown only public methods, if the language has the concept of public and private methods.To build on the "hiding awful code", I think this could help show awful API/interface design. If you have a class with a few hundred lines of code and a number of methods, then you rarely get to see all your methods listed, their signature just drowns in a sea of code. The also add doc strings, then you now have a documentation view of your own code in the IDE.
Write cleaner code or use a higher level language that allows for more readable code. For example, some Go developers (...mainly Rob Pike, granted) eschew even syntax highlighting, as readable code shouldn't need colors to make sense of it.
To clarify, the context of this article is using tools to understand someone else's code. (The author's setting: ">Suppose you are casually reading the source code of rust-analyzer, and are curious about handling of method bodies. There’s a Body struct in the code base, and you want to understand how it is used.")
In that particular perspective, the IDE is the root cause because it can be purposely designed to highlight/emphasize/annotate/correlate/hide ... various aspects of the unfamiliar codebase.
If the author then later chooses to refactor the code, that's when your advice of "write cleaner code" would apply.
Tools can help but isn't the cure-all. I would prefer gradual folding (e.g. fold levels 4+ or fold/unfold one level).
-- Top of file:
data Indexer =
Indexer { indexDocuments :: !(CollectionName -> [Doc] -> IO (Either String Int))
, indexLocalWarcFile :: !(CollectionName -> FilePath -> IO (Either String ()))
, deleteDocument :: !(CollectionName -> String -> IO (Either String ()))
, isDocDeleted :: !(CollectionName -> String -> IO (Either String (Map Text Int)))
}
-- Further down:
indexDocumentsImpl env writer metadataApi compactor registry collectionName unsortedDocs = do
let ds = sort unsortedDocs
let nDocs = length ds
...
newLocalWarcFileIndex env warcFileReader writer metadataApi compactor registry collectionName warcFile =
batchedRead warcFileReader
warcFile
newIndexify
where
newIndexify :: Vector WarcEntry -> IO ()
newIndexify ds = do
...
etc.I try folding from time to time but never really can get used to it (even though I work on massively big source files where folding function bodies actually should make sense). If your code requires code folding to be readable, it's really better to restructure your code to be more readable (and often it is better to not hide complexity because you can see immediately where the gnarly parts are - and by hiding those you're not doing anybody a favour).
has been around for at least a decade -- and that is basically helper code on top of functionality that's been available for longer than a fair few people reading this have been alive.
> First, only method bodies are folded. This is a syntactic check — we are not folding the second level.
edit: by way of https://github.com/mickeynp/combobulate/issues/27, it's been done: https://github.com/emacs-tree-sitter/ts-fold
Code folding by default triggers thoughts of the annoying, mostly useless TypeScript d files that VSCode dumps you in to when you ctrl/cmd click into a function. The only reason I do that is to see implementation details so I can figure out why something's not working. If third-party library d files were replaced by pre-folded source, I guess that'd be OK.
Turns out that JetBrains format the entity as the resulting character... a feature that was meant to make code readable had confused me. I had similar issues with clion which presents weird notations on the code with no indication of their purpose. This is a double edged sword.
Also, while not automatic, if I use GoTo in sublime text, then Edit->Code Fold-> Fold All, it will fold everything I have not focused on, all the way around my code selection or indent level. Seems like you could cobble together a simple plugin or shortcut to handle the expected behavior.
Keys: Cmd+r, goto, cmd+k cmd+1 fold all
In neovim, I prefer aerial's telescope extension: https://github.com/stevearc/aerial.nvim?tab=readme-ov-file#t...
I don't like folding because toggling folding state is clunky with keyboard only. I'm okay with fold/unfold all, but doing it manually fells like a chore.
This is, of course, my experience with mostly unexciting Java and Python; there should be options, maybe per language or per project, or even heuristics (e.g. fold long methods, by character count and/or line count, and recognized getters and setters but not other short methods).
If you are using "Go to..." a struct declaration or impl in Rust (or a class in other languages) it actually makes a lot of sense. If I'm going to a struct/impl/class I don't know yet which method do I won't to see. If you are just opening a file that you haven't open before though... I'm not so sure.
I tried to build it but the extension API is just not there for it. I’d happily pay $$$ for something like this if anyone feels nerd sniped.
So getting this right and ergonomic I think is important regardless of whether there is a symbol tree or similar (which can also be useful).
Even the Atari 2600's Basic Programming had that. (It's an entire IDE in 4K of ROM.)
BTW: In Eclipse JDT you can configure what kind of Java elements should be folded by default. The default config is that leading comments and imports are folded. Would be nice to be able to configure something like that via LSP.
Jetbrains (possibly a plug-in - focus on task els.) have a very-poor-man's version which hides all files from the filetree except the ones opened in an editor.
I never tried the issue integration out myself but agree that it's a good idea.
Of course, we all are trying to write clear code. It doesn't mean that your tool should make working with less than perfect codebases unbearable.