summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2016-12-05 02:13:47 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2016-12-05 02:13:59 +0900
commita442fe0fd0b42c76c5506e84cbf60ac1f8bec9c3 (patch)
tree898cca91b162e48dce835bc9a8fae1625f689059
parentab9ae4f643ed47538bae6cebddc4c529f8700b45 (diff)
Truncate long lines in preview window
Add `:wrap` to --preview-window to wrap lines instead Close #756
-rw-r--r--src/options.go67
-rw-r--r--src/options_test.go19
-rw-r--r--src/terminal.go26
-rw-r--r--src/tui/ncurses.go8
-rw-r--r--src/tui/tcell.go10
5 files changed, 92 insertions, 38 deletions
diff --git a/src/options.go b/src/options.go
index 694084e2..6fd3f6c6 100644
--- a/src/options.go
+++ b/src/options.go
@@ -66,7 +66,7 @@ const usage = `usage: fzf [options]
Preview
--preview=COMMAND Command to preview highlighted line ({})
--preview-window=OPT Preview window layout (default: right:50%)
- [up|down|left|right][:SIZE[%]][:hidden]
+ [up|down|left|right][:SIZE[%]][:wrap][:hidden]
Scripting
-q, --query=STR Start the finder with the given query
@@ -126,6 +126,7 @@ type previewOpts struct {
position windowPosition
size sizeSpec
hidden bool
+ wrap bool
}
// Options stores the values of command-line options
@@ -207,7 +208,7 @@ func defaultOptions() *Options {
Expect: make(map[int]string),
Keymap: make(map[int]actionType),
Execmap: make(map[int]string),
- Preview: previewOpts{"", posRight, sizeSpec{50, true}, false},
+ Preview: previewOpts{"", posRight, sizeSpec{50, true}, false, false},
PrintQuery: false,
ReadZero: false,
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) {
- layout := input
+ // Default
+ opts.position = posRight
+ opts.size = sizeSpec{50, true}
opts.hidden = false
- if strings.HasSuffix(layout, ":hidden") {
- opts.hidden = true
- layout = strings.TrimSuffix(layout, ":hidden")
- }
-
- tokens := strings.Split(layout, ":")
- if len(tokens) == 0 || len(tokens) > 2 {
- errorExit("invalid window layout: " + input)
- }
-
- if len(tokens) > 1 {
- opts.size = parseSize(tokens[1], 99, "window size")
- } else {
- opts.size = sizeSpec{50, true}
+ opts.wrap = false
+
+ tokens := strings.Split(input, ":")
+ sizeRegex := regexp.MustCompile("^[1-9][0-9]*%?$")
+ for _, token := range tokens {
+ switch token {
+ case "hidden":
+ opts.hidden = true
+ case "wrap":
+ opts.wrap = 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 {
// Adjust size for border
opts.size.size += 2
- }
-
- switch tokens[0] {
- 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)
+ // And padding
+ if opts.position == posLeft || opts.position == posRight {
+ opts.size.size += 2
+ }
}
}
@@ -997,7 +1002,7 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Preview.command = ""
case "--preview-window":
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":
opts.Margin = defaultMargin()
case "--margin":
diff --git a/src/options_test.go b/src/options_test.go
index 092efe4d..07616fcf 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -378,26 +378,37 @@ func TestPreviewOpts(t *testing.T) {
opts := optsFor()
if !(opts.Preview.command == "" &&
opts.Preview.hidden == false &&
+ opts.Preview.wrap == false &&
opts.Preview.position == posRight &&
opts.Preview.size.percent == true &&
opts.Preview.size.size == 50) {
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 {}" &&
opts.Preview.hidden == true &&
+ opts.Preview.wrap == true &&
opts.Preview.position == posLeft &&
opts.Preview.size.percent == false &&
- opts.Preview.size.size == 15+2) {
+ opts.Preview.size.size == 15+2+2) {
t.Error(opts.Preview)
}
-
- opts = optsFor("--preview-window=left:15:hidden", "--preview-window=down")
+ opts = optsFor("--preview-window=up:15:wrap:hidden", "--preview-window=down")
if !(opts.Preview.command == "" &&
opts.Preview.hidden == false &&
+ opts.Preview.wrap == false &&
opts.Preview.position == posDown &&
opts.Preview.size.percent == true &&
opts.Preview.size.size == 50) {
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)
+ }
}
diff --git a/src/terminal.go b/src/terminal.go
index 971cd2b5..8a52980d 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -488,7 +488,14 @@ func (t *Terminal) resizeWindows() {
if t.isPreviewEnabled() {
createPreviewWindow := func(y int, x int, w int, h int) {
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 {
case posUp:
@@ -657,7 +664,7 @@ func trimRight(runes []rune, width int) ([]rune, int) {
l := 0
for idx, r := range runes {
l += runeWidth(r, l)
- if idx > 0 && l > width {
+ if l > width {
return runes[:idx], len(runes) - idx
}
}
@@ -828,6 +835,21 @@ func (t *Terminal) printPreview() {
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() {
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.attr)
}
diff --git a/src/tui/ncurses.go b/src/tui/ncurses.go
index 6a09b24d..2c1947f1 100644
--- a/src/tui/ncurses.go
+++ b/src/tui/ncurses.go
@@ -273,6 +273,14 @@ func (w *Window) Erase() {
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 {
return C.waddstr(w.win(), C.CString(str)) == C.OK
}
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index 4a8f502d..1793836a 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -172,6 +172,14 @@ func (w *Window) win() *WindowTcell {
return (*WindowTcell)(w.impl)
}
+func (w *Window) X() int {
+ return w.impl.LastX
+}
+
+func DoesAutoWrap() bool {
+ return false
+}
+
func Clear() {
_screen.Sync()
_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
// word wrap:
- if xPos > (w.Left + w.Width) {
+ if xPos >= (w.Left + w.Width) {
w.win().LastY++
w.win().LastX = 0
lx = 0