Implement --toggle-sort option (#173)
This commit is contained in:
parent
84a7499ae3
commit
50292adacb
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,6 +1,20 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.9.7
|
||||||
|
-----
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- Added `--toggle-sort` option (#173)
|
||||||
|
- `--toggle-sort=ctrl-r` is applied to `CTRL-R` shell extension
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Fixed to print empty line if `--expect` is set and fzf is completed by
|
||||||
|
`--select-1` or `--exit-0` (#172)
|
||||||
|
- Fixed to allow comma character as an argument to `--expect` option
|
||||||
|
|
||||||
0.9.6
|
0.9.6
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -152,6 +152,7 @@ fish.
|
|||||||
|
|
||||||
- `CTRL-T` - Paste the selected file path(s) into the command line
|
- `CTRL-T` - Paste the selected file path(s) into the command line
|
||||||
- `CTRL-R` - Paste the selected command from history into the command line
|
- `CTRL-R` - Paste the selected command from history into the command line
|
||||||
|
- Sort is disabled by default. Press `CTRL-R` again to toggle sort
|
||||||
- `ALT-C` - cd into the selected directory
|
- `ALT-C` - cd into the selected directory
|
||||||
|
|
||||||
If you're on a tmux session, `CTRL-T` will launch fzf in a new split-window. You
|
If you're on a tmux session, `CTRL-T` will launch fzf in a new split-window. You
|
||||||
|
4
fzf
4
fzf
@ -206,7 +206,9 @@ class FZF
|
|||||||
@expect = true
|
@expect = true
|
||||||
when /^--expect=(.*)$/
|
when /^--expect=(.*)$/
|
||||||
@expect = true
|
@expect = true
|
||||||
when '--tac', '--sync'
|
when '--toggle-sort'
|
||||||
|
argv.shift
|
||||||
|
when '--tac', '--sync', '--toggle-sort', /^--toggle-sort=(.*)$/
|
||||||
# XXX
|
# XXX
|
||||||
else
|
else
|
||||||
usage 1, "illegal option: #{o}"
|
usage 1, "illegal option: #{o}"
|
||||||
|
2
install
2
install
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
version=0.9.6
|
version=0.9.7
|
||||||
|
|
||||||
cd $(dirname $BASH_SOURCE)
|
cd $(dirname $BASH_SOURCE)
|
||||||
fzf_base=$(pwd)
|
fzf_base=$(pwd)
|
||||||
|
@ -111,7 +111,7 @@ Print query as the first line
|
|||||||
.TP
|
.TP
|
||||||
.BI "--expect=" "KEY[,..]"
|
.BI "--expect=" "KEY[,..]"
|
||||||
Comma-separated list of keys (\fIctrl-[a-z]\fR, \fIalt-[a-z]\fR, \fIf[1-4]\fR,
|
Comma-separated list of keys (\fIctrl-[a-z]\fR, \fIalt-[a-z]\fR, \fIf[1-4]\fR,
|
||||||
or a single character) that can be used to complete fzf in addition to the
|
or any single character) that can be used to complete fzf in addition to the
|
||||||
default enter key. When this option is set, fzf will print the name of the key
|
default enter key. When this option is set, fzf will print the name of the key
|
||||||
pressed as the first line of its output (or as the second line if
|
pressed as the first line of its output (or as the second line if
|
||||||
\fB--print-query\fR is also used). The line will be empty if fzf is completed
|
\fB--print-query\fR is also used). The line will be empty if fzf is completed
|
||||||
@ -120,6 +120,10 @@ with the default enter key.
|
|||||||
e.g. \fBfzf --expect=ctrl-v,ctrl-t,alt-s,f1,f2,~,@\fR
|
e.g. \fBfzf --expect=ctrl-v,ctrl-t,alt-s,f1,f2,~,@\fR
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
|
.BI "--toggle-sort=" "KEY"
|
||||||
|
Key to toggle sort (\fIctrl-[a-z]\fR, \fIalt-[a-z]\fR, \fIf[1-4]\fR,
|
||||||
|
or any single character)
|
||||||
|
.TP
|
||||||
.B "--sync"
|
.B "--sync"
|
||||||
Synchronous search for multi-staged filtering. If specified, fzf will launch
|
Synchronous search for multi-staged filtering. If specified, fzf will launch
|
||||||
ncurses finder only after the input stream is complete.
|
ncurses finder only after the input stream is complete.
|
||||||
|
@ -44,7 +44,7 @@ if [ -z "$(set -o | \grep '^vi.*on')" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s --tac +m -n2..,.. | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
|
bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s --tac +m -n2..,.. --toggle-sort=ctrl-r | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
|
||||||
|
|
||||||
# ALT-C - cd into the selected directory
|
# ALT-C - cd into the selected directory
|
||||||
bind '"\ec": " \C-e\C-u$(__fcd)\e\C-e\er\C-m"'
|
bind '"\ec": " \C-e\C-u$(__fcd)\e\C-e\er\C-m"'
|
||||||
|
@ -44,7 +44,7 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
|
|
||||||
function __fzf_ctrl_r
|
function __fzf_ctrl_r
|
||||||
history | fzf +s +m > $TMPDIR/fzf.result
|
history | fzf +s +m --toggle-sort=ctrl-r > $TMPDIR/fzf.result
|
||||||
and commandline (cat $TMPDIR/fzf.result)
|
and commandline (cat $TMPDIR/fzf.result)
|
||||||
commandline -f repaint
|
commandline -f repaint
|
||||||
rm -f $TMPDIR/fzf.result
|
rm -f $TMPDIR/fzf.result
|
||||||
|
@ -45,7 +45,7 @@ bindkey '\ec' fzf-cd-widget
|
|||||||
# CTRL-R - Paste the selected command from history into the command line
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
fzf-history-widget() {
|
fzf-history-widget() {
|
||||||
local selected
|
local selected
|
||||||
if selected=$(fc -l 1 | fzf +s --tac +m -n2..,.. -q "$LBUFFER"); then
|
if selected=$(fc -l 1 | fzf +s --tac +m -n2..,.. --toggle-sort=ctrl-r -q "$LBUFFER"); then
|
||||||
num=$(echo "$selected" | head -1 | awk '{print $1}' | sed 's/[^0-9]//g')
|
num=$(echo "$selected" | head -1 | awk '{print $1}' | sed 's/[^0-9]//g')
|
||||||
LBUFFER=!$num
|
LBUFFER=!$num
|
||||||
zle expand-history
|
zle expand-history
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Current version
|
// Current version
|
||||||
const Version = "0.9.6"
|
const Version = "0.9.7"
|
||||||
|
|
||||||
// fzf events
|
// fzf events
|
||||||
const (
|
const (
|
||||||
|
14
src/core.go
14
src/core.go
@ -44,7 +44,7 @@ func initProcs() {
|
|||||||
/*
|
/*
|
||||||
Reader -> EvtReadFin
|
Reader -> EvtReadFin
|
||||||
Reader -> EvtReadNew -> Matcher (restart)
|
Reader -> EvtReadNew -> Matcher (restart)
|
||||||
Terminal -> EvtSearchNew -> Matcher (restart)
|
Terminal -> EvtSearchNew:bool -> Matcher (restart)
|
||||||
Matcher -> EvtSearchProgress -> Terminal (update info)
|
Matcher -> EvtSearchProgress -> Terminal (update info)
|
||||||
Matcher -> EvtSearchFin -> Terminal (update list)
|
Matcher -> EvtSearchFin -> Terminal (update list)
|
||||||
*/
|
*/
|
||||||
@ -54,6 +54,7 @@ func Run(options *Options) {
|
|||||||
initProcs()
|
initProcs()
|
||||||
|
|
||||||
opts := ParseOptions()
|
opts := ParseOptions()
|
||||||
|
sort := opts.Sort > 0
|
||||||
|
|
||||||
if opts.Version {
|
if opts.Version {
|
||||||
fmt.Println(Version)
|
fmt.Println(Version)
|
||||||
@ -112,7 +113,7 @@ func Run(options *Options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reader
|
// Reader
|
||||||
streamingFilter := opts.Filter != nil && opts.Sort == 0 && !opts.Tac && !opts.Sync
|
streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
|
||||||
if !streamingFilter {
|
if !streamingFilter {
|
||||||
reader := Reader{func(str string) { chunkList.Push(str) }, eventBox}
|
reader := Reader{func(str string) { chunkList.Push(str) }, eventBox}
|
||||||
go reader.ReadSource()
|
go reader.ReadSource()
|
||||||
@ -123,7 +124,7 @@ func Run(options *Options) {
|
|||||||
return BuildPattern(
|
return BuildPattern(
|
||||||
opts.Mode, opts.Case, opts.Nth, opts.Delimiter, runes)
|
opts.Mode, opts.Case, opts.Nth, opts.Delimiter, runes)
|
||||||
}
|
}
|
||||||
matcher := NewMatcher(patternBuilder, opts.Sort > 0, opts.Tac, eventBox)
|
matcher := NewMatcher(patternBuilder, sort, opts.Tac, eventBox)
|
||||||
|
|
||||||
// Filtering mode
|
// Filtering mode
|
||||||
if opts.Filter != nil {
|
if opts.Filter != nil {
|
||||||
@ -190,11 +191,14 @@ func Run(options *Options) {
|
|||||||
reading = reading && evt == EvtReadNew
|
reading = reading && evt == EvtReadNew
|
||||||
snapshot, count := chunkList.Snapshot()
|
snapshot, count := chunkList.Snapshot()
|
||||||
terminal.UpdateCount(count, !reading)
|
terminal.UpdateCount(count, !reading)
|
||||||
matcher.Reset(snapshot, terminal.Input(), false, !reading)
|
matcher.Reset(snapshot, terminal.Input(), false, !reading, sort)
|
||||||
|
|
||||||
case EvtSearchNew:
|
case EvtSearchNew:
|
||||||
|
if value.(bool) {
|
||||||
|
sort = !sort
|
||||||
|
}
|
||||||
snapshot, _ := chunkList.Snapshot()
|
snapshot, _ := chunkList.Snapshot()
|
||||||
matcher.Reset(snapshot, terminal.Input(), true, !reading)
|
matcher.Reset(snapshot, terminal.Input(), true, !reading, sort)
|
||||||
delay = false
|
delay = false
|
||||||
|
|
||||||
case EvtSearchProgress:
|
case EvtSearchProgress:
|
||||||
|
@ -15,6 +15,7 @@ type MatchRequest struct {
|
|||||||
chunks []*Chunk
|
chunks []*Chunk
|
||||||
pattern *Pattern
|
pattern *Pattern
|
||||||
final bool
|
final bool
|
||||||
|
sort bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher is responsible for performing search
|
// Matcher is responsible for performing search
|
||||||
@ -69,6 +70,12 @@ func (m *Matcher) Loop() {
|
|||||||
events.Clear()
|
events.Clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if request.sort != m.sort {
|
||||||
|
m.sort = request.sort
|
||||||
|
m.mergerCache = make(map[string]*Merger)
|
||||||
|
clearChunkCache()
|
||||||
|
}
|
||||||
|
|
||||||
// Restart search
|
// Restart search
|
||||||
patternString := request.pattern.AsString()
|
patternString := request.pattern.AsString()
|
||||||
var merger *Merger
|
var merger *Merger
|
||||||
@ -203,7 +210,7 @@ func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset is called to interrupt/signal the ongoing search
|
// Reset is called to interrupt/signal the ongoing search
|
||||||
func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool) {
|
func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final bool, sort bool) {
|
||||||
pattern := m.patternBuilder(patternRunes)
|
pattern := m.patternBuilder(patternRunes)
|
||||||
|
|
||||||
var event util.EventType
|
var event util.EventType
|
||||||
@ -212,5 +219,5 @@ func (m *Matcher) Reset(chunks []*Chunk, patternRunes []rune, cancel bool, final
|
|||||||
} else {
|
} else {
|
||||||
event = reqRetry
|
event = reqRetry
|
||||||
}
|
}
|
||||||
m.reqBox.Set(event, MatchRequest{chunks, pattern, final})
|
m.reqBox.Set(event, MatchRequest{chunks, pattern, final, sort})
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ const usage = `usage: fzf [options]
|
|||||||
-f, --filter=STR Filter mode. Do not start interactive finder.
|
-f, --filter=STR Filter mode. Do not start interactive finder.
|
||||||
--print-query Print query as the first line
|
--print-query Print query as the first line
|
||||||
--expect=KEYS Comma-separated list of keys to complete fzf
|
--expect=KEYS Comma-separated list of keys to complete fzf
|
||||||
|
--toggle-sort=KEY Key to toggle sort
|
||||||
--sync Synchronous search for multi-staged filtering
|
--sync Synchronous search for multi-staged filtering
|
||||||
(e.g. 'fzf --multi | fzf --sync')
|
(e.g. 'fzf --multi | fzf --sync')
|
||||||
|
|
||||||
@ -97,6 +98,7 @@ type Options struct {
|
|||||||
Select1 bool
|
Select1 bool
|
||||||
Exit0 bool
|
Exit0 bool
|
||||||
Filter *string
|
Filter *string
|
||||||
|
ToggleSort int
|
||||||
Expect []int
|
Expect []int
|
||||||
PrintQuery bool
|
PrintQuery bool
|
||||||
Sync bool
|
Sync bool
|
||||||
@ -124,6 +126,7 @@ func defaultOptions() *Options {
|
|||||||
Select1: false,
|
Select1: false,
|
||||||
Exit0: false,
|
Exit0: false,
|
||||||
Filter: nil,
|
Filter: nil,
|
||||||
|
ToggleSort: 0,
|
||||||
Expect: []int{},
|
Expect: []int{},
|
||||||
PrintQuery: false,
|
PrintQuery: false,
|
||||||
Sync: false,
|
Sync: false,
|
||||||
@ -201,9 +204,21 @@ func isAlphabet(char uint8) bool {
|
|||||||
return char >= 'a' && char <= 'z'
|
return char >= 'a' && char <= 'z'
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeyChords(str string) []int {
|
func parseKeyChords(str string, message string) []int {
|
||||||
|
if len(str) == 0 {
|
||||||
|
errorExit(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := strings.Split(str, ",")
|
||||||
|
if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Index(str, ",,,") >= 0 {
|
||||||
|
tokens = append(tokens, ",")
|
||||||
|
}
|
||||||
|
|
||||||
var chords []int
|
var chords []int
|
||||||
for _, key := range strings.Split(str, ",") {
|
for _, key := range tokens {
|
||||||
|
if len(key) == 0 {
|
||||||
|
continue // ignore
|
||||||
|
}
|
||||||
lkey := strings.ToLower(key)
|
lkey := strings.ToLower(key)
|
||||||
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
||||||
chords = append(chords, curses.CtrlA+int(lkey[5])-'a')
|
chords = append(chords, curses.CtrlA+int(lkey[5])-'a')
|
||||||
@ -220,6 +235,14 @@ func parseKeyChords(str string) []int {
|
|||||||
return chords
|
return chords
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkToggleSort(str string) int {
|
||||||
|
keys := parseKeyChords(str, "key name required")
|
||||||
|
if len(keys) != 1 {
|
||||||
|
errorExit("multiple keys specified")
|
||||||
|
}
|
||||||
|
return keys[0]
|
||||||
|
}
|
||||||
|
|
||||||
func parseOptions(opts *Options, allArgs []string) {
|
func parseOptions(opts *Options, allArgs []string) {
|
||||||
for i := 0; i < len(allArgs); i++ {
|
for i := 0; i < len(allArgs); i++ {
|
||||||
arg := allArgs[i]
|
arg := allArgs[i]
|
||||||
@ -238,7 +261,9 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
filter := nextString(allArgs, &i, "query string required")
|
filter := nextString(allArgs, &i, "query string required")
|
||||||
opts.Filter = &filter
|
opts.Filter = &filter
|
||||||
case "--expect":
|
case "--expect":
|
||||||
opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"))
|
opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required")
|
||||||
|
case "--toggle-sort":
|
||||||
|
opts.ToggleSort = checkToggleSort(nextString(allArgs, &i, "key name required"))
|
||||||
case "-d", "--delimiter":
|
case "-d", "--delimiter":
|
||||||
opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required"))
|
opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required"))
|
||||||
case "-n", "--nth":
|
case "-n", "--nth":
|
||||||
@ -316,8 +341,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.WithNth = splitNth(value)
|
opts.WithNth = splitNth(value)
|
||||||
} else if match, _ := optString(arg, "-s|--sort="); match {
|
} else if match, _ := optString(arg, "-s|--sort="); match {
|
||||||
opts.Sort = 1 // Don't care
|
opts.Sort = 1 // Don't care
|
||||||
|
} else if match, value := optString(arg, "--toggle-sort="); match {
|
||||||
|
opts.ToggleSort = checkToggleSort(value)
|
||||||
} else if match, value := optString(arg, "--expect="); match {
|
} else if match, value := optString(arg, "--expect="); match {
|
||||||
opts.Expect = parseKeyChords(value)
|
opts.Expect = parseKeyChords(value, "key names required")
|
||||||
} else {
|
} else {
|
||||||
errorExit("unknown option: " + arg)
|
errorExit("unknown option: " + arg)
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,8 @@ func TestIrrelevantNth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpectKeys(t *testing.T) {
|
func TestParseKeys(t *testing.T) {
|
||||||
keys := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g")
|
keys := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g", "")
|
||||||
check := func(key int, expected int) {
|
check := func(key int, expected int) {
|
||||||
if key != expected {
|
if key != expected {
|
||||||
t.Errorf("%d != %d", key, expected)
|
t.Errorf("%d != %d", key, expected)
|
||||||
@ -88,3 +88,44 @@ func TestExpectKeys(t *testing.T) {
|
|||||||
check(keys[7], curses.AltZ+'J')
|
check(keys[7], curses.AltZ+'J')
|
||||||
check(keys[8], curses.AltZ+'g')
|
check(keys[8], curses.AltZ+'g')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseKeysWithComma(t *testing.T) {
|
||||||
|
check := func(key int, expected int) {
|
||||||
|
if key != expected {
|
||||||
|
t.Errorf("%d != %d", key, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := parseKeyChords(",", "")
|
||||||
|
check(len(keys), 1)
|
||||||
|
check(keys[0], curses.AltZ+',')
|
||||||
|
|
||||||
|
keys = parseKeyChords(",,a,b", "")
|
||||||
|
check(len(keys), 3)
|
||||||
|
check(keys[0], curses.AltZ+'a')
|
||||||
|
check(keys[1], curses.AltZ+'b')
|
||||||
|
check(keys[2], curses.AltZ+',')
|
||||||
|
|
||||||
|
keys = parseKeyChords("a,b,,", "")
|
||||||
|
check(len(keys), 3)
|
||||||
|
check(keys[0], curses.AltZ+'a')
|
||||||
|
check(keys[1], curses.AltZ+'b')
|
||||||
|
check(keys[2], curses.AltZ+',')
|
||||||
|
|
||||||
|
keys = parseKeyChords("a,,,b", "")
|
||||||
|
check(len(keys), 3)
|
||||||
|
check(keys[0], curses.AltZ+'a')
|
||||||
|
check(keys[1], curses.AltZ+'b')
|
||||||
|
check(keys[2], curses.AltZ+',')
|
||||||
|
|
||||||
|
keys = parseKeyChords("a,,,b,c", "")
|
||||||
|
check(len(keys), 4)
|
||||||
|
check(keys[0], curses.AltZ+'a')
|
||||||
|
check(keys[1], curses.AltZ+'b')
|
||||||
|
check(keys[2], curses.AltZ+'c')
|
||||||
|
check(keys[3], curses.AltZ+',')
|
||||||
|
|
||||||
|
keys = parseKeyChords(",,,", "")
|
||||||
|
check(len(keys), 1)
|
||||||
|
check(keys[0], curses.AltZ+',')
|
||||||
|
}
|
||||||
|
@ -54,17 +54,21 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// We can uniquely identify the pattern for a given string since
|
|
||||||
// mode and caseMode do not change while the program is running
|
|
||||||
_patternCache = make(map[string]*Pattern)
|
|
||||||
_splitRegex = regexp.MustCompile("\\s+")
|
_splitRegex = regexp.MustCompile("\\s+")
|
||||||
_cache = NewChunkCache()
|
clearPatternCache()
|
||||||
|
clearChunkCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearPatternCache() {
|
func clearPatternCache() {
|
||||||
|
// We can uniquely identify the pattern for a given string since
|
||||||
|
// mode and caseMode do not change while the program is running
|
||||||
_patternCache = make(map[string]*Pattern)
|
_patternCache = make(map[string]*Pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clearChunkCache() {
|
||||||
|
_cache = NewChunkCache()
|
||||||
|
}
|
||||||
|
|
||||||
// BuildPattern builds Pattern object from the given arguments
|
// BuildPattern builds Pattern object from the given arguments
|
||||||
func BuildPattern(mode Mode, caseMode Case,
|
func BuildPattern(mode Mode, caseMode Case,
|
||||||
nth []Range, delimiter *regexp.Regexp, runes []rune) *Pattern {
|
nth []Range, delimiter *regexp.Regexp, runes []rune) *Pattern {
|
||||||
|
@ -28,6 +28,7 @@ type Terminal struct {
|
|||||||
yanked []rune
|
yanked []rune
|
||||||
input []rune
|
input []rune
|
||||||
multi bool
|
multi bool
|
||||||
|
toggleSort int
|
||||||
expect []int
|
expect []int
|
||||||
pressed int
|
pressed int
|
||||||
printQuery bool
|
printQuery bool
|
||||||
@ -93,6 +94,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
yanked: []rune{},
|
yanked: []rune{},
|
||||||
input: input,
|
input: input,
|
||||||
multi: opts.Multi,
|
multi: opts.Multi,
|
||||||
|
toggleSort: opts.ToggleSort,
|
||||||
expect: opts.Expect,
|
expect: opts.Expect,
|
||||||
pressed: 0,
|
pressed: 0,
|
||||||
printQuery: opts.PrintQuery,
|
printQuery: opts.PrintQuery,
|
||||||
@ -457,6 +459,10 @@ func (t *Terminal) rubout(pattern string) {
|
|||||||
t.input = append(t.input[:t.cx], after...)
|
t.input = append(t.input[:t.cx], after...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keyMatch(key int, event C.Event) bool {
|
||||||
|
return event.Type == key || event.Type == C.Rune && int(event.Char) == key-C.AltZ
|
||||||
|
}
|
||||||
|
|
||||||
// Loop is called to start Terminal I/O
|
// Loop is called to start Terminal I/O
|
||||||
func (t *Terminal) Loop() {
|
func (t *Terminal) Loop() {
|
||||||
<-t.startChan
|
<-t.startChan
|
||||||
@ -553,12 +559,19 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range t.expect {
|
for _, key := range t.expect {
|
||||||
if event.Type == key || event.Type == C.Rune && int(event.Char) == key-C.AltZ {
|
if keyMatch(key, event) {
|
||||||
t.pressed = key
|
t.pressed = key
|
||||||
req(reqClose)
|
req(reqClose)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if t.toggleSort > 0 {
|
||||||
|
if keyMatch(t.toggleSort, event) {
|
||||||
|
t.eventBox.Set(EvtSearchNew, true)
|
||||||
|
t.mutex.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case C.Invalid:
|
case C.Invalid:
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
@ -688,7 +701,7 @@ func (t *Terminal) Loop() {
|
|||||||
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
t.mutex.Unlock() // Must be unlocked before touching reqBox
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
t.eventBox.Set(EvtSearchNew, nil)
|
t.eventBox.Set(EvtSearchNew, false)
|
||||||
}
|
}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
t.reqBox.Set(event, nil)
|
t.reqBox.Set(event, nil)
|
||||||
|
@ -457,6 +457,19 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys "seq 1 100 | #{fzf '-q55 -1 --expect=alt-z --print-query'}", :Enter
|
tmux.send_keys "seq 1 100 | #{fzf '-q55 -1 --expect=alt-z --print-query'}", :Enter
|
||||||
assert_equal ['55', '', '55'], readonce.split($/)
|
assert_equal ['55', '', '55'], readonce.split($/)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_toggle_sort
|
||||||
|
tmux.send_keys "seq 1 111 | #{fzf '-m +s --tac --toggle-sort=ctrl-r -q11'}", :Enter
|
||||||
|
tmux.until { |lines| lines[-3].include? '> 111' }
|
||||||
|
tmux.send_keys :Tab
|
||||||
|
tmux.until { |lines| lines[-2].include? '4/111 (1)' }
|
||||||
|
tmux.send_keys 'C-R'
|
||||||
|
tmux.until { |lines| lines[-3].include? '> 11' }
|
||||||
|
tmux.send_keys :Tab
|
||||||
|
tmux.until { |lines| lines[-2].include? '4/111 (2)' }
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
assert_equal ['111', '11'], readonce.split($/)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module TestShell
|
module TestShell
|
||||||
|
Loading…
Reference in New Issue
Block a user