func parseAttr(attr termbox.Attribute, isFg bool) string {
	var buf bytes.Buffer
	buf.WriteString("\x1b[")
	if attr >= termbox.AttrReverse {
		buf.WriteString("7;")
		attr -= termbox.AttrReverse
	}
	if attr >= termbox.AttrUnderline {
		buf.WriteString("4;")
		attr -= termbox.AttrUnderline
	}
	if attr >= termbox.AttrBold {
		buf.WriteString("1;")
		attr -= termbox.AttrBold
	}
	if attr > termbox.ColorWhite {
		panic(fmt.Sprintf("invalid color code: %d", attr))
	}
	if attr == termbox.ColorDefault {
		if isFg {
			buf.WriteString("39")
		} else {
			buf.WriteString("49")
		}
	} else {
		color := int(attr) - 1
		if isFg {
			fmt.Fprintf(&buf, "38;5;%d", color)
		} else {
			fmt.Fprintf(&buf, "48;5;%d", color)
		}
	}
	buf.WriteString("m")
	return buf.String()
}
parseAttr parses an attribute of termbox
as an escape sequence.
parseAttr doesn't support output modes othar than color256 in termbox-go.
package fuzzyfinder
import (
	"bytes"
	"fmt"
	"sync"
	"time"
	"github.com/gdamore/tcell/v2"
	runewidth "github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)
type cell struct {
	ch     rune
	bg, fg termbox.Attribute
}
type simScreen tcell.SimulationScreen
type TerminalMock struct {
	simScreen
	sizeMu        sync.RWMutex
	width, height int
	eventsMu sync.Mutex
	events   []termbox.Event
	cellsMu sync.RWMutex
	cells   []*cell
	resultMu sync.RWMutex
	result   string
	sleepDuration time.Duration
	v2            bool
}
func (m *TerminalMock) SetSize(w, h int) {
	if m.v2 {
		m.simScreen.SetSize(w, h)
		return
	}
	m.sizeMu.Lock()
	defer m.sizeMu.Unlock()
	m.cellsMu.Lock()
	defer m.cellsMu.Unlock()
	m.width = w
	m.height = h
	m.cells = make([]*cell, w*h)
}
func (m *TerminalMock) SetEvents(events ...termbox.Event) {
	m.eventsMu.Lock()
	defer m.eventsMu.Unlock()
	m.events = events
}
func (m *TerminalMock) SetEventsV2(events ...tcell.Event) {
	for _, event := range events {
		switch event := event.(type) {
		case *tcell.EventKey:
			ek := event
			m.simScreen.InjectKey(ek.Key(), ek.Rune(), ek.Modifiers())
		case *tcell.EventResize:
			er := event
			w, h := er.Size()
			m.simScreen.SetSize(w, h)
		}
	}
}
func (m *TerminalMock) GetResult() string {
	if !m.v2 {
		m.resultMu.RLock()
		defer m.resultMu.RUnlock()
		return m.result
	}
	var s string
	setCursor := func() {
		cursorX, cursorY, _ := m.simScreen.GetCursor()
		mainc, _, _, _ := m.simScreen.GetContent(cursorX, cursorY)
		if mainc == ' ' {
			m.simScreen.SetContent(cursorX, cursorY, '\u2588', nil, tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorDefault))
		} else {
			m.simScreen.SetContent(cursorX, cursorY, mainc, nil, tcell.StyleDefault.Background(tcell.ColorWhite))
		}
		m.simScreen.Show()
	}
	setCursor()
	m.resultMu.Lock()
	cells, width, height := m.simScreen.GetContents()
	for h := 0; h < height; h++ {
		prevFg, prevBg := tcell.ColorDefault, tcell.ColorDefault
		for w := 0; w < width; w++ {
			cell := cells[h*width+w]
			fg, bg, attr := cell.Style.Decompose()
			var fgReset bool
			if fg != prevFg {
				s += "\x1b\x5b\x6d"  
				s += parseAttrV2(&fg, nil, attr)
				prevFg = fg
				prevBg = tcell.ColorDefault
				fgReset = true
			}
			if bg != prevBg {
				if !fgReset {
					s += "\x1b\x5b\x6d"  
					prevFg = tcell.ColorDefault
				}
				s += parseAttrV2(nil, &bg, attr)
				prevBg = bg
			}
			s += string(cell.Runes)
			rw := runewidth.RuneWidth(cell.Runes[0])
			if rw != 0 {
				w += rw - 1
			}
		}
		s += "\n"
	}
	s += "\x1b\x5b\x6d"  
	m.resultMu.Unlock()
	return s
}
func (m *TerminalMock) init() error {
	return nil
}
func (m *TerminalMock) size() (width int, height int) {
	m.sizeMu.RLock()
	defer m.sizeMu.RUnlock()
	return m.width, m.height
}
func (m *TerminalMock) clear(fg termbox.Attribute, bg termbox.Attribute) error {
	return nil
}
func (m *TerminalMock) setCell(x int, y int, ch rune, fg termbox.Attribute, bg termbox.Attribute) {
	m.sizeMu.RLock()
	defer m.sizeMu.RUnlock()
	m.cellsMu.Lock()
	defer m.cellsMu.Unlock()
	if x < 0 || x >= m.width {
		return
	}
	if y < 0 || y >= m.height {
		return
	}
	m.cells[y*m.width+x] = &cell{ch: ch, fg: fg, bg: bg}
}
func (m *TerminalMock) setCursor(x int, y int) {
	m.sizeMu.RLock()
	defer m.sizeMu.RUnlock()
	m.cellsMu.Lock()
	defer m.cellsMu.Unlock()
	if x < 0 || x >= m.width {
		return
	}
	if y < 0 || y >= m.height {
		return
	}
	i := y*m.width + x
	if m.cells[i] == nil {
		m.cells[y*m.width+x] = &cell{ch: '\u2588', fg: termbox.ColorWhite, bg: termbox.ColorDefault}
	} else {
		m.cells[y*m.width+x].bg = termbox.ColorWhite
	}
}
func (m *TerminalMock) pollEvent() termbox.Event {
	m.eventsMu.Lock()
	defer m.eventsMu.Unlock()
	if len(m.events) == 0 {
		panic("pollEvent called with empty events. have you set expected events by SetEvents?")
	}
	e := m.events[0]
	m.events = m.events[1:]
	time.Sleep(m.sleepDuration)
	return e
}
func (m *TerminalMock) flush() {
	m.cellsMu.RLock()
	var s string
	for j := 0; j < m.height; j++ {
		prevFg, prevBg := termbox.ColorDefault, termbox.ColorDefault
		for i := 0; i < m.width; i++ {
			c := m.cells[j*m.width+i]
			if c == nil {
				s += " "
				prevFg, prevBg = termbox.ColorDefault, termbox.ColorDefault
				continue
			} else {
				var fgReset bool
				if c.fg != prevFg {
					s += "\x1b\x5b\x6d"  
					s += parseAttr(c.fg, true)
					prevFg = c.fg
					prevBg = termbox.ColorDefault
					fgReset = true
				}
				if c.bg != prevBg {
					if !fgReset {
						s += "\x1b\x5b\x6d"  
						prevFg = termbox.ColorDefault
					}
					s += parseAttr(c.bg, false)
					prevBg = c.bg
				}
				s += string(c.ch)
				rw := runewidth.RuneWidth(c.ch)
				if rw != 0 {
					i += rw - 1
				}
			}
		}
		s += "\n"
	}
	s += "\x1b\x5b\x6d"  
	m.cellsMu.RUnlock()
	m.cellsMu.Lock()
	m.cells = make([]*cell, m.width*m.height)
	m.cellsMu.Unlock()
	m.resultMu.Lock()
	defer m.resultMu.Unlock()
	m.result = s
}
func (m *TerminalMock) close() {}
func UseMockedTerminal() *TerminalMock {
	f := newFinder()
	return f.UseMockedTerminal()
}
func UseMockedTerminalV2() *TerminalMock {
	f := newFinder()
	return f.UseMockedTerminalV2()
}
func (f *finder) UseMockedTerminal() *TerminalMock {
	m := &TerminalMock{}
	f.term = m
	return m
}
func (f *finder) UseMockedTerminalV2() *TerminalMock {
	screen := tcell.NewSimulationScreen("UTF-8")
	if err := screen.Init(); err != nil {
		panic(err)
	}
	m := &TerminalMock{
		simScreen: screen,
		v2:        true,
	}
	f.term = m
	return m
}