summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2020-07-27 00:15:25 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2020-07-27 00:27:03 +0900
commit0f9cb5590ec0a397143a7a4caf003a54885f4375 (patch)
tree68f3e5e8adf27919e3656d741ecadce6d5f6f2c8
parentc0a83b27eb0e2a9a1f631d04830a7dc6d762a951 (diff)
Add preview window option for setting the initial scroll offset
Close #1057 Close #2120 # Initial scroll offset is set to the line number of each line of # git grep output *minus* 5 lines git grep --line-number '' | fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
-rw-r--r--CHANGELOG.md7
-rw-r--r--man/man1/fzf.121
-rw-r--r--src/options.go12
-rw-r--r--src/terminal.go56
-rwxr-xr-xtest/test_go.rb18
5 files changed, 97 insertions, 17 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a76a488..b4d81536 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,13 @@ CHANGELOG
# Preview window hidden by default, it appears when you first hit '?'
fzf --bind '?:preview:cat {}' --preview-window hidden
```
+- Added preview window option for setting the initial scroll offset
+ ```sh
+ # Initial scroll offset is set to the line number of each line of
+ # git grep output *minus* 5 lines
+ git grep --line-number '' |
+ fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
+ ```
- Added support for ANSI colors in `--prompt` string
- Vim plugin
- `tmux` layout option for using fzf-tmux
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 878b30b5..ed8328c2 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 "Jun 2020" "fzf 0.22.0" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Jul 2020" "fzf 0.22.0" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@@ -381,7 +381,7 @@ Preview window will be updated even when there is no match for the current
query if any of the placeholder expressions evaluates to a non-empty string.
.RE
.TP
-.BI "--preview-window=" "[POSITION][:SIZE[%]][:noborder][:wrap][:hidden]"
+.BI "--preview-window=" "[POSITION][:SIZE[%]][:noborder][:wrap][:hidden][:+SCROLL[-OFFSET]]"
Determines the layout of the preview window. If the argument contains
\fB:hidden\fR, the preview window will be hidden by default until
\fBtoggle-preview\fR action is triggered. Long lines are truncated by default.
@@ -390,6 +390,12 @@ Line wrap can be enabled with \fB:wrap\fR flag.
If size is given as 0, preview window will not be visible, but fzf will still
execute the command in the background.
+\fB+SCROLL[-OFFSET]\fR determines the initial scroll offset of the preview
+window. \fBSCROLL\fR can be either a numeric integer or a single-field index
+expression that refers to a numeric integer. The optional \fB-OFFSET\fR part is
+for adjusting the base offset so that you can see the text above it. It should
+be given as a numeric integer.
+
.RS
.B POSITION: (default: right)
\fBup
@@ -400,8 +406,15 @@ execute the command in the background.
.RS
e.g.
- \fBfzf --preview="head {}" --preview-window=up:30%
- fzf --preview="file {}" --preview-window=down:1\fR
+ \fB# Non-default scroll window positions and sizes
+ fzf --preview="head {}" --preview-window=up:30%
+ fzf --preview="file {}" --preview-window=down:1
+
+ # Initial scroll offset is set to the line number of each line of
+ # git grep output *minus* 5 lines
+ git grep --line-number '' |
+ fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5\fR
+
.RE
.SS Scripting
.TP
diff --git a/src/options.go b/src/options.go
index e43f5e1b..811615eb 100644
--- a/src/options.go
+++ b/src/options.go
@@ -80,7 +80,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[%]][:wrap][:hidden]
+ [up|down|left|right][:SIZE[%]][:wrap][:hidden][:+SCROLL[-OFFSET]]
Scripting
-q, --query=STR Start the finder with the given query
@@ -159,6 +159,7 @@ type previewOpts struct {
command string
position windowPosition
size sizeSpec
+ scroll string
hidden bool
wrap bool
border bool
@@ -260,7 +261,7 @@ func defaultOptions() *Options {
ToggleSort: false,
Expect: make(map[int]string),
Keymap: make(map[int][]action),
- Preview: previewOpts{"", posRight, sizeSpec{50, true}, false, false, true},
+ Preview: previewOpts{"", posRight, sizeSpec{50, true}, "", false, false, true},
PrintQuery: false,
ReadZero: false,
Printer: func(str string) { fmt.Println(str) },
@@ -994,6 +995,7 @@ func parsePreviewWindow(opts *previewOpts, input string) {
tokens := strings.Split(input, ":")
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
+ offsetRegex := regexp.MustCompile("^\\+([0-9]+|{[0-9]+})(-[0-9]+)?$")
for _, token := range tokens {
switch token {
case "":
@@ -1016,8 +1018,10 @@ func parsePreviewWindow(opts *previewOpts, input string) {
default:
if sizeRegex.MatchString(token) {
opts.size = parseSize(token, 99, "window size")
+ } else if offsetRegex.MatchString(token) {
+ opts.scroll = token[1:]
} else {
- errorExit("invalid preview window layout: " + input)
+ errorExit("invalid preview window option: " + token)
}
}
}
@@ -1270,7 +1274,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[%]][:noborder][:wrap][:hidden]"))
+ nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:noborder][:wrap][:hidden][:+SCROLL[-OFFSET]]"))
case "--height":
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
case "--min-height":
diff --git a/src/terminal.go b/src/terminal.go
index e6bda16e..c144569a 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -262,6 +262,11 @@ type previewRequest struct {
list []*Item
}
+type previewResult struct {
+ content string
+ offset int
+}
+
func toActions(types ...actionType) []action {
actions := make([]action, len(types))
for idx, t := range types {
@@ -1347,6 +1352,39 @@ func cleanTemporaryFiles() {
activeTempFiles = []string{}
}
+func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) string {
+ return replacePlaceholder(
+ template, t.ansi, t.delimiter, t.printsep, forcePlus, input, list)
+}
+
+// Ascii to positive integer
+func atopi(s string) int {
+ n, e := strconv.Atoi(strings.ReplaceAll(s, "'", ""))
+ if e != nil || n < 1 {
+ return 0
+ }
+ return n
+}
+
+func (t *Terminal) evaluateScrollOffset(list []*Item) int {
+ offsetExpr := t.replacePlaceholder(t.preview.scroll, false, "", list)
+ nums := strings.Split(offsetExpr, "-")
+ switch len(nums) {
+ case 0:
+ return 0
+ case 1, 2:
+ base := atopi(nums[0])
+ if base == 0 {
+ return 0
+ } else if len(nums) == 1 {
+ return base - 1
+ }
+ return base - atopi(nums[1]) - 1
+ default:
+ return 0
+ }
+}
+
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {
current := allItems[:1]
selected := allItems[1:]
@@ -1445,7 +1483,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
if !valid {
return
}
- command := replacePlaceholder(template, t.ansi, t.delimiter, t.printsep, forcePlus, string(t.input), list)
+ command := t.replacePlaceholder(template, forcePlus, string(t.input), list)
cmd := util.ExecCommand(command, false)
if !background {
cmd.Stdin = os.Stdin
@@ -1629,8 +1667,8 @@ func (t *Terminal) Loop() {
})
// We don't display preview window if no match
if items[0] != nil {
- command := replacePlaceholder(commandTemplate,
- t.ansi, t.delimiter, t.printsep, false, string(t.Input()), items)
+ command := t.replacePlaceholder(commandTemplate, false, string(t.Input()), items)
+ offset := t.evaluateScrollOffset(items)
cmd := util.ExecCommand(command, true)
if t.pwindow != nil {
env := os.Environ()
@@ -1673,11 +1711,11 @@ func (t *Terminal) Loop() {
cmd.Wait()
finishChan <- true
if out.Len() > 0 || !<-updateChan {
- t.reqBox.Set(reqPreviewDisplay, out.String())
+ t.reqBox.Set(reqPreviewDisplay, previewResult{out.String(), offset})
}
cleanTemporaryFiles()
} else {
- t.reqBox.Set(reqPreviewDisplay, "")
+ t.reqBox.Set(reqPreviewDisplay, previewResult{"", 0})
}
}
}()
@@ -1751,9 +1789,10 @@ func (t *Terminal) Loop() {
return exitNoMatch
})
case reqPreviewDisplay:
- t.previewer.text = value.(string)
+ result := value.(previewResult)
+ t.previewer.text = result.content
t.previewer.lines = strings.Count(t.previewer.text, "\n")
- t.previewer.offset = 0
+ t.previewer.offset = util.Constrain(result.offset, 0, t.previewer.lines-1)
t.printPreview()
case reqPreviewRefresh:
t.printPreview()
@@ -2172,8 +2211,7 @@ func (t *Terminal) Loop() {
valid = !slot || query
}
if valid {
- command := replacePlaceholder(a.a,
- t.ansi, t.delimiter, t.printsep, false, string(t.input), list)
+ command := t.replacePlaceholder(a.a, false, string(t.input), list)
newCommand = &command
}
}
diff --git a/test/test_go.rb b/test/test_go.rb
index e1e22575..75e84ec4 100755
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -1787,6 +1787,24 @@ class TestGoFZF < TestBase
tmux.until { |lines| refute_includes lines[1], '2' }
tmux.until { |lines| assert_includes lines[1], '[111]' }
end
+
+ def test_preview_scroll_begin_constant
+ tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+123", :Enter
+ tmux.until { |lines| lines.item_count == 1 }
+ tmux.until { |lines| assert_match %r{123.*123/1000}, lines[1] }
+ end
+
+ def test_preview_scroll_begin_expr
+ tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+{3}", :Enter
+ tmux.until { |lines| lines.item_count == 1 }
+ tmux.until { |lines| assert_match %r{321.*321/1000}, lines[1] }
+ end
+
+ def test_preview_scroll_begin_and_offset
+ tmux.send_keys "echo foo 123 321 | #{FZF} --preview 'seq 1000' --preview-window left:+{2}-2", :Enter
+ tmux.until { |lines| lines.item_count == 1 }
+ tmux.until { |lines| assert_match %r{121.*121/1000}, lines[1] }
+ end
end
module TestShell