mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-17 15:53:39 -05:00
Advanced preview scroll offset expression to better support fixed header
This commit is contained in:
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user