Rust Glancer
Rust Glancer, a functional<br>LSP server for Rust which uses two orders of magnitude less RAM, is incredibly<br>cool. Go check it out! This post started as a comment on lobste.rs, but I<br>figured it out that it’s better to publish it somewhat more prominently. Don’t<br>expect polished writing though!
Some thoughts:
rust-analyzer uses rowan for syntax tree representation
Yeah, rowan is garbage :P I was really thinking about
incremental parsing,
incremental, DOM-mutation style refactorings,
And Rowan is pretty good for that. But that’s 1% use case. The 99% use case is<br>all the code in your 6666 dependencies which you won’t ever look at, but which<br>needs to be at least shallowly analyzed. Even for incremental tool whose main<br>goal is refactoring, the primary AST structure should be just a list of arrays.<br>There might be a real post about that at some point, see<br>https://youtu.be/G93oYL1ry70 as a teaser.
Rust workspaces genuinely have a lot of information that must be indexed:<br>thousands of functions, structures, traits, relationships between these,<br>function bodies and statements in them, etc. Each of these needs to be<br>analyzed and remembered, and you can’t really cheat if you want to have things<br>like “find all references to this structure”.
If I understand correctly, Rust Glancer wants to process each function body. I<br>think that part can perhaps be made lazy (but not incremental!) with little<br>overhead? Index all items, but, for functions, do only the currently opened<br>file? This might combine some of the better parts of both worlds.
Would be interesting to compare memory usage with Rust Rover. Net of the IDE<br>GUI itself, I would expect RR to be more compact.
Some features are unlikely to be supported though, such as build scripts /<br>proc macros support via proc macro invocation
I might be rationalizing/misremembering things, but IIRC it’s exactly around<br>adding proc macros that the thing began to feel unreasonably bulky. Expanding<br>proc macros is slow as we are running real code, we can’t really do normal IDE<br>cheats. And proc macros generate a lot of code. At one point I measured, it was<br>like 30% of rust-analyzer binary size was attributed to JSON parsing code. If no<br>one sees the code, it can’t harm anybody, right?
One potential approach here is to pull the Sorbet trick, where you don’t run<br>meta programming at all, and instead have a plugin interface to “explain” the<br>effects of what that would have done. Instead of running serde, we just add a<br>shim that injects imp Serialize for T {} with an empty body.
I’m not sure why, but in rust-analyzer I’ve observed that when agents edit the<br>code, inlay hints can get out of place
Rust analyzer’s core data model is very pedantic about always observing<br>consistent snapshots of the code, and does its best to ensure that the language<br>client and server have a shared, strictly serializable view of the world. It’s a<br>shame that<br>LSP doesn’t allow that to be correct, only heuristically right,<br>unlike the older Dart Analyzer protocol, which has sound data synchronization.
However our implementation of file watching is sketchy! First, there are two<br>backends: we can ask the editor to do watching for us, or we can use server side<br>watching. Try changing<br>this option<br>and see if it helps? But then, yeah, my recollection is that our native watcher’s API was<br>fundamentally racy, and I didn’t do the messy platform-specific work of making it correct.
But the main thing I want to write, and why I moved from the cozy lobste.rs text<br>area to the comfort of an Emacs buffer, is that right now rust-analyzer is a bit<br>like that half-drawn horse meme, except that it’s only the head half of the<br>horse.
One Big Idea of IntelliJ is that it’s PSI API (essentially AST with resolved<br>types) is really an interface, and there are multiple provides. And in a typical<br>usage, there’s at least three backends in play:
For the files opened in the editor, actively modified by the user, the PSI is<br>backed by the concrete syntax trees.
For the rest of the project files, the PSI is backed by the so called Stub<br>Tree, a compact on disk representation storing only the “externally visible”<br>parts of the file (so, without function bodies). If the user navigates to a<br>new file, its PSI transparently switches from stubs to syntax tree.
For dependencies, the PSI is often backed by the compiled .class files,<br>produced by javac. If you navigate there, the IDE just decompiles stuff four<br>you! Super cool!
This is how I think such things should work. rust analyzer shouldn’t use<br>salsa for all those 6666 dependencies you still haven’t looked at. It should<br>just use rustc’s .rmeta files, switching to salsa, transparently, only when the<br>user starts messing around their ~/.cargo/registry/src folder.
The prerequisite for that is defining the abstract API for accessing Rust code.<br>That was always the plan, and we did start on that at some point:
https://hackmd.io/ytd82QNiT_Ku2XFr1EAtiQ
rmeta-transparent – source code might not be...