Shell – How to Automatically Wrap Long Command Lines

ptyshellterminalterminal-emulatortty

There are several points where I/O is passed through, some of which (to my knowledge) are the shell, pty, tty, termios, terminal emulator application. In most terminal emulators, long command lines (ones that exceed current $COLUMNS) are wrapped to a new line before the user submits the command by pressing Enter. Also, the line is wrapped backward to the line above when the appropriate number of characters are removed from the command line as one would expect.

My question is: where is this magic usually handled? Is it a termios setting, or part of the shell, or is the terminal emulator application responsible for this?

For more context, I'm using the Terminator terminal emulator application on Ubuntu – where the linewrapping works perfectly fine (so there should be no issues with my $PS1 prompt). But I'm working on my own terminal emulator application that works with a go pty spawner (github.com/kr/pty) and I'm having issues where long lines aren't wrapped to a new line, but the beginning of the same line.

Best Answer

In most terminal emulators, long command lines […] are wrapped to a new line before the user submits the command by pressing Enter.

This is not a function of the terminal emulator.

It is a function of your shell. Your shell is not a full-screen application, but it is doing cursor addressing.

When you are editing a command line in a shell, the line editing library in the shell is in full charge of how the line being edited is displayed. In the Bourne Again shell this is the GNU readline library. The Almquist shell uses libedit. Other shells, such as the Z Shell, have their own libraries.

These libraries are layered on top of the termcap/terminfo libraries, which record the terminal capabilities of your terminal emulator. Those capabilities include amongst other things the control sequences for positioning the cursor in relative or absolute terms, the sequences for clearing to the end of the line and the end of the screen, and whether or not your terminal has automatic margins.

With this and information about the width of the terminal determined from the TIOCGWINSZ ioctl (falling back to the COLUMNS variable for some libraries, or the termcap/terminfo database for others) in hand, the line editing library in the shell tracks the command line length and how many terminal lines it is displayed across. It intelligently moves the cursor around to repaint the input line as it is edited. Sometimes it might rely upon automatic margins, if your terminal has them. Sometimes it may explicitly reposition the cursor using control sequences.

The fact that it is doing this is what causes effects such as those discussed at https://superuser.com/questions/695338/ . One can mess up its idea of where the cursor is, and what cursor motions it needs to emit to write to a particular place on the screen, with incorrectly delimited control sequences in a prompt string.

Your terminal emulator does not deal in the notions of command lines, or line editing. It is not part of those layers. It sees a simple stream of characters and control sequences, which it must render. It is your terminal emulator's responsibility to implement the control sequences that are advertised for it in its termcap/terminfo entry. GNU readline, libedit, ZLE, vim, screen, and others will use what they find advertised. If you state in termcap/terminfo that your terminal has automatic margins, for example, then the emulator must do a line wrap when a character is printed at the right margin. If you state that your terminal can move the cursor up and down, then it must indeed do that when it receives the appropriate control sequences.

By the way: If GNU readline finds that it cannot move the cursor up, because the termcap/terminfo entry does not state a way to do so, one doesn't actually see line wrap at all. readline falls back to a mode where it sideways scrolls the input line, all on one line.

Related Question