mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-12-23 11:29:01 +00:00
Implement flag for preserving whitespace around field (#1242)
This commit is contained in:
parent
9ff33814ea
commit
43345fb642
@ -285,6 +285,9 @@ was made) individually quoted.
|
|||||||
e.g. \fBfzf --multi --preview='head -10 {+}'\fR
|
e.g. \fBfzf --multi --preview='head -10 {+}'\fR
|
||||||
\fBgit log --oneline | fzf --multi --preview 'git show {+1}'\fR
|
\fBgit log --oneline | fzf --multi --preview 'git show {+1}'\fR
|
||||||
|
|
||||||
|
When using a field index expression, leading and trailing whitespace is stripped
|
||||||
|
from the replacement string. To preserve the whitespace, use the \fBs\fR flag.
|
||||||
|
|
||||||
Also, \fB{q}\fR is replaced to the current query string.
|
Also, \fB{q}\fR is replaced to the current query string.
|
||||||
|
|
||||||
Note that you can escape a placeholder pattern by prepending a backslash.
|
Note that you can escape a placeholder pattern by prepending a backslash.
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
var placeholder *regexp.Regexp
|
var placeholder *regexp.Regexp
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
placeholder = regexp.MustCompile("\\\\?(?:{\\+?[0-9,-.]*}|{q})")
|
placeholder = regexp.MustCompile("\\\\?(?:{[+s]*[0-9,-.]*}|{q})")
|
||||||
}
|
}
|
||||||
|
|
||||||
type jumpMode int
|
type jumpMode int
|
||||||
@ -221,6 +221,11 @@ const (
|
|||||||
actTop
|
actTop
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type placeholderFlags struct {
|
||||||
|
plus bool
|
||||||
|
preserveSpace bool
|
||||||
|
}
|
||||||
|
|
||||||
func toActions(types ...actionType) []action {
|
func toActions(types ...actionType) []action {
|
||||||
actions := make([]action, len(types))
|
actions := make([]action, len(types))
|
||||||
for idx, t := range types {
|
for idx, t := range types {
|
||||||
@ -1130,12 +1135,37 @@ func quoteEntry(entry string) string {
|
|||||||
return "'" + strings.Replace(entry, "'", "'\\''", -1) + "'"
|
return "'" + strings.Replace(entry, "'", "'\\''", -1) + "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parsePlaceholder(match string) (bool, string, placeholderFlags) {
|
||||||
|
flags := placeholderFlags{}
|
||||||
|
|
||||||
|
if match[0] == '\\' {
|
||||||
|
// Escaped placeholder pattern
|
||||||
|
return true, match[1:], flags
|
||||||
|
}
|
||||||
|
|
||||||
|
skipChars := 1
|
||||||
|
for _, char := range match[1:] {
|
||||||
|
switch char {
|
||||||
|
case '+':
|
||||||
|
flags.plus = true
|
||||||
|
skipChars++
|
||||||
|
case 's':
|
||||||
|
flags.preserveSpace = true
|
||||||
|
skipChars++
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matchWithoutFlags := "{" + match[skipChars:]
|
||||||
|
|
||||||
|
return false, matchWithoutFlags, flags
|
||||||
|
}
|
||||||
|
|
||||||
func hasPlusFlag(template string) bool {
|
func hasPlusFlag(template string) bool {
|
||||||
for _, match := range placeholder.FindAllString(template, -1) {
|
for _, match := range placeholder.FindAllString(template, -1) {
|
||||||
if match[0] == '\\' {
|
_, _, flags := parsePlaceholder(match)
|
||||||
continue
|
if flags.plus {
|
||||||
}
|
|
||||||
if match[1] == '+' {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1152,9 +1182,10 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
|
|||||||
selected = []*Item{}
|
selected = []*Item{}
|
||||||
}
|
}
|
||||||
return placeholder.ReplaceAllStringFunc(template, func(match string) string {
|
return placeholder.ReplaceAllStringFunc(template, func(match string) string {
|
||||||
// Escaped pattern
|
escaped, match, flags := parsePlaceholder(match)
|
||||||
if match[0] == '\\' {
|
|
||||||
return match[1:]
|
if escaped {
|
||||||
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current query
|
// Current query
|
||||||
@ -1162,13 +1193,8 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
|
|||||||
return quoteEntry(query)
|
return quoteEntry(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
plusFlag := forcePlus
|
|
||||||
if match[1] == '+' {
|
|
||||||
match = "{" + match[2:]
|
|
||||||
plusFlag = true
|
|
||||||
}
|
|
||||||
items := current
|
items := current
|
||||||
if plusFlag {
|
if flags.plus || forcePlus {
|
||||||
items = selected
|
items = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1204,7 +1230,9 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
|
|||||||
str = str[:delims[len(delims)-1][0]]
|
str = str[:delims[len(delims)-1][0]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !flags.preserveSpace {
|
||||||
str = strings.TrimSpace(str)
|
str = strings.TrimSpace(str)
|
||||||
|
}
|
||||||
replacements[idx] = quoteEntry(str)
|
replacements[idx] = quoteEntry(str)
|
||||||
}
|
}
|
||||||
return strings.Join(replacements, " ")
|
return strings.Join(replacements, " ")
|
||||||
|
@ -21,6 +21,9 @@ func TestReplacePlaceholder(t *testing.T) {
|
|||||||
newItem("foo'bar \x1b[31mbaz\x1b[m"),
|
newItem("foo'bar \x1b[31mbaz\x1b[m"),
|
||||||
newItem("FOO'BAR \x1b[31mBAZ\x1b[m")}
|
newItem("FOO'BAR \x1b[31mBAZ\x1b[m")}
|
||||||
|
|
||||||
|
delim := "'"
|
||||||
|
var regex *regexp.Regexp
|
||||||
|
|
||||||
var result string
|
var result string
|
||||||
check := func(expected string) {
|
check := func(expected string) {
|
||||||
if result != expected {
|
if result != expected {
|
||||||
@ -72,6 +75,31 @@ func TestReplacePlaceholder(t *testing.T) {
|
|||||||
result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, true, "query", items2)
|
result = replacePlaceholder("echo {1}/{2}/{-1}/{-2}/{..}/{n.t}/\\{}/\\{1}/\\{q}/{3}", true, Delimiter{}, true, "query", items2)
|
||||||
check("echo 'foo'\\''bar' 'FOO'\\''BAR'/'baz' 'BAZ'/'baz' 'BAZ'/'foo'\\''bar' 'FOO'\\''BAR'/'foo'\\''bar baz' 'FOO'\\''BAR BAZ'/{n.t}/{}/{1}/{q}/'' ''")
|
check("echo 'foo'\\''bar' 'FOO'\\''BAR'/'baz' 'BAZ'/'baz' 'BAZ'/'foo'\\''bar' 'FOO'\\''BAR'/'foo'\\''bar baz' 'FOO'\\''BAR BAZ'/{n.t}/{}/{1}/{q}/'' ''")
|
||||||
|
|
||||||
|
// Whitespace preserving flag with "'" delimiter
|
||||||
|
result = replacePlaceholder("echo {s1}", true, Delimiter{str: &delim}, false, "query", items1)
|
||||||
|
check("echo ' foo'")
|
||||||
|
|
||||||
|
result = replacePlaceholder("echo {s2}", true, Delimiter{str: &delim}, false, "query", items1)
|
||||||
|
check("echo 'bar baz'")
|
||||||
|
|
||||||
|
result = replacePlaceholder("echo {s}", true, Delimiter{str: &delim}, false, "query", items1)
|
||||||
|
check("echo ' foo'\\''bar baz'")
|
||||||
|
|
||||||
|
result = replacePlaceholder("echo {s..}", true, Delimiter{str: &delim}, false, "query", items1)
|
||||||
|
check("echo ' foo'\\''bar baz'")
|
||||||
|
|
||||||
|
// Whitespace preserving flag with regex delimiter
|
||||||
|
regex = regexp.MustCompile("\\w+")
|
||||||
|
|
||||||
|
result = replacePlaceholder("echo {s1}", true, Delimiter{regex: regex}, false, "query", items1)
|
||||||
|
check("echo ' '")
|
||||||
|
|
||||||
|
result = replacePlaceholder("echo {s2}", true, Delimiter{regex: regex}, false, "query", items1)
|
||||||
|
check("echo ''\\'''")
|
||||||
|
|
||||||
|
result = replacePlaceholder("echo {s3}", true, Delimiter{regex: regex}, false, "query", items1)
|
||||||
|
check("echo ' '")
|
||||||
|
|
||||||
// No match
|
// No match
|
||||||
result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, false, "query", []*Item{nil, nil})
|
result = replacePlaceholder("echo {}/{+}", true, Delimiter{}, false, "query", []*Item{nil, nil})
|
||||||
check("echo /")
|
check("echo /")
|
||||||
@ -81,12 +109,11 @@ func TestReplacePlaceholder(t *testing.T) {
|
|||||||
check("echo /' foo'\\''bar baz'")
|
check("echo /' foo'\\''bar baz'")
|
||||||
|
|
||||||
// String delimiter
|
// String delimiter
|
||||||
delim := "'"
|
|
||||||
result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, false, "query", items1)
|
result = replacePlaceholder("echo {}/{1}/{2}", true, Delimiter{str: &delim}, false, "query", items1)
|
||||||
check("echo ' foo'\\''bar baz'/'foo'/'bar baz'")
|
check("echo ' foo'\\''bar baz'/'foo'/'bar baz'")
|
||||||
|
|
||||||
// Regex delimiter
|
// Regex delimiter
|
||||||
regex := regexp.MustCompile("[oa]+")
|
regex = regexp.MustCompile("[oa]+")
|
||||||
// foo'bar baz
|
// foo'bar baz
|
||||||
result = replacePlaceholder("echo {}/{1}/{3}/{2..3}", true, Delimiter{regex: regex}, false, "query", items1)
|
result = replacePlaceholder("echo {}/{1}/{3}/{2..3}", true, Delimiter{regex: regex}, false, "query", items1)
|
||||||
check("echo ' foo'\\''bar baz'/'f'/'r b'/''\\''bar b'")
|
check("echo ' foo'\\''bar baz'/'f'/'r b'/''\\''bar b'")
|
||||||
|
Loading…
Reference in New Issue
Block a user