Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ func (v *Terminal) SetMode(mode ansicode.TerminalMode) {
switch mode {
case ansicode.TerminalModeCursorKeys:
forward = true
case ansicode.TerminalModeInsert:
v.insertMode = true
case ansicode.TerminalModeLineWrap:
case ansicode.TerminalModeBlinkingCursor:
epoch := time.Now()
Expand Down Expand Up @@ -627,6 +629,8 @@ func (v *Terminal) UnsetMode(mode ansicode.TerminalMode) {
switch mode {
case ansicode.TerminalModeCursorKeys:
forward = true
case ansicode.TerminalModeInsert:
v.insertMode = false
case ansicode.TerminalModeLineWrap:
case ansicode.TerminalModeBlinkingCursor:
v.CursorBlinkEpoch = nil
Expand Down
8 changes: 8 additions & 0 deletions terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ type Terminal struct {
// to the next line if another character is printed.
wrap bool

// insertMode indicates whether printable input
// should shift row contents right.
insertMode bool

*ansicode.Decoder

// onResize is a hook called every time the terminal resizes.
Expand Down Expand Up @@ -147,6 +151,7 @@ func (v *Terminal) Reset() {
v.mut.Lock()
defer v.mut.Unlock()
v.reset()
v.insertMode = false
}

func (v *Terminal) UsedHeight() int {
Expand Down Expand Up @@ -247,6 +252,9 @@ func (v *Terminal) put(r rune) {
v.wrap = false
}
x, y, f := v.Cursor.X, v.Cursor.Y, v.Cursor.F
if v.insertMode {
v.insertCharacters(1)
}
v.paint(y, x, f, r)
if y > v.MaxY {
v.MaxY = y
Expand Down
66 changes: 64 additions & 2 deletions terminal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,70 @@ func TestResizeGrowingHeightThenShrinkWidth(t *testing.T) {
require.NoError(t, err)
}

func eachFrame(r io.Reader, callback func(frame int, segment []byte)) {
eachNthFrame(r, 1, callback)
func TestInsertModePreservesShiftedContentAcrossLines(t *testing.T) {
term := midterm.NewTerminal(24, 80)
term.Raw = true

// Seed the two-line prompt before the redraw:
//
// Please answer: first
// (▼ for other options)
_, err := io.WriteString(term, "\r\x1b[JPlease answer: first \r\n(▼ for other options)")
require.NoError(t, err)

// Replace "first" with "second",
// and insert "▲" and "▼" on the next line
// so the existing space shifts:
//
// Please answer: second
// (▲▼ for other options)
_, err = io.WriteString(term, "\x1b[A\x1b[6Dsecond\x1b[4h \x1b[4l\r\n\x1b[C▲\x1b[4h▼\x1b[4l")
require.NoError(t, err)

require.Equal(t, "Please answer: second", strings.TrimRight(string(term.Content[0]), " "))
require.Equal(t, "(▲▼ for other options)", strings.TrimRight(string(term.Content[1]), " "))
}

func TestInsertModeShiftsSingleLineContent(t *testing.T) {
term := midterm.NewTerminal(1, 8)
term.Raw = true

// Start with the cursor after the final "e":
//
// abcde^
_, err := io.WriteString(term, "abcde")
require.NoError(t, err)

// Return to column 3, enable insert mode, and insert a space:
//
// abc^de
// abc ^de
_, err = io.WriteString(term, "\r\x1b[3C\x1b[4h \x1b[4l")
require.NoError(t, err)

require.Equal(t, "abc de", strings.TrimRight(string(term.Content[0]), " "))
}

func TestUnsetInsertModeRestoresReplaceMode(t *testing.T) {
term := midterm.NewTerminal(1, 8)
term.Raw = true

// Start with the cursor after the final "e":
//
// abcde^
_, err := io.WriteString(term, "abcde")
require.NoError(t, err)

// Insert a space at column 3, disable insert mode, then overwrite the "d"
// with "Z" at the next cursor position:
//
// abc^de
// abc ^de
// abc Z^e
_, err = io.WriteString(term, "\r\x1b[3C\x1b[4h \x1b[4lZ")
require.NoError(t, err)

require.Equal(t, "abc Ze", strings.TrimRight(string(term.Content[0]), " "))
}

func eachNthFrame(r io.Reader, n int, callback func(frame int, segment []byte)) {
Expand Down