[solved] Any multiline zsh compatible shells? [zsh config]

Some time ago I found xonsh which is a python-based shell. It had really good multiline support, and I am searching for a shell with sameish multiline support as xonsh. Fish shell also has good multiline support, it is around the same level, but it is not posix compatible. I want a shell that has that kind of level of multiline, but zsh (bash is also fine) compatible.

Does anyone know of one?

edit: based on the replies, I get this is unclear. My problem with zsh is that if i press enter and it starts a new line, I can’t get back to the prevous line, because a new prompt is started. In fish this is possible, all lines are one prompt. But, fish is not posix compatible. So, I guess I want a posix-compatible shell with fish-like lines (multiple line) editing. I wanted zsh support to keep using my custom oh-my-zsh prompt, but remaking it for a new shell is not a big problem. Sorry for being unclear.

edit 2: solution is here! Thanks to @andy I started thinking and made the following: When on the first line, enter accepts and alt-enter inserts a newline. When not on the first line, enter inserts a newline and alt-enter accepts. Here is the code to put in your .zshrc:


<span style="color:#323232;"># MULTILINE!!!
</span><span style="color:#323232;">bindkey '^[e' push-line-or-edit
</span><span style="color:#323232;">
</span><span style="color:#323232;"># enter accepts when only one line found, else creates newline
</span><span style="color:#323232;">function _zle_ml_enter {
</span><span style="color:#323232;">    if ! [[ $BUFFER == *$'n'* ]]; then
</span><span style="color:#323232;">        zle accept-line
</span><span style="color:#323232;">    else
</span><span style="color:#323232;">        zle self-insert-unmeta
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">}
</span><span style="color:#323232;">zle -N _zle_ml_enter
</span><span style="color:#323232;">bindkey '^M' _zle_ml_enter
</span><span style="color:#323232;">
</span><span style="color:#323232;"># alt-enter accepts when more than one line found, else creates newline
</span><span style="color:#323232;">function _zle_ml_meta_enter {
</span><span style="color:#323232;">    if [[ $BUFFER == *$'n'* ]]; then
</span><span style="color:#323232;">        zle accept-line
</span><span style="color:#323232;">    else
</span><span style="color:#323232;">        zle self-insert-unmeta
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">}
</span><span style="color:#323232;">zle -N _zle_ml_meta_enter
</span><span style="color:#323232;">bindkey '^[^M' _zle_ml_meta_enter
</span><span style="color:#323232;">
</span>

edit:
changed if [[ “$BUFFERLINES” -le 1 ]]; then to if ! [[ $BUFFER == *$‘n’* ]]; then and if [[ “$BUFFERLINES” -gt 1 ]]; then to if [[ $BUFFER == *$‘n’* ]]; then for improving detection. Also added alt-e shortcut because I had that configured myself but forgot to add here.

nlm,
@nlm@beehaw.org avatar
vosjedev,

Erm… fish-like multiline editing? This does everything but that… but still thanks, as the syntax hylighting is something I wanted.

Andy,
@Andy@programming.dev avatar

There’s a great alternative syntax highlighting plugin, btw: github.com/…/fast-syntax-highlighting

palordrolap,

Your use of "multiline" is not obvious to me.

Most multiline features are available, such as having multiple commands between if, elsif, else and fi is definitely possible (ditto similar constructs), and it's possible to extend commands over an explicit carriage return if the end of line is quoted with a backslash.

If you're talking about having Python-like whitespace-only indentation to specify the level of code, I'm not sure there is a shell like that, but if this is what you mean and you confirm, someone who knows better might be able to point us in that direction.

If you're seeking multiline comments, unfortunately most shells don't have this, but there are definitely ways to fool the shell into skipping portions of text. You would be better off with an editor that can insert a # at the beginning of multiple lines however, since that will guarantee that the shell won't accidentally try to process something in that comment block.

vosjedev,

ah, I updated the post to be clearer :).

Andy,
@Andy@programming.dev avatar

What behavior are you looking for? Could Zsh with some configuration work?

vosjedev,

I updated my post to be clearer.

Andy, (edited )
@Andy@programming.dev avatar

OK, well FWIW in Zsh you can use a keybind to trigger ZLE functions that turn your already-entered lines back into in-progress lines.

The most straightforward built-in function for this is push-line-or-edit:

At the top-level (PS1) prompt, equivalent to push-line. At a secondary (PS2) prompt, move the entire current multiline construct into the editor buffer. The latter is equivalent to push-input followed by get-line.

So let’s say you want to trigger this with ctrl+e, all you need is:


<span style="color:#323232;">bindkey '^e' push-line-or-edit
</span>
vosjedev,

Thanks for your reply! This made me think: could I bind enter to newline and alt-enter to accept, which then made me think if it was possible to have enter to accept and alt-enter to newline on first line only, after that in reverse. That made me think of the script I added to the post.

But serious, without this I would have never thought of doing what I did.

Andy,
@Andy@programming.dev avatar

Ooh very cool! Thanks for sharing back your solution.

Andy,
@Andy@programming.dev avatar

I started using this, it makes a lot of sense and I like it, thanks!

I can imagine myself forgetting how to accept multiline input with alt+enter, so I added a help message to _zle_ml_enter in the multiline case after the second line. It assumes setopt interactivecomments is already set:

EDIT: note that lemmy mangles the less-than symbol


<span style="color:#323232;"># -- Run input if single line, otherwise insert newline --
</span><span style="color:#323232;"># Key: enter
</span><span style="color:#323232;"># Assumes: setopt interactivecomments
</span><span style="color:#323232;"># Credit: https://programming.dev/comment/2479198
</span><span style="color:#323232;">.zle_accept-except-multiline () {
</span><span style="color:#323232;">  if (( BUFFERLINES &lt;= 1 )) {
</span><span style="color:#323232;">    zle accept-line
</span><span style="color:#323232;">  } else {
</span><span style="color:#323232;">    zle self-insert-unmeta
</span><span style="color:#323232;">    if (( BUFFERLINES == 2 )) {
</span><span style="color:#323232;">      LBUFFER+="# Use alt+enter to submit this multiline input"
</span><span style="color:#323232;">      zle self-insert-unmeta
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">  }
</span><span style="color:#323232;">}
</span><span style="color:#323232;">zle -N .zle_accept-except-multiline
</span><span style="color:#323232;">bindkey '^M' .zle_accept-except-multiline  # Enter
</span>
vosjedev, (edited )

So I changed some other things to improve detection. I will update the post. But nice idea adding a hint! I personaly did that in a hint command also specifying other shortcuts and aliases about my zsh config.

Andy, (edited )
@Andy@programming.dev avatar

Thanks again for posting your improvements! I will have them!

The idea here, checking for newline characters rather than counting lines, is to prevent it treating one line that is so long it wraps to the next as counting as a multiline input, right? So now I’m looking like

EDIT: lemmy is at least mangling ampersands here. Hard to believe it doesn’t have proper code blocks yet…


<span style="color:#323232;"># -- Run input if single line, otherwise insert newline --
</span><span style="color:#323232;"># Key: enter
</span><span style="color:#323232;"># Assumes: setopt interactivecomments
</span><span style="color:#323232;"># Credit: https://programming.dev/comment/2479198
</span><span style="color:#323232;">.zle_accept-except-multiline () {
</span><span style="color:#323232;">  if [[ $BUFFER != *$'n'* ]] {
</span><span style="color:#323232;">    zle accept-line
</span><span style="color:#323232;">    return
</span><span style="color:#323232;">  } else {
</span><span style="color:#323232;">    zle self-insert-unmeta
</span><span style="color:#323232;">    if [[ $BUFFER == *$'n'*$'n'* ]] {
</span><span style="color:#323232;">      local hint="# Use alt+enter to submit this multiline input"
</span><span style="color:#323232;">      if [[ $BUFFER != *${hint}* ]] {
</span><span style="color:#323232;">        LBUFFER+=$hint
</span><span style="color:#323232;">        zle self-insert-unmeta
</span><span style="color:#323232;">      }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">  }
</span><span style="color:#323232;">}
</span><span style="color:#323232;">zle -N .zle_accept-except-multiline
</span><span style="color:#323232;">bindkey '^M' .zle_accept-except-multiline  # Enter
</span><span style="color:#323232;">
</span><span style="color:#323232;"># -- Run input if multiline, otherwise insert newline --
</span><span style="color:#323232;"># Key: alt+enter
</span><span style="color:#323232;"># Credit: https://programming.dev/comment/2479198
</span><span style="color:#323232;">.zle_accept_only_multiline () {
</span><span style="color:#323232;">  if [[ $BUFFER == *$'n'* ]] {
</span><span style="color:#323232;">    zle accept-line
</span><span style="color:#323232;">  } else {
</span><span style="color:#323232;">    zle self-insert-unmeta
</span><span style="color:#323232;">  }
</span><span style="color:#323232;">}
</span><span style="color:#323232;">zle -N .zle_accept_only_multiline
</span><span style="color:#323232;">bindkey '^[^M' .zle_accept_only_multiline  # Enter
</span>

For pushing the line/multiline, I combine it with my clear function (ctrl+l):


<span style="color:#323232;"># -- Refresh prompt, rerunning any hooks --
</span><span style="color:#323232;"># Credit: romkatv/z4h
</span><span style="color:#323232;">.zle_redraw-prompt () {
</span><span style="color:#323232;">  for 1 ( chpwd $chpwd_functions precmd $precmd_functions ) {
</span><span style="color:#323232;">    if (( $+functions[$1] ))  $1 &amp;>/dev/null
</span><span style="color:#323232;">  }
</span><span style="color:#323232;">  zle .reset-prompt
</span><span style="color:#323232;">  zle -R
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;"># -- Better Screen Clearing --
</span><span style="color:#323232;"># Clear line and redraw prompt, restore line at next prompt
</span><span style="color:#323232;"># Key: ctrl+l
</span><span style="color:#323232;"># Depends: .zle_redraw-prompt
</span><span style="color:#323232;">.zle_push-line-and-clear () { zle push-input; zle clear-screen; .zle_redraw-prompt }
</span><span style="color:#323232;">zle -N       .zle_push-line-and-clear
</span><span style="color:#323232;">bindkey '^L' .zle_push-line-and-clear  # ctrl+l
</span>
ultra,

Why not xonsh?

brokenix,

@ultra @vosjedev yc hn 5yr ago
problems people seem to be stating about xonsh were similar to a lot of the early bugs with Fish, but those things tend to go away once more people start using the shell and reporting issues.

ultra,

What early bugs with fish?

vosjedev,

because it is a python shell, not a posix shell. and, my python broke, and I don’t know how to solve that (but I am not lokking for help with that :)

SmokeInFog,
@SmokeInFog@midwest.social avatar

zsh is a shell. If you change shells, you won’t be using zsh anymore. Are you asking for shells that are compatible with scripts written for zsh? If so, I imagine you’d just want it to be bash compatible as most third party scripts are written with bash in mind over zsh or other shells

  • All
  • Subscribed
  • Moderated
  • Favorites
  • linux@lemmy.ml
  • rosin
  • ethstaker
  • tacticalgear
  • osvaldo12
  • mdbf
  • DreamBathrooms
  • thenastyranch
  • magazineikmin
  • Youngstown
  • everett
  • InstantRegret
  • slotface
  • ngwrru68w68
  • kavyap
  • JUstTest
  • GTA5RPClips
  • Leos
  • modclub
  • cisconetworking
  • Durango
  • khanakhh
  • cubers
  • normalnudes
  • anitta
  • tester
  • megavids
  • provamag3
  • lostlight
  • All magazines