So… my last post was somewhat sad – I tell about how impossible is to keep the Atom editor. Unfortunately, it still holds true – it’s close to impossible to handle the insane amount of code that Atom have.
But maybe is there a different way? So I decided to try: presenting, Project Saturn: a huge editor, but less dense. That’s the idea.
Saturn always fascinated me, and still fascinates: it’s a huge planet, but less dense than water. With a sufficient big bowl of water, Saturn would float. That’s precisely the idea I want in this new editor: to be less dense to keep, while somehow maintaining the power and “wow factor” of the editor. So, how to do it?
Reusing the Atom Editor’s Editor
At the beginning, I tried to reuse the “editor component”. Unfortunately, it’s not that simple: the Atom text editor is HUGE, and it gets worse: it uses seven native libraries to work correctly, some of them that do not compile on Electron >11, and also the code is split between code on Atom repository, and code on NPM modules (that, unfortunately, use undocumented APIs and editor’s internal state to work). So I decided to not use the component at all, much to my sadness because, honestly, it’s a great hackable editor component, but it’s just too much work. So, for now, I chose Monaco editor – it’s fast, it’s the same editor that’s on VSCode, but this version is more flexible than the one on VSCode, for some reason… if it proves to be too complicated, I can always change to CodeMirror – and that’s the beauty of it.
Reusing the Docks and Tree View
Using the Dock component I had more luck: with some usage of internal state, I could make it work correctly. Tree view was a little bit harder: it contains CoffeeScript code, so I made a script to convert Coffee to JS, and also added some polyfills on Shadow-CLJS to ignore some native components that I do not need for now (like “watch for changes” in filesystem). In fact, this may be one reason why Atom sometimes gets sluggish: it seems that every place that needs to watch files adds a watch by itself, instead of reusing a “global watch system”. This can change on Saturn too.
Atom trigger events with EventKit. I am redirecting these events to re-frame, which have some interesting properties: first, it’s all global and easy to track, and second, I can have schemas and changes. This can be a problem in the future, because some of these events that are triggered expect to return a Promise. For example,
atom.workspace.open triggers a
:open/file for now in re-frame, but the API returns a promise when things happen, so… I’ll have to handle this somehow.
Another interesting take of using re-frame is that, now, the editor is hot-reloadable: I can save files and see the results immediately. I will probably start to write some tests now, and add some Malli schemas to not become crazy with such a big project, and see if I can wire up tabs.
Use Atom components as much as I can, and support at least sufficient APIs to make some plug-ins work. Add some VSCode plug-in support too, and see if I can reuse both Atom and VSCode plug-ins. Add as much code to allow for some plug-ins that I use to work, and make the editor in a state of “works for me”. Then try to replace some “native components” with WASM equivalents if possible, so that editor installation and developlemtn does not depend on a compiler installed on the system.
Also, avoid some bizarre decisions like usage of lots of internal NPM modules to “be easier to test” (that’s the explanation the Atom team gave for some separation) and also avoid tying a specific Electron API for example (so maybe it’s easier to port the editor for other environments). Finally, make everything be a plug-in, with a facade with some API. This means that most, if not all, components can be interchangeable. It also means that, with sufficient effort, the editor can be ported to “pure terminal”, “full web”, and even Android or ARM system if one decides to do it.
Will I do it?
I don’t know yet. I tried to pitch the idea in multiple channels. In some channels, the answer was 100% silent, and others, it was met with excitement. As of today, I received zero people wanting to help, even from people that was excited.
Honestly though, it’s probably too much work for too little gain. It’s hard to convince people to change editors, as I discovered over the years. Over the course of other open-source projects I did, I found multiple people asking me to change a specific library to one that’s “supposedly better”, or to change a coding style, or to change a specific implementation, etc. Most of the time, these people do not contribute with anything in code. Yeah, I know I’m being harsh here, but it’s the true – so at this current implementation, I want to be sure that while I would love to work on this project and make something interesting of it, I know that I’ll probably be doing 95% of the work alone, if I’m lucky to find someone that could do the 5%. So… I’ll make no promises to end this project at all.
Reagent with local state (
r/atom for the intimates) and re-frame for the global things. I sure won’t use IndexedDB for anything – I’ll just use SQLite3 for persistence. I’m thinking about adding an undo for some configs, so I’ll probably will do some “config versions” by hand. I though about using
Datascript, but in the end, what I’m doing is converting DOM Events or EventKit events to Re-Frame, then to Persistence in SQLite3. If I add Datascript on the mix, I will have yet another conversion to do between Re-Frame and the persistence layer for no reason at all, so, yeah, this will not be the time I’ll use Datalog, again (I’m starting to think that Datalog is yet another “noSQL fever” that will come to pass).
ClojureScript, for obvious reasons: hot-reload (yeah, the editor must be 100% hot-reloadable, otherwise it’ll be just too difficult to work on it), REPL-Driven (again, it’s a requirement – so things that can’t connect a REPL, like Node-Test or ESM modules are no-go), and also because it’s incredible easy to add polyfills and intercept npm modules. This translates to “if Atom uses something weird, I can redirect the
require to a JS file that I keep on my side, and everything will keep working just fine” (and indeed, I already did that).
Finally, an interesting thing (but not requirement) for the project could be a hot-reloadable init script. Again, it’s somehow easy to make this with ClojureScript and SCI (and in fact, I could probably even do autocomplete with the API with it, based on my work on Chlorine. Another thing that could be interesting to try is to make the editor query-able, initially with EQL (Pathom). These are stretch goals, over a project that for now, its still written on beach sand… but it could be interesting!