diff --git a/src/options.go b/src/options.go
index 694084e20604c5b14805a01101be8a64ff5344c8..6fd3f6c69add493eb04033846a80eabae85f3c6e 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 092efe4df1b9bcb6225e1d8b0dc1661e61438c5a..07616fcf580467257e34603e6dae1e993be14eba 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 971cd2b5a3e4bf411fa4117af5a32acb7327ca92..8a52980d0cb3bf0cff5cd14f7ffa56a0b7d7ffe7 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 6a09b24d8eb80b8d25c0e02203bf309aaa2c4652..2c1947f1bd0752976e80406f3ce54a6f99afb77e 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 4a8f502d08c0638946706cf053ab5402585e6190..1793836aa1a5a39ddfc308602b0c78d43c6e1d78 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