package terminal

import (
	"bufio"
	"io"
	"strconv"

	"github.com/mattn/go-runewidth"
)

type Terminal struct {
	w   *bufio.Writer
	msg string
}

func New(w io.Writer) *Terminal {
	return &Terminal{
		w: bufio.NewWriterSize(w, 32),
	}
}

func (t *Terminal) Refresh(prompt string, s []rune, pos int) {
	t.w.WriteString("\r\033[J")
	t.w.WriteString(prompt)
	t.w.WriteString(string(s))
	if t.msg != "" {
		t.w.WriteString("\n")
		t.w.WriteString(t.msg)
		t.w.WriteString("\033[A")
	}
	t.w.WriteString("\033[")
	t.w.WriteString(strconv.Itoa(runewidth.StringWidth(prompt) + runesWidth(s[:pos]) + 1))
	t.w.WriteString("G")
	t.w.Flush()
}

func runesWidth(s []rune) (width int) {
	for _, r := range s {
		width += runewidth.RuneWidth(r)
	}
	return width
}

func (t *Terminal) SetLastLine(msg string) {
	t.msg = msg
}

package terminal

import (
	"bytes"
	"io"
	"strconv"

	"github.com/mattn/go-runewidth"
)

type Terminal struct {
	w   io.Writer
	buf *bytes.Buffer
	msg string
}

func New(w io.Writer) *Terminal {
	return &Terminal{
		w:   w,
		buf: bytes.NewBuffer(make([]byte, 0, 32)),
	}
}

func (t *Terminal) Refresh(prompt string, s []rune, pos int) {
	t.buf.WriteString("\r\033[J")
	t.buf.WriteString(prompt)
	t.buf.WriteString(string(s))
	if t.msg != "" {
		t.buf.WriteString("\n")
		t.buf.WriteString(t.msg)
		t.buf.WriteString("\033[A")
	}
	t.buf.WriteString("\033[")
	t.buf.WriteString(strconv.Itoa(runewidth.StringWidth(prompt) + runesWidth(s[:pos]) + 1))
	t.buf.WriteString("G")
	t.buf.WriteTo(t.w)
}

func runesWidth(s []rune) (width int) {
	for _, r := range s {
		width += runewidth.RuneWidth(r)
	}
	return width
}

func (t *Terminal) SetLastLine(msg string) {
	t.msg = msg
}

--- 
+++ 
@@ -1,7 +1,7 @@
 package terminal
 
 import (
-	"bytes"
+	"bufio"
 	"io"
 	"strconv"
 
@@ -9,31 +9,29 @@
 )
 
 type Terminal struct {
-	w   io.Writer
-	buf *bytes.Buffer
+	w   *bufio.Writer
 	msg string
 }
 
 func New(w io.Writer) *Terminal {
 	return &Terminal{
-		w:   w,
-		buf: bytes.NewBuffer(make([]byte, 0, 32)),
+		w: bufio.NewWriterSize(w, 32),
 	}
 }
 
 func (t *Terminal) Refresh(prompt string, s []rune, pos int) {
-	t.buf.WriteString("\r\033[J")
-	t.buf.WriteString(prompt)
-	t.buf.WriteString(string(s))
+	t.w.WriteString("\r\033[J")
+	t.w.WriteString(prompt)
+	t.w.WriteString(string(s))
 	if t.msg != "" {
-		t.buf.WriteString("\n")
-		t.buf.WriteString(t.msg)
-		t.buf.WriteString("\033[A")
+		t.w.WriteString("\n")
+		t.w.WriteString(t.msg)
+		t.w.WriteString("\033[A")
 	}
-	t.buf.WriteString("\033[")
-	t.buf.WriteString(strconv.Itoa(runewidth.StringWidth(prompt) + runesWidth(s[:pos]) + 1))
-	t.buf.WriteString("G")
-	t.buf.WriteTo(t.w)
+	t.w.WriteString("\033[")
+	t.w.WriteString(strconv.Itoa(runewidth.StringWidth(prompt) + runesWidth(s[:pos]) + 1))
+	t.w.WriteString("G")
+	t.w.Flush()
 }
 
 func runesWidth(s []rune) (width int) {
Use bufio.Writer instead of io.Writer & bytes.Buffer

name        old time/op    new time/op    delta
Terminal-4    1.69µs ± 2%    1.64µs ± 1%  -2.69%  (p=0.000 n=9+10)
