|
|
@ -32,20 +32,26 @@ var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]
|
|
|
|
var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
|
|
|
|
var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
|
|
|
|
|
|
|
|
|
|
|
|
func (r *LightRenderer) stderr(str string) {
|
|
|
|
func (r *LightRenderer) stderr(str string) {
|
|
|
|
r.stderrInternal(str, true)
|
|
|
|
r.stderrInternal(str, true, "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: Need better handling of non-displayable characters
|
|
|
|
const CR string = "\x1b[2m␍"
|
|
|
|
func (r *LightRenderer) stderrInternal(str string, allowNLCR bool) {
|
|
|
|
const LF string = "\x1b[2m␊"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (r *LightRenderer) stderrInternal(str string, allowNLCR bool, resetCode string) {
|
|
|
|
bytes := []byte(str)
|
|
|
|
bytes := []byte(str)
|
|
|
|
runes := []rune{}
|
|
|
|
runes := []rune{}
|
|
|
|
for len(bytes) > 0 {
|
|
|
|
for len(bytes) > 0 {
|
|
|
|
r, sz := utf8.DecodeRune(bytes)
|
|
|
|
r, sz := utf8.DecodeRune(bytes)
|
|
|
|
nlcr := r == '\n' || r == '\r'
|
|
|
|
nlcr := r == '\n' || r == '\r'
|
|
|
|
if r >= 32 || r == '\x1b' || nlcr {
|
|
|
|
if r >= 32 || r == '\x1b' || nlcr {
|
|
|
|
if r == utf8.RuneError || nlcr && !allowNLCR {
|
|
|
|
if nlcr && !allowNLCR {
|
|
|
|
runes = append(runes, ' ')
|
|
|
|
if r == '\r' {
|
|
|
|
} else {
|
|
|
|
runes = append(runes, []rune(CR+resetCode)...)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
runes = append(runes, []rune(LF+resetCode)...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if r != utf8.RuneError {
|
|
|
|
runes = append(runes, r)
|
|
|
|
runes = append(runes, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -54,8 +60,10 @@ func (r *LightRenderer) stderrInternal(str string, allowNLCR bool) {
|
|
|
|
r.queued.WriteString(string(runes))
|
|
|
|
r.queued.WriteString(string(runes))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (r *LightRenderer) csi(code string) {
|
|
|
|
func (r *LightRenderer) csi(code string) string {
|
|
|
|
r.stderr("\x1b[" + code)
|
|
|
|
fullcode := "\x1b[" + code
|
|
|
|
|
|
|
|
r.stderr(fullcode)
|
|
|
|
|
|
|
|
return fullcode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (r *LightRenderer) flush() {
|
|
|
|
func (r *LightRenderer) flush() {
|
|
|
@ -825,12 +833,12 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
|
|
|
|
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, (w.width-bcw)/hw)+repeat(' ', rem)+string(w.border.bottomRight))
|
|
|
|
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, (w.width-bcw)/hw)+repeat(' ', rem)+string(w.border.bottomRight))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) csi(code string) {
|
|
|
|
func (w *LightWindow) csi(code string) string {
|
|
|
|
w.renderer.csi(code)
|
|
|
|
return w.renderer.csi(code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) stderrInternal(str string, allowNLCR bool) {
|
|
|
|
func (w *LightWindow) stderrInternal(str string, allowNLCR bool, resetCode string) {
|
|
|
|
w.renderer.stderrInternal(str, allowNLCR)
|
|
|
|
w.renderer.stderrInternal(str, allowNLCR, resetCode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) Top() int {
|
|
|
|
func (w *LightWindow) Top() int {
|
|
|
@ -936,10 +944,10 @@ func colorCodes(fg Color, bg Color) []string {
|
|
|
|
return codes
|
|
|
|
return codes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) csiColor(fg Color, bg Color, attr Attr) bool {
|
|
|
|
func (w *LightWindow) csiColor(fg Color, bg Color, attr Attr) (bool, string) {
|
|
|
|
codes := append(attrCodes(attr), colorCodes(fg, bg)...)
|
|
|
|
codes := append(attrCodes(attr), colorCodes(fg, bg)...)
|
|
|
|
w.csi(";" + strings.Join(codes, ";") + "m")
|
|
|
|
code := w.csi(";" + strings.Join(codes, ";") + "m")
|
|
|
|
return len(codes) > 0
|
|
|
|
return len(codes) > 0, code
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) Print(text string) {
|
|
|
|
func (w *LightWindow) Print(text string) {
|
|
|
@ -951,16 +959,17 @@ func cleanse(str string) string {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) CPrint(pair ColorPair, text string) {
|
|
|
|
func (w *LightWindow) CPrint(pair ColorPair, text string) {
|
|
|
|
w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
|
|
|
|
_, code := w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
|
|
|
|
w.stderrInternal(cleanse(text), false)
|
|
|
|
w.stderrInternal(cleanse(text), false, code)
|
|
|
|
w.csi("m")
|
|
|
|
w.csi("m")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
|
|
|
|
func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
|
|
|
|
if w.csiColor(fg, bg, attr) {
|
|
|
|
hasColors, code := w.csiColor(fg, bg, attr)
|
|
|
|
|
|
|
|
if hasColors {
|
|
|
|
defer w.csi("m")
|
|
|
|
defer w.csi("m")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.stderrInternal(cleanse(text), false)
|
|
|
|
w.stderrInternal(cleanse(text), false, code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type wrappedLine struct {
|
|
|
|
type wrappedLine struct {
|
|
|
@ -980,6 +989,8 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
|
|
|
|
if len(rs) == 1 && rs[0] == '\t' {
|
|
|
|
if len(rs) == 1 && rs[0] == '\t' {
|
|
|
|
w = tabstop - (prefixLength+width)%tabstop
|
|
|
|
w = tabstop - (prefixLength+width)%tabstop
|
|
|
|
str = repeat(' ', w)
|
|
|
|
str = repeat(' ', w)
|
|
|
|
|
|
|
|
} else if rs[0] == '\r' {
|
|
|
|
|
|
|
|
w++
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
w = runewidth.StringWidth(str)
|
|
|
|
w = runewidth.StringWidth(str)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -998,12 +1009,12 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
|
|
|
|
return lines
|
|
|
|
return lines
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) fill(str string, onMove func()) FillReturn {
|
|
|
|
func (w *LightWindow) fill(str string, resetCode string) FillReturn {
|
|
|
|
allLines := strings.Split(str, "\n")
|
|
|
|
allLines := strings.Split(str, "\n")
|
|
|
|
for i, line := range allLines {
|
|
|
|
for i, line := range allLines {
|
|
|
|
lines := wrapLine(line, w.posx, w.width, w.tabstop)
|
|
|
|
lines := wrapLine(line, w.posx, w.width, w.tabstop)
|
|
|
|
for j, wl := range lines {
|
|
|
|
for j, wl := range lines {
|
|
|
|
w.stderrInternal(wl.text, false)
|
|
|
|
w.stderrInternal(wl.text, false, resetCode)
|
|
|
|
w.posx += wl.displayWidth
|
|
|
|
w.posx += wl.displayWidth
|
|
|
|
|
|
|
|
|
|
|
|
// Wrap line
|
|
|
|
// Wrap line
|
|
|
@ -1013,7 +1024,7 @@ func (w *LightWindow) fill(str string, onMove func()) FillReturn {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.MoveAndClear(w.posy, w.posx)
|
|
|
|
w.MoveAndClear(w.posy, w.posx)
|
|
|
|
w.Move(w.posy+1, 0)
|
|
|
|
w.Move(w.posy+1, 0)
|
|
|
|
onMove()
|
|
|
|
w.renderer.stderr(resetCode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1022,22 +1033,26 @@ func (w *LightWindow) fill(str string, onMove func()) FillReturn {
|
|
|
|
return FillSuspend
|
|
|
|
return FillSuspend
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.Move(w.posy+1, 0)
|
|
|
|
w.Move(w.posy+1, 0)
|
|
|
|
onMove()
|
|
|
|
w.renderer.stderr(resetCode)
|
|
|
|
return FillNextLine
|
|
|
|
return FillNextLine
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FillContinue
|
|
|
|
return FillContinue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) setBg() {
|
|
|
|
func (w *LightWindow) setBg() string {
|
|
|
|
if w.bg != colDefault {
|
|
|
|
if w.bg != colDefault {
|
|
|
|
w.csiColor(colDefault, w.bg, AttrRegular)
|
|
|
|
_, code := w.csiColor(colDefault, w.bg, AttrRegular)
|
|
|
|
|
|
|
|
return code
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should clear dim attribute after ␍ in the preview window
|
|
|
|
|
|
|
|
// e.g. printf "foo\rbar" | fzf --ansi --preview 'printf "foo\rbar"'
|
|
|
|
|
|
|
|
return "\x1b[m"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) Fill(text string) FillReturn {
|
|
|
|
func (w *LightWindow) Fill(text string) FillReturn {
|
|
|
|
w.Move(w.posy, w.posx)
|
|
|
|
w.Move(w.posy, w.posx)
|
|
|
|
w.setBg()
|
|
|
|
code := w.setBg()
|
|
|
|
return w.fill(text, w.setBg)
|
|
|
|
return w.fill(text, code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillReturn {
|
|
|
|
func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillReturn {
|
|
|
@ -1048,11 +1063,11 @@ func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillRetu
|
|
|
|
if bg == colDefault {
|
|
|
|
if bg == colDefault {
|
|
|
|
bg = w.bg
|
|
|
|
bg = w.bg
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if w.csiColor(fg, bg, attr) {
|
|
|
|
if hasColors, resetCode := w.csiColor(fg, bg, attr); hasColors {
|
|
|
|
defer w.csi("m")
|
|
|
|
defer w.csi("m")
|
|
|
|
return w.fill(text, func() { w.csiColor(fg, bg, attr) })
|
|
|
|
return w.fill(text, resetCode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return w.fill(text, w.setBg)
|
|
|
|
return w.fill(text, w.setBg())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *LightWindow) FinishFill() {
|
|
|
|
func (w *LightWindow) FinishFill() {
|
|
|
|