From 9977a3e9fcdc2d2feda2f8cd2990a89c21804e56 Mon Sep 17 00:00:00 2001
From: Junegunn Choi <junegunn.c@gmail.com>
Date: Wed, 11 Jan 2017 22:13:40 +0900
Subject: [PATCH] Make preview renderer suspend early on line wrap

---
 src/terminal.go    | 72 ++++++++++++++++++++++++++--------------------
 src/tui/light.go   | 16 +++++++----
 src/tui/ncurses.go |  9 ++++--
 src/tui/tcell.go   | 10 +++----
 src/tui/tui.go     | 12 ++++++--
 5 files changed, 73 insertions(+), 46 deletions(-)

diff --git a/src/terminal.go b/src/terminal.go
index ce63adf1..0353157e 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -1,8 +1,10 @@
 package fzf
 
 import (
+	"bufio"
 	"bytes"
 	"fmt"
+	"io"
 	"os"
 	"os/signal"
 	"regexp"
@@ -852,41 +854,49 @@ func (t *Terminal) printPreview() {
 		return
 	}
 	t.pwindow.Erase()
-	skip := t.previewer.offset
-	extractColor(t.previewer.text, nil, func(str string, ansi *ansiState) bool {
-		if skip > 0 {
-			newlines := numLinesMax(str, skip)
-			if skip <= newlines {
-				for i := 0; i < skip; i++ {
-					str = str[strings.Index(str, "\n")+1:]
-				}
-				skip = 0
-			} else {
-				skip -= newlines
-				return true
-			}
+
+	maxWidth := t.pwindow.Width()
+	if t.tui.DoesAutoWrap() {
+		maxWidth -= 1
+	}
+	reader := bufio.NewReader(strings.NewReader(t.previewer.text))
+	lineNo := -t.previewer.offset
+	for {
+		line, err := reader.ReadString('\n')
+		eof := err == io.EOF
+		if !eof {
+			line = line[:len(line)-1]
 		}
-		lines := strings.Split(str, "\n")
-		for i, line := range lines {
-			limit := t.pwindow.Width()
-			if t.tui.DoesAutoWrap() {
-				limit -= 1
-			}
-			if i == 0 {
-				limit -= t.pwindow.X()
-			}
-			trimmed := []rune(line)
-			if !t.preview.wrap {
-				trimmed, _ = t.trimRight(trimmed, limit)
+		lineNo++
+		if lineNo > t.pwindow.Height() {
+			break
+		} else if lineNo > 0 {
+			var fillRet tui.FillReturn
+			extractColor(line, nil, func(str string, ansi *ansiState) bool {
+				trimmed := []rune(str)
+				if !t.preview.wrap {
+					trimmed, _ = t.trimRight(trimmed, maxWidth-t.pwindow.X())
+				}
+				str, _ = t.processTabs(trimmed, 0)
+				if ansi != nil && ansi.colored() {
+					fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
+				} else {
+					fillRet = t.pwindow.Fill(str)
+				}
+				return fillRet == tui.FillContinue
+			})
+			switch fillRet {
+			case tui.FillNextLine:
+				continue
+			case tui.FillSuspend:
+				break
 			}
-			lines[i], _ = t.processTabs(trimmed, 0)
-			str = strings.Join(lines, "\n")
+			t.pwindow.Fill("\n")
 		}
-		if ansi != nil && ansi.colored() {
-			return t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
+		if eof {
+			break
 		}
-		return t.pwindow.Fill(str)
-	})
+	}
 	t.pwindow.FinishFill()
 	if t.previewer.lines > t.pwindow.Height() {
 		offset := fmt.Sprintf("%d/%d", t.previewer.offset+1, t.previewer.lines)
diff --git a/src/tui/light.go b/src/tui/light.go
index 4725ef56..248477c7 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -730,23 +730,29 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
 	return lines
 }
 
-func (w *LightWindow) fill(str string, onMove func()) bool {
+func (w *LightWindow) fill(str string, onMove func()) FillReturn {
 	allLines := strings.Split(str, "\n")
 	for i, line := range allLines {
 		lines := wrapLine(line, w.posx, w.width, w.tabstop)
 		for j, wl := range lines {
+			if w.posx >= w.Width()-1 && wl.displayWidth == 0 {
+				if w.posy < w.height-1 {
+					w.MoveAndClear(w.posy+1, 0)
+				}
+				return FillNextLine
+			}
 			w.stderr(wl.text)
 			w.posx += wl.displayWidth
 			if j < len(lines)-1 || i < len(allLines)-1 {
 				if w.posy+1 >= w.height {
-					return false
+					return FillSuspend
 				}
 				w.MoveAndClear(w.posy+1, 0)
 				onMove()
 			}
 		}
 	}
-	return true
+	return FillContinue
 }
 
 func (w *LightWindow) setBg() {
@@ -755,13 +761,13 @@ func (w *LightWindow) setBg() {
 	}
 }
 
-func (w *LightWindow) Fill(text string) bool {
+func (w *LightWindow) Fill(text string) FillReturn {
 	w.MoveAndClear(w.posy, w.posx)
 	w.setBg()
 	return w.fill(text, w.setBg)
 }
 
-func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) bool {
+func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillReturn {
 	w.MoveAndClear(w.posy, w.posx)
 	if bg == colDefault {
 		bg = w.bg
diff --git a/src/tui/ncurses.go b/src/tui/ncurses.go
index f6feefc6..3e636ba2 100644
--- a/src/tui/ncurses.go
+++ b/src/tui/ncurses.go
@@ -282,11 +282,14 @@ func (r *FullscreenRenderer) DoesAutoWrap() bool {
 	return true
 }
 
-func (w *CursesWindow) Fill(str string) bool {
-	return C.waddstr(w.impl, C.CString(str)) == C.OK
+func (w *CursesWindow) Fill(str string) FillReturn {
+	if C.waddstr(w.impl, C.CString(str)) == C.OK {
+		return FillContinue
+	}
+	return FillSuspend
 }
 
-func (w *CursesWindow) CFill(fg Color, bg Color, attr Attr, str string) bool {
+func (w *CursesWindow) CFill(fg Color, bg Color, attr Attr, str string) FillReturn {
 	index := ColorPair{fg, bg, -1}.index()
 	C.wcolor_set(w.impl, C.short(index), nil)
 	C.wattron(w.impl, C.int(attr))
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index b6f08192..3399d32b 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -477,7 +477,7 @@ func (w *TcellWindow) CPrint(pair ColorPair, attr Attr, text string) {
 	w.printString(text, pair, attr)
 }
 
-func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) bool {
+func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn {
 	lx := 0
 
 	var style tcell.Style
@@ -511,7 +511,7 @@ func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) bool {
 			var yPos = w.top + w.lastY
 
 			if yPos >= (w.top + w.height) {
-				return false
+				return FillSuspend
 			}
 
 			_screen.SetContent(xPos, yPos, r, nil, style)
@@ -520,14 +520,14 @@ func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) bool {
 	}
 	w.lastX += lx
 
-	return true
+	return FillContinue
 }
 
-func (w *TcellWindow) Fill(str string) bool {
+func (w *TcellWindow) Fill(str string) FillReturn {
 	return w.fillString(str, ColDefault, 0)
 }
 
-func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) bool {
+func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
 	return w.fillString(str, ColorPair{fg, bg, -1}, a)
 }
 
diff --git a/src/tui/tui.go b/src/tui/tui.go
index eb504f8c..33358e85 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -117,6 +117,14 @@ const (
 	colWhite
 )
 
+type FillReturn int
+
+const (
+	FillContinue FillReturn = iota
+	FillNextLine
+	FillSuspend
+)
+
 type ColorPair struct {
 	fg Color
 	bg Color
@@ -216,8 +224,8 @@ type Window interface {
 	MoveAndClear(y int, x int)
 	Print(text string)
 	CPrint(color ColorPair, attr Attr, text string)
-	Fill(text string) bool
-	CFill(fg Color, bg Color, attr Attr, text string) bool
+	Fill(text string) FillReturn
+	CFill(fg Color, bg Color, attr Attr, text string) FillReturn
 	Erase()
 }
 
-- 
GitLab