Make --extended default

Close #400
pull/403/head
Junegunn Choi 9 years ago
parent 68541e66b7
commit 81a88693c1

@ -1,6 +1,15 @@
CHANGELOG CHANGELOG
========= =========
0.10.9
------
- Extended-search mode is now enabled by default
- `--extended-exact` is deprecated and instead we have `--exact` for
orthogonally controlling "exactness" of search
- Fixed not to display non-printable characters
- Added `double-click` for `--bind` option
0.10.8 0.10.8
------ ------

@ -110,7 +110,7 @@ vim $(fzf)
#### Extended-search mode #### Extended-search mode
With `-x` or `--extended` option, fzf will start in "extended-search mode". Since 0.10.9, fzf starts in "extended-search mode" by default.
In this mode, you can specify multiple patterns delimited by spaces, In this mode, you can specify multiple patterns delimited by spaces,
such as: `^music .mp3$ sbtrkt !rmx` such as: `^music .mp3$ sbtrkt !rmx`
@ -125,15 +125,15 @@ such as: `^music .mp3$ sbtrkt !rmx`
| `!'fire` | Items that do not include `fire` | inverse-exact-match | | `!'fire` | Items that do not include `fire` | inverse-exact-match |
If you don't prefer fuzzy matching and do not wish to "quote" every word, If you don't prefer fuzzy matching and do not wish to "quote" every word,
start fzf with `-e` or `--extended-exact` option. Note that in start fzf with `-e` or `--exact` option. Note that when `--exact` is set,
`--extended-exact` mode, `'`-prefix "unquotes" the term. `'`-prefix "unquotes" the term.
#### Environment variables #### Environment variables
- `FZF_DEFAULT_COMMAND` - `FZF_DEFAULT_COMMAND`
- Default command to use when input is tty - Default command to use when input is tty
- `FZF_DEFAULT_OPTS` - `FZF_DEFAULT_OPTS`
- Default options. e.g. `export FZF_DEFAULT_OPTS="--extended --cycle"` - Default options. e.g. `export FZF_DEFAULT_OPTS="--reverse --inline-info"`
Examples Examples
-------- --------

@ -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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Oct 2015" "fzf 0.10.8" "fzf - a command-line fuzzy finder" .TH fzf 1 "Nov 2015" "fzf 0.10.9" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@ -36,10 +36,11 @@ fzf is a general-purpose command-line fuzzy finder.
.SS Search mode .SS Search mode
.TP .TP
.B "-x, --extended" .B "-x, --extended"
Extended-search mode Extended-search mode. Since 0.10.9, this is enabled by default. You can disable
it with \fB+x\fR or \fB--no-extended\fR.
.TP .TP
.B "-e, --extended-exact" .B "-e, --exact"
Extended-search mode (exact match) Enable exact-match
.TP .TP
.B "-i" .B "-i"
Case-insensitive match (default: smart-case match) Case-insensitive match (default: smart-case match)
@ -370,9 +371,9 @@ of field index expressions.
.SH EXTENDED SEARCH MODE .SH EXTENDED SEARCH MODE
With \fB-x\fR or \fB--extended\fR option, fzf will start in "extended-search Unless specified otherwise, fzf will start in "extended-search mode". In this
mode". In this mode, you can specify multiple patterns delimited by spaces, mode, you can specify multiple patterns delimited by spaces, such as: \fB'wild
such as: \fB'wild ^music .mp3$ sbtrkt !rmx\fR ^music .mp3$ sbtrkt !rmx\fR
.SS Exact-match (quoted) .SS Exact-match (quoted)
A term that is prefixed by a single-quote character (\fB'\fR) is interpreted as A term that is prefixed by a single-quote character (\fB'\fR) is interpreted as
@ -388,11 +389,10 @@ with the given string. An anchored-match term is also an exact-match term.
If a term is prefixed by \fB!\fR, fzf will exclude the items that satisfy the If a term is prefixed by \fB!\fR, fzf will exclude the items that satisfy the
term from the result. term from the result.
.SS Extended-exact mode .SS Exact-match by default
If you don't prefer fuzzy matching and do not wish to "quote" (prefixing with If you don't prefer fuzzy matching and do not wish to "quote" (prefixing with
\fB'\fR) every word, start fzf with \fB-e\fR or \fB--extended-exact\fR option \fB'\fR) every word, start fzf with \fB-e\fR or \fB--exact\fR option. Note that
(instead of \fB-x\fR or \fB--extended\fR). Note that in \fB--extended-exact\fR when \fB--exact\fR is set, \fB'\fR-prefix "unquotes" the term.
mode, \fB'\fR-prefix "unquotes" the term.
.SH AUTHOR .SH AUTHOR
Junegunn Choi (\fIjunegunn.c@gmail.com\fR) Junegunn Choi (\fIjunegunn.c@gmail.com\fR)

