summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2021-03-13 02:24:37 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2021-03-13 02:26:41 +0900
commit1b08f43f82af507165ec773922f4793da3cd9b4b (patch)
tree7c553c96917437a5953d0901e1151eeefe4e7858
parentb24a2e2fdcf15b3d0d353795f4bfa3d0b0fc06e6 (diff)
Advanced preview scroll offset expression to better support fixed header
-rw-r--r--CHANGELOG.md15
-rw-r--r--man/man1/fzf-tmux.12
-rw-r--r--man/man1/fzf.137
-rw-r--r--src/options.go8
-rw-r--r--src/options_test.go6
-rw-r--r--src/terminal.go52
6 files changed, 68 insertions, 52 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82422907..78fed7d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,21 @@ CHANGELOG
# Display top 3 lines as the fixed header
fzf --preview 'bat --style=header,grid --color=always {}' --preview-window '~3'
```
+- More advanced preview offset expression to better support the fixed header
+ ```sh
+ # Preview with bat, matching line in the middle of the window below
+ # the fixed header of the top 3 lines
+ #
+ # ~3 Top 3 lines as the fixed header
+ # +{2} Base scroll offset extracted from the second field
+ # +3 Extra offset to compensate for the 3-line header
+ # /2 Put in the middle of the preview area
+ #
+ git grep --line-number '' |
+ fzf --delimiter : \
+ --preview 'bat --style=full --color=always --highlight-line {2} {1}' \
+ --preview-window '~3:+{2}+3/2'
+ ```
- Added `select` and `deselect` action for unconditionally selecting or
deselecting a single item in `--multi` mode. Complements `toggle` action.
- Sigificant performance improvement in ANSI code processing
diff --git a/man/man1/fzf-tmux.1 b/man/man1/fzf-tmux.1
index 21cd0135..7aae95a8 100644
--- a/man/man1/fzf-tmux.1
+++ b/man/man1/fzf-tmux.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-tmux 1 "Feb 2021" "fzf 0.25.1" "fzf-tmux - open fzf in tmux split pane"
+.TH fzf-tmux 1 "Mar 2021" "fzf 0.26.0" "fzf-tmux - open fzf in tmux split pane"
.SH NAME
fzf-tmux - open fzf in tmux split pane
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index d4388940..54f9fe0c 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 2021" "fzf 0.25.1" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Mar 2021" "fzf 0.26.0" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@@ -442,7 +442,7 @@ e.g.
done'\fR
.RE
.TP
-.BI "--preview-window=" "[POSITION][:SIZE[%]][:rounded|sharp|noborder][:[no]wrap][:[no]follow][:[no]cycle][:[no]hidden][:+SCROLL[-OFFSET]][:~HEADER_LINES][:default]"
+.BI "--preview-window=" "[POSITION][:SIZE[%]][:rounded|sharp|noborder][:[no]wrap][:[no]follow][:[no]cycle][:[no]hidden][:+SCROLL[OFFSETS][/DENOM]][:~HEADER_LINES][:default]"
.RS
.B POSITION: (default: right)
@@ -480,12 +480,14 @@ e.g.
\fBrounded\fR (border with rounded edges, default), \fBsharp\fR (border with
sharp edges), or \fBnoborder\fR (no border).
-* \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 (\fB-INTEGER\fR), or as a denominator form
-(\fB-/INTEGER\fR) for specifying a fraction of the preview window height.
+* \fB[:+SCROLL[OFFSETS][/DENOM]]\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 \fBOFFSETS\fR part is for adjusting the base offset. It should be given as a series of signed integers (\fB-INTEGER\fR or \fB+INTEGER\fR).
+
+ - The final \fB/DENOM\fR part is for specifying a fraction of the preview window height.
* \fB~HEADER_LINES\fR keeps the top N lines as the fixed header so that they
are always visible.
@@ -501,16 +503,23 @@ e.g.
# Initial scroll offset is set to the line number of each line of
# git grep output *minus* 5 lines (-5)
git grep --line-number '' |
- fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
-
- # Preview with bat, matching line in the middle of the window (-/2)
+ fzf --delimiter : --preview 'nl {1}' --preview-window '+{2}-5'
+
+ # Preview with bat, matching line in the middle of the window below
+ # the fixed header of the top 3 lines
+ #
+ # ~3 Top 3 lines as the fixed header
+ # +{2} Base scroll offset extracted from the second field
+ # +3 Extra offset to compensate for the 3-line header
+ # /2 Put in the middle of the preview area
+ #
git grep --line-number '' |
fzf --delimiter : \\
- --preview 'bat --style=numbers --color=always --highlight-line {2} {1}' \\
- --preview-window +{2}-/2\fR
+ --preview 'bat --style=full --color=always --highlight-line {2} {1}' \\
+ --preview-window '~3:+{2}+3/2'
# Display top 3 lines as the fixed header
- fzf --preview 'bat --style=header,grid --color=always {}' --preview-window '~3'
+ fzf --preview 'bat --style=full --color=always {}' --preview-window '~3'\fR
.RE
.SS Scripting
diff --git a/src/options.go b/src/options.go
index 29c90373..8d0afe06 100644
--- a/src/options.go
+++ b/src/options.go
@@ -84,7 +84,7 @@ const usage = `usage: fzf [options]
[up|down|left|right][:SIZE[%]]
[:[no]wrap][:[no]cycle][:[no]follow][:[no]hidden]
[:rounded|sharp|noborder]
- [:+SCROLL[-OFFSET]][:~HEADER_LINES]
+ [:+SCROLL[OFFSETS][/DENOM]][:~HEADER_LINES]
[:default]
Scripting
@@ -1078,7 +1078,7 @@ func parseInfoStyle(str string) infoStyle {
func parsePreviewWindow(opts *previewOpts, input string) {
tokens := strings.Split(input, ":")
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
- offsetRegex := regexp.MustCompile("^\\+([0-9]+|{-?[0-9]+})(-[0-9]+|-/[1-9][0-9]*)?$")
+ offsetRegex := regexp.MustCompile(`^(\+{-?[0-9]+})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`)
headerRegex := regexp.MustCompile("^~(0|[1-9][0-9]*)$")
for _, token := range tokens {
switch token {
@@ -1121,7 +1121,7 @@ func parsePreviewWindow(opts *previewOpts, input string) {
} else if sizeRegex.MatchString(token) {
opts.size = parseSize(token, 99, "window size")
} else if offsetRegex.MatchString(token) {
- opts.scroll = token[1:]
+ opts.scroll = token
} else {
errorExit("invalid preview window option: " + token)
}
@@ -1368,7 +1368,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[%]][:rounded|sharp|noborder][:wrap][:cycle][:hidden][:+SCROLL[-OFFSET]][:~HEADER_LINES][:default]"))
+ nextString(allArgs, &i, "preview window layout required: [up|down|left|right][:SIZE[%]][:rounded|sharp|noborder][:wrap][:cycle][:hidden][:+SCROLL[OFFSETS][/DENOM]][:~HEADER_LINES][:default]"))
case "--height":
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
case "--min-height":
diff --git a/src/options_test.go b/src/options_test.go
index 791b55d4..ce135413 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -389,18 +389,18 @@ func TestPreviewOpts(t *testing.T) {
opts.Preview.hidden == true &&
opts.Preview.wrap == true &&
opts.Preview.position == posLeft &&
- opts.Preview.scroll == "{1}-/2" &&
+ opts.Preview.scroll == "+{1}-/2" &&
opts.Preview.size.percent == false &&
opts.Preview.size.size == 15) {
t.Error(opts.Preview)
}
- opts = optsFor("--preview-window=up:15:wrap:hidden:+{1}-/2", "--preview-window=down", "--preview-window=cycle")
+ opts = optsFor("--preview-window=up:15:wrap:hidden:+{1}+3-1-2/2", "--preview-window=down", "--preview-window=cycle")
if !(opts.Preview.command == "" &&
opts.Preview.hidden == true &&
opts.Preview.wrap == true &&
opts.Preview.cycle == true &&
opts.Preview.position == posDown &&
- opts.Preview.scroll == "{1}-/2" &&
+ opts.Preview.scroll == "+{1}+3-1-2/2" &&
opts.Preview.size.percent == false &&
opts.Preview.size.size == 15) {
t.Error(opts.Preview.size.size)
diff --git a/src/terminal.go b/src/terminal.go
index 9fa11212..53a32769 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -22,8 +22,9 @@ import (
// import "github.com/pkg/profile"
var placeholder *regexp.Regexp
-var numericPrefix *regexp.Regexp
var whiteSuffix *regexp.Regexp
+var offsetComponentRegex *regexp.Regexp
+var offsetTrimCharsRegex *regexp.Regexp
var activeTempFiles []string
const ellipsis string = ".."
@@ -31,8 +32,9 @@ const clearCode string = "\x1b[2J"
func init() {
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
- numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
whiteSuffix = regexp.MustCompile(`\s*$`)
+ offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
+ offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
activeTempFiles = []string{}
}
@@ -1591,43 +1593,33 @@ func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input str
template, t.ansi, t.delimiter, t.printsep, forcePlus, input, list)
}
-// Ascii to positive integer
-func atopi(s string) int {
- matches := numericPrefix.FindStringSubmatch(s)
- if len(matches) < 2 {
- return 0
- }
- n, e := strconv.Atoi(matches[1])
- if e != nil || n < 1 {
- return 0
- }
- return n
-}
-
func (t *Terminal) evaluateScrollOffset(list []*Item, height int) int {
- offsetExpr := t.replacePlaceholder(t.previewOpts.scroll, false, "", list)
- nums := strings.Split(offsetExpr, "-")
- switch len(nums) {
- case 0:
- return 0
- case 1, 2:
- base := atopi(nums[0])
- if base == 0 {
+ offsetExpr := offsetTrimCharsRegex.ReplaceAllString(
+ t.replacePlaceholder(t.previewOpts.scroll, false, "", list), "")
+
+ atoi := func(s string) int {
+ n, e := strconv.Atoi(s)
+ if e != nil {
return 0
- } else if len(nums) == 1 {
- return base - 1
}
- if nums[1][0] == '/' {
- denom := atopi(nums[1][1:])
+ return n
+ }
+
+ base := -1
+ for _, component := range offsetComponentRegex.FindAllString(offsetExpr, -1) {
+ if strings.HasPrefix(component, "-/") {
+ component = component[1:]
+ }
+ if component[0] == '/' {
+ denom := atoi(component[1:])
if denom == 0 {
return base
}
return base - height/denom
}
- return base - atopi(nums[1]) - 1
- default:
- return 0
+ base += atoi(component)
}
+ return base
}
func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, printsep string, forcePlus bool, query string, allItems []*Item) string {