this is a completely serious question: @mogenslund: is there any plans on setting up a webserver + rendering liquid output into svg
this is what I am imagining:
liquid fires up, runs a ring server, has websocket
there is a minimal *.cljs file, which talks to the jvm side, and it (1) routes keystroke / mouse clicks back to jmv and (2) gets a new svg object, which it displays
the main.cljs file is minimal and basically never changes; all the logic is doine in clojure
there should not be latency ikssues so longa s everything is over localhost
so now we get all the power of dom/svg rendering, but we don't have to deal with cljs, ahd all the logic + scripts + extensions stay in jvm land; this also allows the editor to still make system calls
[/end idea]
Hi @qqq. Thank you for making suggestions. I think I need a bit of clearification of what you want to achieve. By "liquid output", do you mean just the text editor view? An not something custom rendered within the editor? Like "(render-elephant)" which will then produce the image of an elephant when going to localhost? Right now, if you start liquid with "--server" and go to "localhost:8520" in a browser (Chrome seems to give the best experience) then you can use the editor within the browser. It uses JavaScript to update the changed lines. It should be possible to make "localhost" return svg as alternative, I just need to understand the usecase to be able to implement the right solution.
Here's the idea I've had for a while, and perhaps there is something fundamentally flawed with it since no one has done this yet. I want a Clojure IDE where: 1. the "GUI" is rendered in dom/svg on localhost:8000 2. all the logic / 'backend' is running on clojure/jvm ==== The way this would work is: 1. I run "start-ide project-dir" -- it runs the jvm, loads up clojure, and sets up a ring server + websocket on 8000 2. I point my browser at localhost:8000 3. at every keystroke, (in the browser) an event is sent back to the clojure/jvm logic code, which processes the keystroke, generates a new dom/svg tree, sends it back via websocket, and my localhost:8000 shows the new data 4. if everything is localhost, the latency should be < realtime ===== 1. Why has the backend be in Clojure/JVM, why not do everything in CLJS? 1a. CLJS in the browser can not modify the filesystem / make OS calls. I want my IDE to be able to do those. 1b. In practice, the Clojure REPL has worked better for me than any CLJS REPL. 2. Why use the DOM/SVG/WebBrowser as the GUI instead of Terminal / JFrame ? 2a. I don't have a good answer to this, besides "I just want to have the full power the DOM/SVG at my fingertips for displaying output."
@mogenslund: ^
So the idea is we take the editor/ide, split it into "front-end/back-end", and instead of having the front-end be Terminal/JFrame, we make it DOM/SVG in a browser.
I believe this would be better than: * better than emacs because: I can script in CLJ instead of eliksp; front end of dom/svg is more expressive than emacs/gtk * better than atom because I can script in CLJ instead of javascript
But is that not what you get, if you start Liquid with the "--server" flag? Then you can access Liquid from a browser on port 8520 with localhost:8520. Or you can run with "--server --port=8000" to have it on port 8000. If you have the liq.jar you can just execute java -jar liq.jar --server --port=8000 or from the source folder with clj -m dk.salza.liq.core --server --port=8000
WTF, this is already implemented? I have to try this now. 🙂
based on the video linked on the github page, I incorectly thought it was terminal/jframe only
is there a lein or boot command to run from src? for some reason, I actually don't have clojure installed on a system-wide path (i.e. it's accessible only via boot/lein)
From source you should be able to just run lein run --server --port=8000
trying that now; was just writing a boot file 🙂
whoa
I'm sold.
wait, you changed hjkl to jkli ? 🙂
I guess it makes an "wasd" style arrow
Yes I did that. And JL beginning of line and end of line. I have not done any fancy resize, but you can adjust the size at startup with: lein run --server --port=8000 --rows=50 --columns=160
this is amazing
is there something wrong with my left hand side? it's not the "repl answer" that I see in your videos
or is that a lambda ascii art .?
n/m, I get https://imgur.com/a/GVkAH now
all looks good
this is amazing, I just defined a function + executed it
how is this only 4906 linesof code w/o any dependencies besides cloljure-complete ?
@mogenslund: I don't see any cljs / cljc / js files in the source tree; where is the client side code?
The js code is just generated as strings inside src/dk/salza/liq/adapters/webadapter.clj and returned to the browser.
just found it myself via webpage -> inspect -> hmm, hand written html starts grepping for html file --> nothing found starts grepping for substrings --> adapter/webadapter.clj, see this big (str " .... ") blob this is impressive
@mogenslund: so I just finished skimming webadapter.clj ; are the highlihts the following: 1. this is done via http-get requests, not websockets 2. if auto-update is turned on (default off), we pull every 20 ms 3. updates are of the form: line-id : raw-html-for-that-line [ this also explains how you got color in the dom output] there's some keymap defined -- is this just common names for weird characters? (I'm not sure why it exists since I can't find the corresponding a-z A-'Z 0-9)
Yes it is done via http get. Autoupdate is only useful if multiple user are accessing the editor at the same time, so Bob will see if Alice is moving the cursor. Yes, line ids with raw html is the way I send the changes. Internally I store keys as keywords like :a 😛 :c :space :enter etc. Conversion from "a" to :a is done automatically, while special symbols are done manually. I have a general internal representation of a view. In the tty.clj it is translated to terminal codes, in the JFrame it is translated into Graphics commands and for web.clj it is translated into html. I also have a "recorder view". Which just translates it into a series of changes which can be replayed.
Each step from input until a generic view is created is described in this document: https://github.com/mogenslund/liquid/wiki/Data-Transformations So creating a new view is done by translating the last data structure to specific view commands.
That doc is really useful. Especially the secion on sliders + synta x highlighting. I have the following confusion: Based on section "Sliders"< I get the impression that each buffer = 1 slider. However, there is a latter section "split into lines", which seems to imply one slider / line when highlighting? What is going on here?
The syntax highlight is sort of injected into the slider (a copy). Then the slider is transformed into a list of lines. Here it is not a slider anymore. It is an intermediate step between having a slider with syntax highlight and having a list of lines for "printing". I have not looked into it yet, but a am playing with the thought of using the slider as an even more general structure, so I do not have to make these abusive in between steps.
I know very little about editor internals. I think emacs uses something similar to slider. However, it seems very weird to have to constantly convert before/after <-> 2d matrix of chars For example, implementing 'i" seems to be like: look at :before, figure out current column from that, then look at :before again, to, based on \r\n, get previous line, then shift a bunch of chars over to :after whereas in a 2d view, it's (update-in [.... ::y] dec) 🙂
When I started creating the editor, my main thought was to create a very robust editor without a view, considering all characters equal, including newlines. The slider is incredibly fast a when inserting text at point and moving the cursor. And it is very vel covered with tests. So my second steps was to implement as much as possible of editor operations in terms of the slider. So everything is pretty until something needs to be rendered (there is line wrap and possible multiple views of the same buffer.) Since I have learned a lot about text editors, since I began, I do consider rewriting some parts, espacially the views.
I have a performance test for the slider which does around 200,000 editor operations within ½ second. I think it is the key to few codelines, that my basis is very robust and very fast.
But maybe the update-in is fast also. I was actually surprised about the good performance if vector for another project. If I was to change I think I would have to define an interface and compare performance directly. Right now I also only split the visual part of the slider into lines and only apply syntax highlight to the visual part as well, to avoid a lot of processing on large files.
A big difference between you and me -- is that you actually wrote an editor, whereas my knowledge is only from reading stuff. 🙂
But I do like improvement suggestions, anyway 🙂 And sometimes I actually do change code based on feedback 🙂 One of the most demanding actions when doing a text editor is actually moving the cursor up and down when a line is wrapped multiple times, so that is usually the case I consider, when I consider changing implementation (And I really hate that this action requires knowledge about the view)
I wonder if you can get the best of both words via: (this doesn't solve line wrapping issue)
{::before-lines ;; vector of lines
::cur-line-before-chars ;; vector of chars
::cur-line-after-chars ;; vector of chars
;;after-lines ;; vector of lines
}
I'm really in this weird situation where the only two languages I currently use are elisp + clojure; soon to be possibly on clojure. As such, I'm fasinated by structure/ast editors, and saying fuck it to the whole notion of strings/chars.i
But this will probably require splitting a vector when moving up and down. I think, if the implementation is done with a vector of vectors then the position should be implemented as just a row number and a column number, so must non inserting/deleting operations would just be updating those two numbers. But I still think it is hard to compete with just basic operations on the head of two lists.
1. Yes, this would still require splitting vectors on up/down. 2. It mainly avoids the "re-split string into lines" on "vertical operations" switching topics a bit: are there any features from https://github.com/oakes/Nightcode or https://github.com/arthuredelstein/clooj you are looking to borrow ?