mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-07 07:31:58 -07:00
Truncate long lines in preview window
Add `:wrap` to --preview-window to wrap lines instead Close #756
This commit is contained in:
@@ -66,7 +66,7 @@ const usage = `usage: fzf [options]
|
|||||||
Preview
|
Preview
|
||||||
--preview=COMMAND Command to preview highlighted line ({})
|
--preview=COMMAND Command to preview highlighted line ({})
|
||||||
--preview-window=OPT Preview window layout (default: right:50%)
|
--preview-window=OPT Preview window layout (default: right:50%)
|
||||||
[up|down|left|right][:SIZE[%]][:hidden]
|
[up|down|left|right][:SIZE[%]][:wrap][:hidden]
|
||||||
|
|
||||||
Scripting
|
Scripting
|
||||||
-q, --query=STR Start the finder with the given query
|
-q, --query=STR Start the finder with the given query
|
||||||
@@ -126,6 +126,7 @@ type previewOpts struct {
|
|||||||
position windowPosition
|
position windowPosition
|
||||||
size sizeSpec
|
size sizeSpec
|
||||||
hidden bool
|
hidden bool
|
||||||
|
wrap bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options stores the values of command-line options
|
// Options stores the values of command-line options
|
||||||
@@ -207,7 +208,7 @@ func defaultOptions() *Options {
|
|||||||
Expect: make(map[int]string),
|
Expect: make(map[int]string),
|
||||||
Keymap: make(map[int]actionType),
|
Keymap: make(map[int]actionType),
|
||||||
Execmap: make(map[int]string),
|
Execmap: make(map[int]string),
|
||||||
Preview: previewOpts{"", posRight, sizeSpec{50, true}, false},
|
Preview: previewOpts{"", posRight, sizeSpec{50, true}, false, false},
|
||||||
PrintQuery: false,
|
PrintQuery: false,
|
||||||
ReadZero: false,
|
ReadZero: false,
|
||||||
Printer: func(str string) { fmt.Println(str) },
|
Printer: func(str string) { fmt.Println(str) },
|
||||||
@@ -760,39 +761,43 @@ func parseSize(str string, maxPercent float64, label string) sizeSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parsePreviewWindow(opts *previewOpts, input string) {
|
func parsePreviewWindow(opts *previewOpts, input string) {
|
||||||
layout := input
|
// Default
|
||||||
|
opts.position = posRight
|
||||||
|
opts.size = sizeSpec{50, true}
|
||||||
opts.hidden = false
|
opts.hidden = false
|
||||||
if strings.HasSuffix(layout, ":hidden") {
|
opts.wrap = false
|
||||||
opts.hidden = true
|
|
||||||
layout = strings.TrimSuffix(layout, ":hidden")
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens := strings.Split(layout, ":")
|
tokens := strings.Split(input, ":")
|
||||||
if len(tokens) == 0 || len(tokens) > 2 {
|
sizeRegex := regexp.MustCompile("^[1-9][0-9]*%?$")
|
||||||
errorExit("invalid window layout: " + input)
|
for _, token := range tokens {
|
||||||
}
|
switch token {
|
||||||
|
case "hidden":
|
||||||
if len(tokens) > 1 {
|
opts.hidden = true
|
||||||
opts.size = parseSize(tokens[1], 99, "window size")
|
case "wrap":
|
||||||
} else {
|
opts.wrap = true
|
||||||
opts.size = sizeSpec{50, true}
|
case "up", "top":
|
||||||
|
opts.position = posUp
|
||||||
|
case "down", "bottom":
|
||||||
|
opts.position = posDown
|
||||||
|
case "left":
|
||||||
|
opts.position = posLeft
|
||||||
|
case "right":
|
||||||
|
opts.position = posRight
|
||||||
|
default:
|
||||||
|
if sizeRegex.MatchString(token) {
|
||||||
|
opts.size = parseSize(token, 99, "window size")
|
||||||
|
} else {
|
||||||
|
errorExit("invalid preview window layout: " + input)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !opts.size.percent && opts.size.size > 0 {
|
if !opts.size.percent && opts.size.size > 0 {
|
||||||
// Adjust size for border
|
// Adjust size for border
|
||||||
opts.size.size += 2
|
opts.size.size += 2
|
||||||
}
|
// And padding
|
||||||
|
if opts.position == posLeft || opts.position == posRight {
|
||||||
switch tokens[0] {
|
opts.size.size += 2
|
||||||
case "up":
|
}
|
||||||
opts.position = posUp
|
|
||||||
case "down":
|
|
||||||
opts.position = posDown
|
|
||||||
case "left":
|
|
||||||
opts.position = posLeft
|
|
||||||
case "right":
|
|
||||||
opts.position = posRight
|
|
||||||
default:
|
|
||||||
errorExit("invalid window position: " + input)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -997,7 +1002,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Preview.command = ""
|
opts.Preview.command = ""
|
||||||
case "--preview-window":
|
case "--preview-window":
|
||||||
parsePreviewWindow(&opts.Preview,
|
parsePreviewWindow(&opts.Preview,
|
||||||
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]]"))
|
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:wrap][:hidden]"))
|
||||||
case "--no-margin":
|
case "--no-margin":
|
||||||
opts.Margin = defaultMargin()
|
opts.Margin = defaultMargin()
|
||||||
case "--margin":
|
case "--margin":
|
||||||
|
@@ -378,26 +378,37 @@ func TestPreviewOpts(t *testing.T) {
|
|||||||
opts := optsFor()
|
opts := optsFor()
|
||||||
if !(opts.Preview.command == "" &&
|
if !(opts.Preview.command == "" &&
|
||||||
opts.Preview.hidden == false &&
|
opts.Preview.hidden == false &&
|
||||||
|
opts.Preview.wrap == false &&
|
||||||
opts.Preview.position == posRight &&
|
opts.Preview.position == posRight &&
|
||||||
opts.Preview.size.percent == true &&
|
opts.Preview.size.percent == true &&
|
||||||
opts.Preview.size.size == 50) {
|
opts.Preview.size.size == 50) {
|
||||||
t.Error()
|
t.Error()
|
||||||
}
|
}
|
||||||
opts = optsFor("--preview", "cat {}", "--preview-window=left:15:hidden")
|
opts = optsFor("--preview", "cat {}", "--preview-window=left:15:hidden:wrap")
|
||||||
if !(opts.Preview.command == "cat {}" &&
|
if !(opts.Preview.command == "cat {}" &&
|
||||||
opts.Preview.hidden == true &&
|
opts.Preview.hidden == true &&
|
||||||
|
opts.Preview.wrap == true &&
|
||||||
opts.Preview.position == posLeft &&
|
opts.Preview.position == posLeft &&
|
||||||
opts.Preview.size.percent == false &&
|
opts.Preview.size.percent == false &&
|
||||||
opts.Preview.size.size == 15+2) {
|
opts.Preview.size.size == 15+2+2) {
|
||||||
t.Error(opts.Preview)
|
t.Error(opts.Preview)
|
||||||
}
|
}
|
||||||
|
opts = optsFor("--preview-window=up:15:wrap:hidden", "--preview-window=down")
|
||||||
opts = optsFor("--preview-window=left:15:hidden", "--preview-window=down")
|
|
||||||
if !(opts.Preview.command == "" &&
|
if !(opts.Preview.command == "" &&
|
||||||
opts.Preview.hidden == false &&
|
opts.Preview.hidden == false &&
|
||||||
|
opts.Preview.wrap == false &&
|
||||||
opts.Preview.position == posDown &&
|
opts.Preview.position == posDown &&
|
||||||
opts.Preview.size.percent == true &&
|
opts.Preview.size.percent == true &&
|
||||||
opts.Preview.size.size == 50) {
|
opts.Preview.size.size == 50) {
|
||||||
t.Error(opts.Preview)
|
t.Error(opts.Preview)
|
||||||
}
|
}
|
||||||
|
opts = optsFor("--preview-window=up:15:wrap:hidden")
|
||||||
|
if !(opts.Preview.command == "" &&
|
||||||
|
opts.Preview.hidden == true &&
|
||||||
|
opts.Preview.wrap == true &&
|
||||||
|
opts.Preview.position == posUp &&
|
||||||
|
opts.Preview.size.percent == false &&
|
||||||
|
opts.Preview.size.size == 15+2) {
|
||||||
|
t.Error(opts.Preview)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -488,7 +488,14 @@ func (t *Terminal) resizeWindows() {
|
|||||||
if t.isPreviewEnabled() {
|
if t.isPreviewEnabled() {
|
||||||
createPreviewWindow := func(y int, x int, w int, h int) {
|
createPreviewWindow := func(y int, x int, w int, h int) {
|
||||||
t.bwindow = tui.NewWindow(y, x, w, h, true)
|
t.bwindow = tui.NewWindow(y, x, w, h, true)
|
||||||
t.pwindow = tui.NewWindow(y+1, x+2, w-4, h-2, false)
|
pwidth := w - 4
|
||||||
|
// ncurses auto-wraps the line when the cursor reaches the right-end of
|
||||||
|
// the window. To prevent unintended line-wraps, we use the width one
|
||||||
|
// column larger than the desired value.
|
||||||
|
if !t.preview.wrap && tui.DoesAutoWrap() {
|
||||||
|
pwidth += 1
|
||||||
|
}
|
||||||
|
t.pwindow = tui.NewWindow(y+1, x+2, pwidth, h-2, false)
|
||||||
}
|
}
|
||||||
switch t.preview.position {
|
switch t.preview.position {
|
||||||
case posUp:
|
case posUp:
|
||||||
@@ -657,7 +664,7 @@ func trimRight(runes []rune, width int) ([]rune, int) {
|
|||||||
l := 0
|
l := 0
|
||||||
for idx, r := range runes {
|
for idx, r := range runes {
|
||||||
l += runeWidth(r, l)
|
l += runeWidth(r, l)
|
||||||
if idx > 0 && l > width {
|
if l > width {
|
||||||
return runes[:idx], len(runes) - idx
|
return runes[:idx], len(runes) - idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -828,6 +835,21 @@ func (t *Terminal) printPreview() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !t.preview.wrap {
|
||||||
|
lines := strings.Split(str, "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
limit := t.pwindow.Width
|
||||||
|
if tui.DoesAutoWrap() {
|
||||||
|
limit -= 1
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
limit -= t.pwindow.X()
|
||||||
|
}
|
||||||
|
trimmed, _ := trimRight([]rune(line), limit)
|
||||||
|
lines[i] = string(trimmed)
|
||||||
|
}
|
||||||
|
str = strings.Join(lines, "\n")
|
||||||
|
}
|
||||||
if ansi != nil && ansi.colored() {
|
if ansi != nil && ansi.colored() {
|
||||||
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.attr)
|
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.attr)
|
||||||
}
|
}
|
||||||
|
@@ -273,6 +273,14 @@ func (w *Window) Erase() {
|
|||||||
C.werase(w.win())
|
C.werase(w.win())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Window) X() int {
|
||||||
|
return int(C.getcurx(w.win()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoesAutoWrap() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Window) Fill(str string) bool {
|
func (w *Window) Fill(str string) bool {
|
||||||
return C.waddstr(w.win(), C.CString(str)) == C.OK
|
return C.waddstr(w.win(), C.CString(str)) == C.OK
|
||||||
}
|
}
|
||||||
|
@@ -172,6 +172,14 @@ func (w *Window) win() *WindowTcell {
|
|||||||
return (*WindowTcell)(w.impl)
|
return (*WindowTcell)(w.impl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Window) X() int {
|
||||||
|
return w.impl.LastX
|
||||||
|
}
|
||||||
|
|
||||||
|
func DoesAutoWrap() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func Clear() {
|
func Clear() {
|
||||||
_screen.Sync()
|
_screen.Sync()
|
||||||
_screen.Clear()
|
_screen.Clear()
|
||||||
@@ -521,7 +529,7 @@ func (w *Window) FillString(text string, pair ColorPair, a Attr) bool {
|
|||||||
var xPos = w.Left + w.win().LastX + lx
|
var xPos = w.Left + w.win().LastX + lx
|
||||||
|
|
||||||
// word wrap:
|
// word wrap:
|
||||||
if xPos > (w.Left + w.Width) {
|
if xPos >= (w.Left + w.Width) {
|
||||||
w.win().LastY++
|
w.win().LastY++
|
||||||
w.win().LastX = 0
|
w.win().LastX = 0
|
||||||
lx = 0
|
lx = 0
|
||||||
|
Reference in New Issue
Block a user