Fork me on GitHub
#unrepl
<
2018-03-09
>
kotarak04:03:21

Hmm.... the repl buffer should probably not do read tracking. However, then some special treatment of prompts is necessary.

kotarak04:03:14

Eg. user=> (read) :foo (read)<enter> would result in: user=> (read) :foo (read) :foo user=> <cursor here in stdin mode> So there is an empty prompt. However it must be ignored. So it is a bit difficult because the buffer is one channel. Not two like a terminal. Maybe it just works. Maybe it need some special magic. I have to think about this.

kotarak08:03:10

Yup. (read) :foo (read) works. :-)

kotarak08:03:01

-.- encountered the first problem where vim and clojure disagree on character count....

cgrand08:03:36

endline or non-ascii?

kotarak09:03:45

Non-ascii. Vim has strchars and strlen. The latter gives bytes the former characters. I thought I needed that, but the failed now. Strlen however was correct. -.- Why can't we have cookies?

kotarak09:03:09

Oh. How are endlines counted in :len? That is also a point.

kotarak09:03:50

(Not in this case. Then the discrepancy would have been much higher.)

cgrand09:03:24

endlines are normalized to a single \n

kotarak09:03:14

Maybe it's not a problem. In this case it wasn't. I need to check this. How is the wire encoding chosen?

cgrand09:03:03

You can have any encoding you want as long as it’s UTF8

kotarak12:03:30

Nope. "Cp1252" 😛

kotarak12:03:35

Maybe there should be a note that you might need -Dfile.encoding=UTF-8 on Windows.

cgrand12:03:16

ouch just checked the code: I really thought that clojure.core.server was forcing UTF-8. No. Platform default. Argh!

kotarak12:03:09

I can live with setting the option. Now that I know it.

cgrand12:03:56

how vim works with encodings?

kotarak12:03:07

I set the option. For me: utf-8. It was the clojure side.

kotarak13:03:45

Vim actually also has file and term encoding settings. For me these are all hardwired to utf-8.

kotarak13:03:05

It has a iconv(). So I could also convert things on the vim side. But I prefer the model T approach.

cgrand11:03:45

Previously people (at least @volrath and @kotarak) had requested a way to set the eval-id because having to track :read messages to find the matching id was cumbersome.

cgrand11:03:41

