stdlib icon indicating copy to clipboard operation
stdlib copied to clipboard

[RFC]: add support for multi-line editing to the REPL

Open kgryte opened this issue 1 year ago • 8 comments

Description

This RFC proposes adding support for multi-line editing to the REPL. Currently, we a user does

In [29]: function foo() {
    // ...
}<|>

where <|> represents the cursor, hitting the up arrow auto-completes previous commands, rather than allows a user to move the cursor up to edit existing content.

It would be nice to allow users to support multi-line editing, as otherwise they need to hit ENTER and start over.

Related Issues

No.

Questions

  • What are the pitfalls and considerations for implementing this? For one, could be tricky to implement due to needing to listen for certain keypress sequences and having to manipulate cursor position via ANSI escape sequences.
  • Should the default be to move the cursor up? Or is there a use case for hitting the up arrow in multi-line mode and auto-completing previous commands?

Other

No.

Checklist

  • [X] I have read and understood the Code of Conduct.
  • [X] Searched for existing issues and pull requests.
  • [X] The issue name begins with RFC:.

kgryte avatar Mar 26 '24 23:03 kgryte

Another option for this, which may be better, is to enter an "editor" mode. This would be similar to the built-in Node.js REPL which supports the .editor command.

kgryte avatar Mar 27 '24 03:03 kgryte

@kgryte I tried the .editor mode in node, but I can't seem to move up to the previous line using backspaces or the up arrow. But by editor mode are you proposing a vi editor like experience?

Snehil-Shah avatar Mar 27 '24 05:03 Snehil-Shah

@Snehil-Shah Oh! Interesting. You're right about .editor. You can do multi-line input without triggering execution, but you cannot return to previous lines.

Yes, I suppose I had in mind something more like nano. vi may be a bit much to replicate. 😅

kgryte avatar Mar 27 '24 05:03 kgryte

However, with nano, you clear the terminal and input text. It would be nice for multi-line editing to not leave the REPL view (similar to now). But I can see advantages of both approaches: nano vs if the current quasi-multi-line editing could support editing previous lines.

kgryte avatar Mar 27 '24 05:03 kgryte

@kgryte I think having both would be great, a nano type editing mode can allow for writing long scripts and programs with maybe even options to export it to a .js file or execute it independently to the rest of the REPL context (like a playground of sorts), while our current multiline (with ability to edit previous lines) can allow for writing normal multiline commands.

Snehil-Shah avatar Mar 27 '24 05:03 Snehil-Shah

Agreed.

kgryte avatar Mar 27 '24 05:03 kgryte

Following up on this issue, with the addition of auto-pairing closing symbols, the current approach for multi-line editing (relying on incomplete expressions) no longer works very well and makes for a frustrating UX. Recently, I ended up turning off autoClosePairs, entering function contents to allow "incomplete expression" multi-line editing to work, and re-enabling.

kgryte avatar Apr 22 '24 03:04 kgryte

@kgryte We need a keybinding to make multiline editing easier. Yes ALT+ENTER didn't work the last time we tried, but I assume different terminals might be interpreting it differently because of ENTER. We can try a combination like CTRL+O (like IPython) to enter multiline editing and it should work.

Snehil-Shah avatar Apr 22 '24 03:04 Snehil-Shah

@kgryte I spent some time studying python REPL's new multiline mode. Apparently, it was a big overhaul and I also think they stopped using their standard readline module, and re-wrote it for the REPL (not entirely sure).

  • Their way of entering multi-line mode is similar to ours where they try to compile the statements, if found incomplete, they add a newline. Manually entering a newline involves adding a backslash at the end of the statement and hitting enter from any cursor position. So,
... prin<|>t(\

press ENTER

... prin
... <|>t(\

In my opinion, it's not at all intuitive and also requires us additional parsing.

  • Like us, they handle all keypress as events. The up and down key events trigger certain commands as defined here. We can see for the up event, if at the beginning of the line, they cycle through previous commands, and if not, they call the setpos_from_xy method of their reader class with the new co-ordinates in the console window. setpos_from_xy takes these console window coordinates to update their pos integer which is the line position in the buffer which I presume is a string containing the current command, analogous to our _cmd variable.

  • For comparison, our approach is similar. We handle keypress events up and down. Once we encounter the up event, we update the current line in the _cmd array, update the line index denoting the line number in the input we are on, and update the readline interface properties like line and cursor position to be able to edit that line. We also have to render the lines below the current line with every keypress because apparently, a lot of the common methods of the node's readline interface including things like deleting a character or moving the cursor etc, uses this method called kRefreshLine which uses clearScreenDown to clear everything below the current prompt.

Snehil-Shah avatar Jun 11 '24 20:06 Snehil-Shah

@Snehil-Shah Thanks for taking a look! Your overview seems reasonable to me. And yes, they ended up reimplementing Python's readline. I actually work with the lead dev who spearheaded this effort. Seems like we don't have much, if anything, to take from their efforts.

kgryte avatar Jun 12 '24 04:06 kgryte