Some time ago I found out this post: https://petevilter.me/post/datalog-typechecking/. It stroked me as really interesting – because it was an idea to make some IDE not depend on some internal API that only works for a single editor, but somehow make a tooling that’s query able by datalog.
This datalog queries would allow anyone that want to implement new features, refactoring, or whatever it is without needing to learn lots of internal apis and complicated things.
I found the idea so interesting that I immediately implemented it on Chlorine.
The first implementation was just to ease the resolution of some vars, documentation, etc. Then it became more interesting – instead of just as a way to make my code more easy to follow, maybe I could expose a way for users to overwrite, or complement what’s already available?
So, I’ve used Pathom as the tooling to implement the code. Working with Pathom was so wonderful that it became one of my favorite libraries to handle cases when I have to aggregate and convert data – even when there’s no need to query databases or request things from APIs.
So, after the first iteration, I’ve decided to spice up things – on next versions of Chlorine, Pathom’s resolvers will be extensible. This means that if you want to define your own version of “Goto Var Definition”, or “Doc for Var”, you can – and it’s simple. In fact, I’m using it right now to find the definition of not only vars, but keywords (they will find the Pathom resolver that output that keyword).
What to expect?
Currently, every tooling for any editor that I worked with have an incredible limitation – you can only use things that exist in the tooling. This may seem obvious, but it doesn’t need to be – you only would need, for example, “where a specific variable is used” and “when a specific variable is defined” to generate lots of tooling options – rename variable, find usages, with more effort “extract function”, “auto import” and “remove unused imports”, and so on.
You can also, by defining custom resolvers, use information from the REPL, from static analysis, from LSP, linter tools, etc. So, if a tool does not support a specific detection, or if you want to aggregate new information based on some new data you may have, you can extend the resolver as much as you want – and then, everything that depends on that information will automatically use your “custom data” as input. I believe this may be a game changer.
But why?
The idea have merit. In fact, an interesting experiment was the Eve Language, an idea to make a literate programming environment where everything is based on queries. Live projects are also interesting, for example, SmallTalk (to a great extend) and CommonLisp (to a lesser) are able to make great things happen by merging the IDE and the language into a single “image” (that’s the terminology – something that is both the source code, the runtime, and IDE at the same time).
But it’s hard to make it right. For now, Chlorine/Clover is customizable with code. That means you have to learn a whole new API, a whole new way to organize your ideas, just to be able to do “simple things”. Worse yet, I need to add “extension points” manually, so if someone wants to extend in a way that I didn’t think beforehand, he can’t. I don’t like this “dictator power” over tooling, to be honest. There’s another problem: Chlorine was never meant to have this kind of power, so lots of abstractions are currently leaking: incomplete strings, datafy and nav, are all objects that are quite what they had to be, but not really so there’s lots of conversions and other issues.
There are also other problems: suppose you have an exception. Sometimes, you have to “run code” to be able to go to the filename over that exception. Chlorine/Clover needs to know if that exception happened on a Clojure file, on a ClojureScript file, or on other Clojure-like language and what REPL will it use to run code, should you need it. There’s also some issues with Clover, where the exception in rendered in a part of the editor where you can’t run arbitrary Node.JS code. With Pathom, some of these issues disappear: you expose an “EQL” over any place, and it’ll pass through whatever channels it needs until it finds a “resolver” that will try to handle your case. Sometimes, you’ll have to inform new things like “I’m currently on a JS exception” but this is way easier to do with a query than with “save these objects/protocols and then try to serialize un-serializable things like REPL interactions and then send it to the part where it’ll be resolved”…
And last, but not least, the idea is so interesting that I decided to extract a new library from REPL-Tooling. Duck-REPLed is a library that, given a “connection”, it’ll use REPL evaluations and other things to get documentation, var definition, etc. It uses Pathom3 (instead of 2) so it’s a little bit faster, and can “prioritize” a path over others, so there are some bugs that’ll be fixed in this version. And, even better, I can test-drive the tooling over a lot of different REPLs with ease – so it’s almost guaranteed that I’ll not break functionality from less-used implementations like Clojerl, for example!