From 2ccdf21a1fa0ce64123fe89e1b7931240420e8e6 Mon Sep 17 00:00:00 2001
From: Junegunn Choi <junegunn.c@gmail.com>
Date: Wed, 2 Mar 2016 03:06:21 +0900
Subject: [PATCH] Add --hscroll-off=COL option

Close #513
---
 man/man1/fzf.1   |  7 ++++++-
 src/options.go   | 12 ++++++++++++
 src/terminal.go  |  9 +++++----
 src/util/util.go |  8 ++++++++
 test/test_go.rb  | 12 ++++++++++++
 5 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index ef48770a..1a99a548 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf 1 "Feb 2016" "fzf 0.11.3" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Mar 2016" "fzf 0.11.4" "fzf - a command-line fuzzy finder"
 
 .SH NAME
 fzf - a command-line fuzzy finder
@@ -175,6 +175,11 @@ Enable cyclic scroll
 .B "--no-hscroll"
 Disable horizontal scroll
 .TP
+.BI "--hscroll-off=" "COL"
+Number of screen columns to keep to the right of the highlighted substring
+(default: 10). Setting it to a large value will cause the text to be positioned
+on the center of the screen.
+.TP
 .B "--inline-info"
 Display finder info inline with the query
 .TP
diff --git a/src/options.go b/src/options.go
index 723c7fc6..dfd9a1b6 100644
--- a/src/options.go
+++ b/src/options.go
@@ -42,6 +42,8 @@ const usage = `usage: fzf [options]
     --tabstop=SPACES      Number of spaces for a tab character (default: 8)
     --cycle               Enable cyclic scroll
     --no-hscroll          Disable horizontal scroll
+    --hscroll-off=COL     Number of screen columns to keep to the right of the
+                          highlighted substring (default: 10)
     --inline-info         Display finder info inline with the query
     --prompt=STR          Input prompt (default: '> ')
     --bind=KEYBINDS       Custom key bindings. Refer to the man page.
@@ -108,6 +110,7 @@ type Options struct {
 	Reverse     bool
 	Cycle       bool
 	Hscroll     bool
+	HscrollOff  int
 	InlineInfo  bool
 	Prompt      string
 	Query       string
@@ -155,6 +158,7 @@ func defaultOptions() *Options {
 		Reverse:     false,
 		Cycle:       false,
 		Hscroll:     true,
+		HscrollOff:  10,
 		InlineInfo:  false,
 		Prompt:      "> ",
 		Query:       "",
@@ -795,6 +799,8 @@ func parseOptions(opts *Options, allArgs []string) {
 			opts.Hscroll = true
 		case "--no-hscroll":
 			opts.Hscroll = false
+		case "--hscroll-off":
+			opts.HscrollOff = nextInt(allArgs, &i, "hscroll offset required")
 		case "--inline-info":
 			opts.InlineInfo = true
 		case "--no-inline-info":
@@ -884,6 +890,8 @@ func parseOptions(opts *Options, allArgs []string) {
 				opts.Margin = parseMargin(value)
 			} else if match, value := optString(arg, "--tabstop="); match {
 				opts.Tabstop = atoi(value)
+			} else if match, value := optString(arg, "--hscroll-off="); match {
+				opts.HscrollOff = atoi(value)
 			} else {
 				errorExit("unknown option: " + arg)
 			}
@@ -894,6 +902,10 @@ func parseOptions(opts *Options, allArgs []string) {
 		errorExit("header lines must be a non-negative integer")
 	}
 
+	if opts.HscrollOff < 0 {
+		errorExit("hscroll offset must be a non-negative integer")
+	}
+
 	if opts.Tabstop < 1 {
 		errorExit("tab stop must be a positive integer")
 	}
diff --git a/src/terminal.go b/src/terminal.go
index 3c6f47c6..d95cfad3 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -26,6 +26,7 @@ type Terminal struct {
 	prompt     string
 	reverse    bool
 	hscroll    bool
+	hscrollOff int
 	cx         int
 	cy         int
 	offset     int
@@ -210,6 +211,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
 		prompt:     opts.Prompt,
 		reverse:    opts.Reverse,
 		hscroll:    opts.Hscroll,
+		hscrollOff: opts.HscrollOff,
 		cx:         len(input),
 		cy:         0,
 		offset:     0,
@@ -556,11 +558,9 @@ func trimLeft(runes []rune, width int) ([]rune, int32) {
 }
 
 func (t *Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int, current bool) {
-	var maxe int32
+	var maxe int
 	for _, offset := range item.offsets {
-		if offset[1] > maxe {
-			maxe = offset[1]
-		}
+		maxe = util.Max(maxe, int(offset[1]))
 	}
 
 	// Overflow
@@ -568,6 +568,7 @@ func (t *Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int, c
 	copy(text, item.text)
 	offsets := item.colorOffsets(col2, bold, current)
 	maxWidth := C.MaxX() - 3 - t.marginInt[1] - t.marginInt[3]
+	maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
 	fullWidth := displayWidth(text)
 	if fullWidth > maxWidth {
 		if t.hscroll {
diff --git a/src/util/util.go b/src/util/util.go
index ab9e7664..4f3d409d 100644
--- a/src/util/util.go
+++ b/src/util/util.go
@@ -21,6 +21,14 @@ func Max(first int, items ...int) int {
 	return max
 }
 
+// Min returns the smallest integer
+func Min(first int, second int) int {
+	if first <= second {
+		return first
+	}
+	return second
+}
+
 // Min32 returns the smallest 32-bit integer
 func Min32(first int32, second int32) int32 {
 	if first <= second {
diff --git a/test/test_go.rb b/test/test_go.rb
index 7a2d0039..28cb8339 100644
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -1137,6 +1137,18 @@ class TestGoFZF < TestBase
       `seq 10 | #{FZF} -f '1 | !1'`.lines.map(&:chomp)
   end
 
+  def test_hscroll_off
+    writelines tempname, ['=' * 10000 + '0123456789']
+    [0, 3, 6].each do |off|
+      tmux.prepare
+      tmux.send_keys "#{FZF} --hscroll-off=#{off} -q 0 < #{tempname}", :Enter
+      tmux.until { |lines| lines[-3].end_with?((0..off).to_a.join + '..') }
+      tmux.send_keys '9'
+      tmux.until { |lines| lines[-3].end_with? '789' }
+      tmux.send_keys :Enter
+    end
+  end
+
 private
   def writelines path, lines
     File.unlink path while File.exists? path
-- 
GitLab