Now since there’s always a prompt before an eval (previously (+ 1 2)(+ 3 4)\n would have triggered two :evals but one :prompt, the prompt is respobsible for allocating the eval-id.

cgrand11:03:36

synchornizing is thus easier: you know that there’s nothing in flight when prompt offset matches the sent-chars count of the client (better add a tolerance for whitespaces)

cgrand11:03:21

so if the repl has caught up with the input then the prompt bears the next eval-id

cgrand11:03:32

Does it sound easier?

volrath11:03:50

correct me if I'm wrong, but now if we want to produce the same output than a regular repl, the clients would have to know that some prompt messages should be ignored. i.e.

$> clj
Clojure 1.9.0
user=> (+ 1 2)(+ 3 4)
3
7
user=> 
but with these extra prompt messages, the client would print a prompt between the first result 3 and the second 7
$> clj
Clojure 1.9.0
user=> (+ 1 2)(+ 3 4)
3
user=>
7
user=>

volrath11:03:26

like that.. (i didn't actually test it)

cgrand11:03:32

Yes, it behaves like

$ clj
Clojure 1.9.0
user=> (clojure.main/repl :need-prompt (constantly true))
user=> (+ 1 2)(+ 3 4)
3
user=> 7
user=>

cgrand11:03:48

(badly multiplexed outputs notwithstanding)

cgrand11:03:42

[:prompt {:file nil, :line 1, :column 1, :offset 0, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 1]
(+ 1 2)(+ 3 4)
[:read {:file “unrepl-reader-684”, :from [1 1], :to [1 8], :offset 0, :len 7} 1]
[:started-eval {:actions {:interrupt (unrepl.repl$NPYB29j9QDmbJ9ILAyMl3uHuLOs/interrupt! :session683 1), :background (unrepl.repl$NPYB29j9QDmbJ9ILAyMl3uHuLOs/background! :session683 1)}} 1]
[:eval 3 1]
[:prompt {:file nil, :line 1, :column 8, :offset 7, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 2]
[:read {:file “unrepl-reader-684", :from [1 8], :to [1 15], :offset 7, :len 7} 2]
[:started-eval {:actions {:interrupt (unrepl.repl$NPYB29j9QDmbJ9ILAyMl3uHuLOs/interrupt! :session683 2), :background (unrepl.repl$NPYB29j9QDmbJ9ILAyMl3uHuLOs/background! :session683 2)}} 2]
[:eval 7 2]
[:prompt {:file nil, :line 1, :column 15, :offset 14, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 3]

cgrand11:03:25

@volrath are you just making sure you understand it or is it against your repl aesthetics?

volrath11:03:06

would it be great to have it as an optional thing

cgrand11:03:24

you can dedupe prompts client side, I think the extra messages are useful.

kotarak12:03:08

I simply check if there is a prompt and whether the command is empty. If so the spurious prompt is removed. There is a race condition though.

kotarak12:03:42

Vim is async. I have to add a prompt because I don't know what else is coming. The second form then starts. And the spurious prompt is removed. However the event handling is async. So the user might already have typed something. The chance that this happens is pretty low. Or so I should hope.

cgrand11:03:54

maybe a flag to mark the “good” prompts

volrath11:03:05

that'd also work

volrath11:03:29

an easier way to filter in/out those prompts

volrath11:03:38

brb, gonna grab lunch

cgrand11:03:44

legacy behaviour is “you get a prompt when at the start of a line” so

(+ 1
  2) (+ 3 4)
triggers only one final prompt

cgrand11:03:54

FWIW my own repl aesthetics: I see the repl split in two frames: upper frame is the log, divider holds the prompt info, bottom frame is the input buffer. Having actual input split in chunks (in the log) according to eval is ok for me.

kotarak12:03:33

That's even more funky in vim. I rather keep one window with one buffer. Complicated enough.

cgrand12:03:20

Can you make part of a buffer read-only?

kotarak12:03:22

Vim is very ... hmmm ... inept regarding buffer and window handling.

cgrand11:03:08

But ok I’m thinking about how to accomodate yours.

cgrand11:03:03

ah it was simpler than I thought

cgrand11:03:00

A “traditional” prompt is a prompt whose :column is 1. Look Ma, no flag!

volrath12:03:03

cool, that works 🙂

kotarak16:03:31

This doesn't help. Since I now have a eval operator, the user might choose to send any span of text for evaluation. In particular multiple forms separated by empty lines. So I will get multiple :column one prompts. So I still need to track the read count. But that's ok. I think that works.

cgrand16:03:46

Eval selection is a kind of framed evaluation and support is not there yet.

kotarak17:03:29

As I said: read tracking works! If you find that too funky on the unrepl side, there is no need to have it. Clients can do it, if they need it.

kotarak17:03:23

I just wanted to point out, that the :column one approach might be insufficient. Depending on your inputs.

cgrand11:03:25

Now:

[:prompt {:file nil, :line 1, :column 1, :offset 0, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 1]
(+ 1 2)(+ 3 4)
[:read {:file "unrepl-reader-683", :from [1 1], :to [1 8], :offset 0, :len 7} 1]
[:started-eval {:actions {:interrupt (unrepl.repl$jr7rtNzCvE2Cb_3TntSHIMlqPCY/interrupt! :session682 1), :background (unrepl.repl$jr7rtNzCvE2Cb_3TntSHIMlqPCY/background! :session682 1)}} 1]
[:eval 3 1]
[:prompt {:file nil, :line 1, :column 8, :offset 7, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 2]
[:read {:file "unrepl-reader-683", :from [1 8], :to [1 15], :offset 7, :len 7} 2]
[:started-eval {:actions {:interrupt (unrepl.repl$jr7rtNzCvE2Cb_3TntSHIMlqPCY/interrupt! :session682 2), :background (unrepl.repl$jr7rtNzCvE2Cb_3TntSHIMlqPCY/background! :session682 2)}} 2]
[:eval 7 2]
[:prompt {:file nil, :line 1, :column 15, :offset 14, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 3]
; THE TWO LINES BELOW ARE WHAT CHANGED
[:read {:file "unrepl-reader-683", :from [1 15], :to [2 1], :offset 14, :len 1} 3]
[:prompt {:file nil, :line 2, :column 1, :offset 15, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 4]

cgrand11:03:10

Now:

[:prompt {:file nil, :line 1, :column 1, :offset 0, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 1]
(+ 1 2)(+ 3 4)
[:read {:file "unrepl-reader-683", :from [1 1], :to [1 8], :offset 0, :len 7} 1]
[:started-eval {:actions {:interrupt (unrepl.repl$L5y82Z_WyPDOudROXJCUgcOs6ZY/interrupt! :session682 1), :background (unrepl.repl$L5y82Z_WyPDOudROXJCUgcOs6ZY/background! :session682 1)}} 1]
[:eval 3 1]
[:prompt {:file nil, :line 1, :column 8, :offset 7, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 2]
[:read {:file "unrepl-reader-683", :from [1 8], :to [1 15], :offset 7, :len 7} 2]
[:started-eval {:actions {:interrupt (unrepl.repl$L5y82Z_WyPDOudROXJCUgcOs6ZY/interrupt! :session682 2), :background (unrepl.repl$L5y82Z_WyPDOudROXJCUgcOs6ZY/background! :session682 2)}} 2]
[:eval 7 2]
[:prompt {:file nil, :line 1, :column 15, :offset 14, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 3]
; NO MORE :READ HERE
[:prompt {:file nil, :line 2, :column 1, :offset 15, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 4]

cgrand12:03:54

With more prompts, :read messages are almost obsolete (its data could be derived by comparing two consecutive prompts). The only thing that it has for it is that you get the data earlier (you don’t have to wait until end of eval)

kotarak13:03:21

Öh. Evaling a form with four lines, I get now 4 prompts?

kotarak13:03:57

Why do I see it in the log?

cgrand13:03:38

1 form spanning 4 lines right?

cgrand13:03:09

(+
 1
 2
 3)
[:read {:file “unrepl-reader-683”, :from [2 1], :to [5 4], :offset 15, :len 12} 4]
[:started-eval {:actions {:interrupt (unrepl.repl$L5y82Z_WyPDOudROXJCUgcOs6ZY/interrupt! :session682 4), :background (unrepl.repl$L5y82Z_WyPDOudROXJCUgcOs6ZY/background! :session682 4)}} 4]
[:eval 6 4]
[:prompt {:file nil, :line 5, :column 4, :offset 27, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 5]
[:prompt {:file nil, :line 6, :column 1, :offset 28, clojure.core/*ns* #unrepl/ns user, clojure.core/*warn-on-reflection* false} 6]
`

kotarak13:03:12

Ok. 3 lines there was one empty line.

cgrand13:03:45

I get two prompts: one after the form, one after the newline (newlines outside of forms)

kotarak13:03:55

:-( The newlines without read break my read tracking....

kotarak13:03:25

So now I have to check offset in prompt?

cgrand13:03:11

You are too bleeding edge.

cgrand13:03:43

Yes, :prompt

kotarak13:03:57

I noticed, I got cut.

cgrand13:03:29

Okay. That’s written in stone 🙂 I almost specified a regex-like structure (like yesterday here) but it was too strong of a commitment for little gain. :prompts are the backbone of the output. There’s one at each start of the Loop. The eval-id should almost be renamed to iteration-id