dundalek / closh

Bash-like shell based on Clojure
Eclipse Public License 1.0
1.62k stars 66 forks source link

Support multiline input #17

Open dundalek opened 6 years ago

dundalek commented 6 years ago

When parentheses are unbalanced and enter is pressed go to multiline mode. Lumo does this by checking isReadable.

Would be also cool to integrate parinfer.

mnewt commented 6 years ago

Expect to see a PR from me adding multiline using parinfer. Parinfer elegantly solves multiline and paren balancing problems in an intuitive way. Hopefully it will solve #22 as well, we'll see.

I have it mostly working but there are a couple annoying sticking points because of node's I/O quirks so I'll send it as soon as I iron those out. Also, multiline breaks history in some situations so I'll have to figure that out.

dundalek commented 6 years ago

Very cool! My instinct for the history is that one starts with up/down keys to cycle history. Then once you accept the line or maybe move with right arrow then up/down keys move the cursor within lines.

mnewt commented 6 years ago

@dundalek As you and I discussed, node/readline is very OO and mutates state all over the place. It's really hard to use with a state map and while implementing the multi-line features I'm seeing cases where a single key press changes the terminal 2 or 3 times, maybe more.

Also, readline is really not set up for multi-line input and I'm looking at some pretty weird and fragile monkey patches to get it to work.

I know it sounds drastic but I think it is going to be easier in the long run to just write our own readline interface (still using as much of node/readline as possible and probably using some functions from terminal-kit as well). This interface would be the single place where all rendering takes place.

So to make a change to the terminal, we would simply update the state map and call render-line on it.

Thoughts?

dundalek commented 6 years ago

I agree, replacing node/readline is inevitable. Makes sense to reuse where possible and explore terminal-kit. Also when rebuilding from ground up it also makes sense to think about how we would let users customize keybindings. I am not too familiar with Emacs, but I think its Buffer implementation might be a good source of inspiration (I have it on my list to explore it more).

mnewt commented 6 years ago

Great, I’m glad you agree. I’ll start working on it. I see key bindings as being its own subsection of the readline module, and I think something like the reverse of the key-value function will work.

I’m fairly familiar with emacs as a user but not really deeper than that and I don’t know anything about the buffer implementation. But I’ll check it out. I think there’s lots to learn from it. Its shell (it’s really a terminal) is one of the more innovative ones around, with things like great history management, highlighting, copy/paste, undo, and autocomplete with inline menus like you expect in an editor. I want to copy all of those. Also, if we are careful we can put in some emacs like extensibility points and hooks so people can easily extend e.g. the completion system from .closhrc.

In fact, when appropriate I’m thinking we might want to directly copy emacs-lisp names and semantics since they know what they’re doing and also many people will be familiar with them already.

But all that said I’ll try not to go overboard in the first iteration since I think we want to get basic multi line done so we can wrap up phase 2.

On Dec 19, 2017, at 4:10 PM, Jakub Dundalek notifications@github.com wrote:

I agree, replacing node/readline is inevitable. Makes sense to reuse where possible and explore terminal-kit. Also when rebuilding from ground up it also makes sense to think about how we would let users customize keybindings. I am not too familiar with Emacs, but I think its Buffer implementation might be a good source of inspiration (I have it on my list to explore it more).

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

johanatan commented 6 years ago

Any progress on this?

dundalek commented 6 years ago

I plan to port closh to run on JVM and then we can use rebel-readline which would give us multiline support and other cool features. This may take some time though. But I expect by the end of this year would be a reasonable timeframe.

mnewt commented 6 years ago

Great! I have also worked some on creating a standalone library that would handle terminal presentation and interaction in a reagent-like way. I actually have the basics complete but have not had time to add all the features closh would need. Don’t hold your breath on it but I hope to have something to show eventually. Haven’t had much time to work on it and I don’t really anticipate having a lot of time for the next few months.

On May 16, 2018, at 1:25 PM, Jakub Dundalek notifications@github.com wrote:

I plan to port closh to run on JVM and then we can use rebel-readline https://github.com/bhauman/rebel-readline which would give us multiline support and other cool features. This may take some time though. But I expect by the end of this year would be a reasonable timeframe.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/dundalek/closh/issues/17#issuecomment-389653849, or mute the thread https://github.com/notifications/unsubscribe-auth/ABaKihgahkOrKg6ixyNXwbDwW7TSEW6Pks5tzIs_gaJpZM4QRVtR.

johanatan commented 6 years ago

@mnewt have you seen: https://github.com/vadimdemedes/ink ?

perhaps wrapping this for reagent would be trivial?

mnewt commented 6 years ago

@johanatan I've seen it, thanks. It's a really cool project. But in my opinion it's not quite the right direction because it's pulling a bunch of libraries in that have no discernible value on the terminal (most of reagent and react really assume you have html and a browser DOM). More immediately though, my proof of concept code is more geared toward a non-full screen app and has cursor support (https://github.com/vadimdemedes/ink/issues/47). I hope I can carve some time out to flesh out the concept...

johanatan commented 6 years ago

Here's a workaround that takes multiline input and produces single-line per sexp output:

#!/usr/bin/env bash
"exec" "lumo" "$0" "$*"

(import '[goog.string format])
(require '[cljs.tools.reader :as r]
         '[lumo.io :refer [slurp]])

(assert (= 4 (count js/process.argv)))

(def filename (last js/process.argv))
(def file
 (remove
  #(or (clojure.string/starts-with? %1 "\"exec\"")
       (clojure.string/starts-with? %1 "#!"))
  (map
   (comp #(clojure.string/replace %1 #";.*$" "") clojure.string/trim)
   (-> filename slurp clojure.string/split-lines))))

(doseq [l (r/read-string (format "( %s )" (clojure.string/join " " file)))]
  (prn l))

For mixing bash and s-exprs, one needs to just join together any string of lines not beginning in ( with the next subsequent s-expr. [I intend to stick to s-exprs so no need for this].

johanatan commented 6 years ago

Also, I'm working with files so no need to read from stdin. Should be easy to modify to read from stdin rather than from file for interactive shell.

dundalek commented 5 years ago

Multi-line input works in the JVM version (thanks to rebel-readline). Adding lumo label to clarify that this only a limitation of the lumo version.