@ -22,7 +22,7 @@ _fzf_opts_completion() {
prev="${COMP_WORDS[COMP_CWORD-1]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
opts=" opts="
-x --extended -x --extended
-e --extended-exact -e --exact
-i +i -i +i
-n --nth -n --nth
-d --delimiter -d --delimiter

@ -143,7 +143,7 @@ func Run(opts *Options) {
// Matcher // Matcher
patternBuilder := func(runes []rune) *Pattern { patternBuilder := func(runes []rune) *Pattern {
return BuildPattern( return BuildPattern(
opts.Mode, opts.Case, opts.Tiebreak != byEnd, opts.Fuzzy, opts.Extended, opts.Case, opts.Tiebreak != byEnd,
opts.Nth, opts.Delimiter, runes) opts.Nth, opts.Delimiter, runes)
} }
matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox) matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)

@ -16,7 +16,8 @@ const usage = `usage: fzf [options]
Search Search
-x, --extended Extended-search mode -x, --extended Extended-search mode
-e, --extended-exact Extended-search mode (exact match) (enabled by default; +x or --no-extended to disable)
-e, --exact Enable Exact-match
-i Case-insensitive match (default: smart-case match) -i Case-insensitive match (default: smart-case match)
+i Case-sensitive match +i Case-sensitive match
-n, --nth=N[,..] Comma-separated list of field index expressions -n, --nth=N[,..] Comma-separated list of field index expressions
@ -58,20 +59,10 @@ const usage = `usage: fzf [options]
Environment variables Environment variables
FZF_DEFAULT_COMMAND Default command to use when input is tty FZF_DEFAULT_COMMAND Default command to use when input is tty
FZF_DEFAULT_OPTS Defaults options. (e.g. '-x -m') FZF_DEFAULT_OPTS Defaults options. (e.g. '--reverse --inline-info')
` `
// Mode denotes the current search mode
type Mode int
// Search modes
const (
ModeFuzzy Mode = iota
ModeExtended
ModeExtendedExact
)
// Case denotes case-sensitivity of search // Case denotes case-sensitivity of search
type Case int type Case int
@ -98,7 +89,8 @@ func defaultMargin() [4]string {
// Options stores the values of command-line options // Options stores the values of command-line options
type Options struct { type Options struct {
Mode Mode Fuzzy bool
Extended bool
Case Case Case Case
Nth []Range Nth []Range
WithNth []Range WithNth []Range
@ -143,7 +135,8 @@ func defaultTheme() *curses.ColorTheme {
func defaultOptions() *Options { func defaultOptions() *Options {
return &Options{ return &Options{
Mode: ModeFuzzy, Fuzzy: true,
Extended: true,
Case: CaseSmart, Case: CaseSmart,
Nth: make([]Range, 0), Nth: make([]Range, 0),
WithNth: make([]Range, 0), WithNth: make([]Range, 0),
@ -684,11 +677,17 @@ func parseOptions(opts *Options, allArgs []string) {
case "-h", "--help": case "-h", "--help":
help(exitOk) help(exitOk)
case "-x", "--extended": case "-x", "--extended":
opts.Mode = ModeExtended opts.Extended = true
case "-e", "--extended-exact": case "-e", "--exact":
opts.Mode = ModeExtendedExact opts.Fuzzy = false
case "+x", "--no-extended", "+e", "--no-extended-exact": case "--extended-exact":
opts.Mode = ModeFuzzy // Note that we now don't have --no-extended-exact
opts.Fuzzy = false
opts.Extended = true
case "+x", "--no-extended":
opts.Extended = false
case "+e", "--no-exact":
opts.Fuzzy = true
case "-q", "--query": case "-q", "--query":
opts.Query = nextString(allArgs, &i, "query string required") opts.Query = nextString(allArgs, &i, "query string required")
case "-f", "--filter": case "-f", "--filter":
@ -873,7 +872,7 @@ func parseOptions(opts *Options, allArgs []string) {
// If we're not using extended search mode, --nth option becomes irrelevant // If we're not using extended search mode, --nth option becomes irrelevant
// if it contains the whole range // if it contains the whole range
if opts.Mode == ModeFuzzy || len(opts.Nth) == 1 { if !opts.Extended || len(opts.Nth) == 1 {
for _, r := range opts.Nth { for _, r := range opts.Nth {
if r.begin == rangeEllipsis && r.end == rangeEllipsis { if r.begin == rangeEllipsis && r.end == rangeEllipsis {
opts.Nth = make([]Range, 0) opts.Nth = make([]Range, 0)

@ -100,7 +100,7 @@ func TestIrrelevantNth(t *testing.T) {
t.Errorf("nth should be empty: %s", opts.Nth) t.Errorf("nth should be empty: %s", opts.Nth)
} }
} }
for _, words := range [][]string{[]string{"--nth", "..,3"}, []string{"--nth", "3,1.."}, []string{"--nth", "..-1,1"}} { for _, words := range [][]string{[]string{"--nth", "..,3", "+x"}, []string{"--nth", "3,1..", "+x"}, []string{"--nth", "..-1,1", "+x"}} {
{ {
opts := defaultOptions() opts := defaultOptions()
parseOptions(opts, words) parseOptions(opts, words)

@ -38,7 +38,8 @@ type term struct {
// Pattern represents search pattern // Pattern represents search pattern
type Pattern struct { type Pattern struct {
mode Mode fuzzy bool
extended bool
caseSensitive bool caseSensitive bool
forward bool forward bool
text []rune text []rune
@ -63,7 +64,7 @@ func init() {
func clearPatternCache() { func clearPatternCache() {
// We can uniquely identify the pattern for a given string since // We can uniquely identify the pattern for a given string since
// mode and caseMode do not change while the program is running // search mode and caseMode do not change while the program is running
_patternCache = make(map[string]*Pattern) _patternCache = make(map[string]*Pattern)
} }
@ -72,14 +73,13 @@ func clearChunkCache() {
} }
// BuildPattern builds Pattern object from the given arguments // BuildPattern builds Pattern object from the given arguments
func BuildPattern(mode Mode, caseMode Case, forward bool, func BuildPattern(fuzzy bool, extended bool, caseMode Case, forward bool,
nth []Range, delimiter Delimiter, runes []rune) *Pattern { nth []Range, delimiter Delimiter, runes []rune) *Pattern {
var asString string var asString string
switch mode { if extended {
case ModeExtended, ModeExtendedExact:
asString = strings.Trim(string(runes), " ") asString = strings.Trim(string(runes), " ")
default: } else {
asString = string(runes) asString = string(runes)
} }
@ -91,15 +91,14 @@ func BuildPattern(mode Mode, caseMode Case, forward bool,
caseSensitive, hasInvTerm := true, false caseSensitive, hasInvTerm := true, false
terms := []term{} terms := []term{}
switch mode { if extended {
case ModeExtended, ModeExtendedExact: terms = parseTerms(fuzzy, caseMode, asString)
terms = parseTerms(mode, caseMode, asString)
for _, term := range terms { for _, term := range terms {
if term.inv { if term.inv {
hasInvTerm = true hasInvTerm = true
} }
} }
default: } else {
lowerString := strings.ToLower(asString) lowerString := strings.ToLower(asString)
caseSensitive = caseMode == CaseRespect || caseSensitive = caseMode == CaseRespect ||
caseMode == CaseSmart && lowerString != asString caseMode == CaseSmart && lowerString != asString
@ -109,7 +108,8 @@ func BuildPattern(mode Mode, caseMode Case, forward bool,
} }
ptr := &Pattern{ ptr := &Pattern{
mode: mode, fuzzy: fuzzy,
extended: extended,
caseSensitive: caseSensitive, caseSensitive: caseSensitive,
forward: forward, forward: forward,
text: []rune(asString), text: []rune(asString),
@ -129,7 +129,7 @@ func BuildPattern(mode Mode, caseMode Case, forward bool,
return ptr return ptr
} }
func parseTerms(mode Mode, caseMode Case, str string) []term { func parseTerms(fuzzy bool, caseMode Case, str string) []term {
tokens := _splitRegex.Split(str, -1) tokens := _splitRegex.Split(str, -1)
terms := []term{} terms := []term{}
for _, token := range tokens { for _, token := range tokens {
@ -141,7 +141,7 @@ func parseTerms(mode Mode, caseMode Case, str string) []term {
text = lowerText text = lowerText
} }
origText := []rune(text) origText := []rune(text)
if mode == ModeExtendedExact { if !fuzzy {
typ = termExact typ = termExact
} }
@ -151,10 +151,11 @@ func parseTerms(mode Mode, caseMode Case, str string) []term {
} }
if strings.HasPrefix(text, "'") { if strings.HasPrefix(text, "'") {
if mode == ModeExtended { // Flip exactness
if fuzzy {
typ = termExact typ = termExact
text = text[1:] text = text[1:]
} else if mode == ModeExtendedExact { } else {
typ = termFuzzy typ = termFuzzy
text = text[1:] text = text[1:]
} }
@ -185,7 +186,7 @@ func parseTerms(mode Mode, caseMode Case, str string) []term {
// IsEmpty returns true if the pattern is effectively empty // IsEmpty returns true if the pattern is effectively empty
func (p *Pattern) IsEmpty() bool { func (p *Pattern) IsEmpty() bool {
if p.mode == ModeFuzzy { if !p.extended {
return len(p.text) == 0 return len(p.text) == 0
} }
return len(p.terms) == 0 return len(p.terms) == 0
@ -198,7 +199,7 @@ func (p *Pattern) AsString() string {
// CacheKey is used to build string to be used as the key of result cache // CacheKey is used to build string to be used as the key of result cache
func (p *Pattern) CacheKey() string { func (p *Pattern) CacheKey() string {
if p.mode == ModeFuzzy { if !p.extended {
return p.AsString() return p.AsString()
} }
cacheableTerms := []string{} cacheableTerms := []string{}
@ -250,9 +251,9 @@ Loop:
func (p *Pattern) matchChunk(chunk *Chunk) []*Item { func (p *Pattern) matchChunk(chunk *Chunk) []*Item {
matches := []*Item{} matches := []*Item{}
if p.mode == ModeFuzzy { if !p.extended {
for _, item := range *chunk { for _, item := range *chunk {
if sidx, eidx, tlen := p.fuzzyMatch(item); sidx >= 0 { if sidx, eidx, tlen := p.basicMatch(item); sidx >= 0 {
matches = append(matches, matches = append(matches,
dupItem(item, []Offset{Offset{int32(sidx), int32(eidx), int32(tlen)}})) dupItem(item, []Offset{Offset{int32(sidx), int32(eidx), int32(tlen)}}))
} }
@ -269,8 +270,8 @@ func (p *Pattern) matchChunk(chunk *Chunk) []*Item {
// MatchItem returns true if the Item is a match // MatchItem returns true if the Item is a match
func (p *Pattern) MatchItem(item *Item) bool { func (p *Pattern) MatchItem(item *Item) bool {
if p.mode == ModeFuzzy { if !p.extended {
sidx, _, _ := p.fuzzyMatch(item) sidx, _, _ := p.basicMatch(item)
return sidx >= 0 return sidx >= 0
} }
offsets := p.extendedMatch(item) offsets := p.extendedMatch(item)
@ -289,9 +290,12 @@ func dupItem(item *Item, offsets []Offset) *Item {
rank: Rank{0, 0, item.index}} rank: Rank{0, 0, item.index}}
} }
func (p *Pattern) fuzzyMatch(item *Item) (int, int, int) { func (p *Pattern) basicMatch(item *Item) (int, int, int) {
input := p.prepareInput(item) input := p.prepareInput(item)
return p.iter(algo.FuzzyMatch, input, p.caseSensitive, p.forward, p.text) if p.fuzzy {
return p.iter(algo.FuzzyMatch, input, p.caseSensitive, p.forward, p.text)
}
return p.iter(algo.ExactMatchNaive, input, p.caseSensitive, p.forward, p.text)
} }
func (p *Pattern) extendedMatch(item *Item) []Offset { func (p *Pattern) extendedMatch(item *Item) []Offset {

@ -8,7 +8,7 @@ import (
) )
func TestParseTermsExtended(t *testing.T) { func TestParseTermsExtended(t *testing.T) {
terms := parseTerms(ModeExtended, CaseSmart, terms := parseTerms(true, CaseSmart,
"aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$ ^iii$") "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$ ^iii$")
if len(terms) != 9 || if len(terms) != 9 ||
terms[0].typ != termFuzzy || terms[0].inv || terms[0].typ != termFuzzy || terms[0].inv ||
@ -33,7 +33,7 @@ func TestParseTermsExtended(t *testing.T) {
} }
func TestParseTermsExtendedExact(t *testing.T) { func TestParseTermsExtendedExact(t *testing.T) {
terms := parseTerms(ModeExtendedExact, CaseSmart, terms := parseTerms(false, CaseSmart,
"aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$") "aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$")
if len(terms) != 8 || if len(terms) != 8 ||
terms[0].typ != termExact || terms[0].inv || len(terms[0].text) != 3 || terms[0].typ != termExact || terms[0].inv || len(terms[0].text) != 3 ||
@ -49,7 +49,7 @@ func TestParseTermsExtendedExact(t *testing.T) {
} }
func TestParseTermsEmpty(t *testing.T) { func TestParseTermsEmpty(t *testing.T) {
terms := parseTerms(ModeExtended, CaseSmart, "' $ ^ !' !^ !$") terms := parseTerms(true, CaseSmart, "' $ ^ !' !^ !$")
if len(terms) != 0 { if len(terms) != 0 {
t.Errorf("%s", terms) t.Errorf("%s", terms)
} }
@ -58,7 +58,7 @@ func TestParseTermsEmpty(t *testing.T) {
func TestExact(t *testing.T) { func TestExact(t *testing.T) {
defer clearPatternCache() defer clearPatternCache()
clearPatternCache() clearPatternCache()
pattern := BuildPattern(ModeExtended, CaseSmart, true, pattern := BuildPattern(true, true, CaseSmart, true,
[]Range{}, Delimiter{}, []rune("'abc")) []Range{}, Delimiter{}, []rune("'abc"))
sidx, eidx := algo.ExactMatchNaive( sidx, eidx := algo.ExactMatchNaive(
pattern.caseSensitive, pattern.forward, []rune("aabbcc abc"), pattern.terms[0].text) pattern.caseSensitive, pattern.forward, []rune("aabbcc abc"), pattern.terms[0].text)
@ -70,7 +70,7 @@ func TestExact(t *testing.T) {
func TestEqual(t *testing.T) { func TestEqual(t *testing.T) {
defer clearPatternCache() defer clearPatternCache()
clearPatternCache() clearPatternCache()
pattern := BuildPattern(ModeExtended, CaseSmart, true, []Range{}, Delimiter{}, []rune("^AbC$")) pattern := BuildPattern(true, true, CaseSmart, true, []Range{}, Delimiter{}, []rune("^AbC$"))
match := func(str string, sidxExpected int, eidxExpected int) { match := func(str string, sidxExpected int, eidxExpected int) {
sidx, eidx := algo.EqualMatch( sidx, eidx := algo.EqualMatch(
@ -86,17 +86,17 @@ func TestEqual(t *testing.T) {
func TestCaseSensitivity(t *testing.T) { func TestCaseSensitivity(t *testing.T) {
defer clearPatternCache() defer clearPatternCache()
clearPatternCache() clearPatternCache()
pat1 := BuildPattern(ModeFuzzy, CaseSmart, true, []Range{}, Delimiter{}, []rune("abc")) pat1 := BuildPattern(true, false, CaseSmart, true, []Range{}, Delimiter{}, []rune("abc"))
clearPatternCache() clearPatternCache()
pat2 := BuildPattern(ModeFuzzy, CaseSmart, true, []Range{}, Delimiter{}, []rune("Abc")) pat2 := BuildPattern(true, false, CaseSmart, true, []Range{}, Delimiter{}, []rune("Abc"))
clearPatternCache() clearPatternCache()
pat3 := BuildPattern(ModeFuzzy, CaseIgnore, true, []Range{}, Delimiter{}, []rune("abc")) pat3 := BuildPattern(true, false, CaseIgnore, true, []Range{}, Delimiter{}, []rune("abc"))
clearPatternCache() clearPatternCache()
pat4 := BuildPattern(ModeFuzzy, CaseIgnore, true, []Range{}, Delimiter{}, []rune("Abc")) pat4 := BuildPattern(true, false, CaseIgnore, true, []Range{}, Delimiter{}, []rune("Abc"))
clearPatternCache() clearPatternCache()
pat5 := BuildPattern(ModeFuzzy, CaseRespect, true, []Range{}, Delimiter{}, []rune("abc")) pat5 := BuildPattern(true, false, CaseRespect, true, []Range{}, Delimiter{}, []rune("abc"))
clearPatternCache() clearPatternCache()
pat6 := BuildPattern(ModeFuzzy, CaseRespect, true, []Range{}, Delimiter{}, []rune("Abc")) pat6 := BuildPattern(true, false, CaseRespect, true, []Range{}, Delimiter{}, []rune("Abc"))
if string(pat1.text) != "abc" || pat1.caseSensitive != false || if string(pat1.text) != "abc" || pat1.caseSensitive != false ||
string(pat2.text) != "Abc" || pat2.caseSensitive != true || string(pat2.text) != "Abc" || pat2.caseSensitive != true ||
@ -109,19 +109,19 @@ func TestCaseSensitivity(t *testing.T) {
} }
func TestOrigTextAndTransformed(t *testing.T) { func TestOrigTextAndTransformed(t *testing.T) {
pattern := BuildPattern(ModeExtended, CaseSmart, true, []Range{}, Delimiter{}, []rune("jg")) pattern := BuildPattern(true, true, CaseSmart, true, []Range{}, Delimiter{}, []rune("jg"))
tokens := Tokenize([]rune("junegunn"), Delimiter{}) tokens := Tokenize([]rune("junegunn"), Delimiter{})
trans := Transform(tokens, []Range{Range{1, 1}}) trans := Transform(tokens, []Range{Range{1, 1}})
origRunes := []rune("junegunn.choi") origRunes := []rune("junegunn.choi")
for _, mode := range []Mode{ModeFuzzy, ModeExtended} { for _, extended := range []bool{false, true} {
chunk := Chunk{ chunk := Chunk{
&Item{ &Item{
text: []rune("junegunn"), text: []rune("junegunn"),
origText: &origRunes, origText: &origRunes,
transformed: trans}, transformed: trans},
} }
pattern.mode = mode pattern.extended = extended
matches := pattern.matchChunk(&chunk) matches := pattern.matchChunk(&chunk)
if string(matches[0].text) != "junegunn" || string(*matches[0].origText) != "junegunn.choi" || if string(matches[0].text) != "junegunn" || string(*matches[0].origText) != "junegunn.choi" ||
matches[0].offsets[0][0] != 0 || matches[0].offsets[0][1] != 5 || matches[0].offsets[0][0] != 0 || matches[0].offsets[0][1] != 5 ||

@ -8,7 +8,7 @@ DEFAULT_TIMEOUT = 20
base = File.expand_path('../../', __FILE__) base = File.expand_path('../../', __FILE__)
Dir.chdir base Dir.chdir base
FZF = "#{base}/bin/fzf" FZF = "FZF_DEFAULT_OPTS= FZF_DEFAULT_COMMAND= #{base}/bin/fzf"
class NilClass class NilClass
def include? str def include? str
@ -213,7 +213,7 @@ class TestGoFZF < TestBase
end end
def test_fzf_default_command def test_fzf_default_command
tmux.send_keys "FZF_DEFAULT_COMMAND='echo hello' #{fzf}", :Enter tmux.send_keys fzf.sub('FZF_DEFAULT_COMMAND=', "FZF_DEFAULT_COMMAND='echo hello'"), :Enter
tmux.until { |lines| lines.last =~ /^>/ } tmux.until { |lines| lines.last =~ /^>/ }
tmux.send_keys :Enter tmux.send_keys :Enter
@ -904,6 +904,17 @@ class TestGoFZF < TestBase
end end
end end
def test_default_extended
assert_equal '100', `seq 100 | #{FZF} -f "1 00$"`.chomp
assert_equal '', `seq 100 | #{FZF} -f "1 00$" +x`.chomp
end
def test_exact
assert_equal 4, `seq 123 | #{FZF} -f 13`.lines.length
assert_equal 2, `seq 123 | #{FZF} -f 13 -e`.lines.length
assert_equal 4, `seq 123 | #{FZF} -f 13 +e`.lines.length
end
private private
def writelines path, lines def writelines path, lines
File.unlink path while File.exists? path File.unlink path while File.exists? path

Loading…
Cancel
Save