A piece of intro to Calcit Editor

It’s an old project from me and it can be traced back to 2015 when I was generating JavaScript from S-Expressions. Now I’m just trying to update the README a bit so that people might get to know what I was trying to acheive easier.

It started with a simple idea: what if I just write code and let the editor handling layout for me? You might have seen other answers, Calcit Editor is my answer:


Calcit Editor

Intuitive S-expressions editing for Clojure(Script).

  • Auto Layout: expressions in blocks and inline-blocks, styled with CSS
  • Tree Editing: intuitive way of structural editing as nested expressions
  • Call Stack Navigation: more fine-grained functions navigation
  • Collaboration: changes real-time synced among multiple clients via WebSockets

One function/definition in a screen, Command d to open called function at next tab, Command j Command k Command i to switch:

Based on DOM/CSS, easy for another theme:

Command p to search and jump inspired by Sublime Text :

Browse namespaces and functions/variables:

Usages

npm CLI of calcit-editor

Install CLI and start a local WebSocket server, it uses calcit.cirru as a snapshot file:

npm i -g calcit-editor
calcit-editor

UI of the editor is a webapp on http://calcit-editor.cirru.org/?port=6001

You may try with my project templates:

or even clone current repo for trying out.

Don’t forget to check out keyboard shortcuts. My old introduction videos can be found on YouTube.

Options

CLI variables for compiling code directly from calcit.cirru:

op=compile calcit-editor

The web UI takes several query options:

http://calcit-editor.cirru.org/?host=localhost&port=6001
  • port, defaults to 6001
  • host, defaults to localhost, connects via WebSocket

By default, ClojureScript code is emitted in src/ by pressing Command s.
When server is stopped with Control c, calcit.cirru is also updated.

There are also several options in :configs field in calcit.cirru:

  • port, defaults to 6001
  • output, defaults to src/
  • extension, defaults to .cljs

Editor UI is decoupled with WebSocket server, so it’s okay to connect remote server from multiple pages with all expressions synced in real-time.

Also there’s a local version of web editor to enable:

ui=local calcit-editor
# serving UI at http://localhost:6101

Workflow

Based on https://github.com/Cumulo/cumulo-workflow


…some feedbacks can be helpful :smiley:

6 Likes

Last time I checked a Cirru related topic on Reddit I tried to gather some histories related to Calcit Editor. I realised that the earlier version of Calcit Editor, would be more appealing to people who want to try and explore. That project was Stack Editor:

The project has not been updated for 3 years. shadow-cljs was just release 0.x at that time. So I did some updates to the project based on my current toolchains.

Difference from Calcit Editor

I highlighted the main differences between Stack Editor and Calcit Editor:

  • Stack Editor uses stack.cirru as the snapshot file, which is mostly vectors in Cirru EDN and human-readable.
  • Stack Editor connects server via HTTP, which is a bit clumsy for data syncing.
  • Stack Editor loads all the code in browser, so available for more analysis.
  • Stack Editor has not been actively maintained in years, fewer features…

The snapshot file was ir.edn and after refactoring in these days, the file became stack.cirru. It’s an indented version of EDN. I believe it’s more compact and diff-friendlier than EDN. You might feel it unfamiliar to read though. Try my Cirru EDN -> X converter if you would like…

{} (:package |app)
  :root $ {} (:ns |main) (:def |main!)
  :files $ {}
    |updater.router $ {}
      :ns $ [] |ns |app.updater.router
        [] |:require ([] |[] |app.util.stack |:refer $ [] |[] |get-path) ([] |[] |clojure.string |:as |string)
      :defs $ {}
        |toggle-palette $ [] |defn |toggle-palette ([] |store |op-data |op-id)
          [] |update-in |store ([] |[] |:router |:show-palette?) (, |not)
        |route $ [] |defn |route ([] |store |op-data)
          [] |let ([] $ [] |router |op-data) ([] |assoc |store |:router |router)
        |open-file-tree $ [] |defn |open-file-tree ([] |store |op-data |op-id)
          [] |let
            [] $ [] |code-path ([] |get-path |store)
            [] |-> |store
              [] |assoc-in ([] |[] |:router |:name) (, |:file-tree)
              [] |assoc-in ([] |[] |:graph |:ns-path)
                [] |vec $ [] |string/split ([] |:ns |code-path) (, ||.)
      :procs $ []
    |comp.brief-file $ {}
      :ns $ [] |ns |app.comp.brief-file
        [] |:require ([] |[] |hsl.core |:refer $ [] |[] |hsl) ([] |[] |respo.core |:refer $ [] |[] |defcomp |div |list-> |<> |span |input) ([] |[] |clojure.string |:as |string) ([] |[] |respo-ui.core |:as |ui) ([] |[] |respo.comp.space |:refer $ [] |[] |=<) ([] |[] |app.style.widget |:as |widget) ([] |[] |app.util.keycode |:as |keycode)
      :defs $ {}

a whole file: https://github.com/Cirru/stack-editor/blob/master/stack.cirru

Stack Editor has the very basic features of Calcit Editor: creating files, editing functions, goto definitions(for internal ones), search and jump… I used it for development for at least some months.

The reason I moved to Calcit Editor is I got WebSockets for syncing every change from users, it’s more reliable and more extensive. But there are indeed several nice features I can’t clone to Calcit Editor.

Dependency graph

In Stack Editor, the whole snapshot file is sent to browser via HTTP. That means I have chance to analyze all entries just in browser. The picture shows how each function is depending on other functions, starting from app.main/main! function:

Goto references

In Stack Editor, if you press “Command u”, it will find functions that are using current function. It can be quite useful sometimes.

I can hardly add this feature in Calcit Editor since it only loads small parts of code in browser. I might have chance to do that on the server-side, but turned out to be a bit expensive. Besides, another person might be editing the code in realtime and bringing more edge cases to handle.

Storage file editing

As shown above, stack.cirru is quite a simple format, good for git merge, good for manual editing. Run op=compile stack-editor code regenerated again. If you prefer EDN, just fork project and use EDN. Previously I added a package called cirru/favored-edn for writing EDN in a way similar to JSON, it was also available.

Meanwhile calcit.cirru from Calcit Editor just looks like a mess. I want to say calcit.cirru is mostly maintained by the CLI, watching file, syncing to browsers… in a lot more details. Those things can be hardly finished if I just use simple HTTP connections.

Implementations

The UI part of the editor is rendered with Respo, my own virtual DOM library. And its editor component is splitted in respo-cirru-editor , code generation using sepal.clj like in Calcit Editor, so not much code was left inside stack-editor repo.

If you want, just reuse the storage format, code generation library, and create your own version of Stack Editor. Analyzing basic informations from a tree is way easier compared to analysing Clojure code.

Yes, the limitation is mainly from the data structure of stack.cirru. We have libraries from jars located in ~/.m2/repository. Those pieces are not analyzed in this way. I always imagined one day(fool’s days is approaching…) we store code in snapshots(like in Smalltalk?) rather than in text files. Then we don’t have to build that many parsers but instead build UIs with HTML/CSS. Then we have interperter reading snapshot file and run directly. It appears we won’t get there at least in ten years…

And yes we need another way for Macros too, sepal.clj does not generate macros since it’s using FIPP… so for now if you want macros, add another folder to classpaths for adding macros…