mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-12-23 11:29:01 +00:00
parent
14775aa975
commit
6c37177cf5
27
CHANGELOG.md
27
CHANGELOG.md
@ -12,6 +12,18 @@ CHANGELOG
|
|||||||
# Send actions to the server
|
# Send actions to the server
|
||||||
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
||||||
```
|
```
|
||||||
|
- New event
|
||||||
|
- Added `load` event that is triggered when the input stream is complete
|
||||||
|
and the initial processing of the list is complete.
|
||||||
|
```sh
|
||||||
|
# Change the prompt to "loaded" when the input stream is complete
|
||||||
|
(seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '
|
||||||
|
|
||||||
|
# You can use it instead of 'start' event without `--sync` if asynchronous
|
||||||
|
# trigger is not an issue.
|
||||||
|
(seq 10; sleep 1; seq 11 20) | fzf --bind 'load:last'
|
||||||
|
```
|
||||||
|
- New actions
|
||||||
- Added `pos(...)` action to move the cursor to the numeric position
|
- Added `pos(...)` action to move the cursor to the numeric position
|
||||||
- `first` and `last` are equivalent to `pos(1)` and `pos(-1)` respectively
|
- `first` and `last` are equivalent to `pos(1)` and `pos(-1)` respectively
|
||||||
```sh
|
```sh
|
||||||
@ -21,15 +33,13 @@ CHANGELOG
|
|||||||
# Put the cursor on the 10th to last item
|
# Put the cursor on the 10th to last item
|
||||||
seq 100 | fzf --sync --bind 'start:pos(-10)'
|
seq 100 | fzf --sync --bind 'start:pos(-10)'
|
||||||
```
|
```
|
||||||
- Added `load` event that is triggered when the input stream is complete and
|
- Added `reload-sync(...)` action which replaces the current list only after
|
||||||
the initial processing of the list is complete.
|
the reload process is complete. This is useful when the command takes
|
||||||
|
a while to produce the initial output and you don't want fzf to run against
|
||||||
|
an empty list while the command is running.
|
||||||
```sh
|
```sh
|
||||||
# Change the prompt to "loaded" when the input stream is complete
|
# You can still filter and select entries from the initial list for 3 seconds
|
||||||
(seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '
|
seq 100 | fzf --bind 'load:reload-sync(sleep 3; seq 1000)+unbind(load)'
|
||||||
|
|
||||||
# You can use it instead of 'start' event without `--sync` if asynchronous
|
|
||||||
# trigger is not an issue.
|
|
||||||
(seq 10; sleep 1; seq 11 20) | fzf --bind 'load:last'
|
|
||||||
```
|
```
|
||||||
- Added `next-selected` and `prev-selected` actions to move between selected
|
- Added `next-selected` and `prev-selected` actions to move between selected
|
||||||
items
|
items
|
||||||
@ -58,6 +68,7 @@ CHANGELOG
|
|||||||
# Can only type numbers
|
# Can only type numbers
|
||||||
fzf --bind 'change:transform-query(sed 's/[^0-9]//g' <<< {q})'
|
fzf --bind 'change:transform-query(sed 's/[^0-9]//g' <<< {q})'
|
||||||
```
|
```
|
||||||
|
- Improvements
|
||||||
- `put` action can optionally take an argument string
|
- `put` action can optionally take an argument string
|
||||||
```sh
|
```sh
|
||||||
# a will put 'alpha' on the prompt, ctrl-b will put 'bravo'
|
# a will put 'alpha' on the prompt, ctrl-b will put 'bravo'
|
||||||
|
@ -1019,6 +1019,7 @@ A key or an event can be bound to one or more of the following actions.
|
|||||||
\fBrefresh-preview\fR
|
\fBrefresh-preview\fR
|
||||||
\fBrebind(...)\fR (rebind bindings after \fBunbind\fR)
|
\fBrebind(...)\fR (rebind bindings after \fBunbind\fR)
|
||||||
\fBreload(...)\fR (see below for the details)
|
\fBreload(...)\fR (see below for the details)
|
||||||
|
\fBreload-sync(...)\fR (see below for the details)
|
||||||
\fBreplace-query\fR (replace query string with the current selection)
|
\fBreplace-query\fR (replace query string with the current selection)
|
||||||
\fBselect\fR
|
\fBselect\fR
|
||||||
\fBselect-all\fR (select all matches)
|
\fBselect-all\fR (select all matches)
|
||||||
@ -1122,6 +1123,16 @@ e.g.
|
|||||||
fzf --bind "change:reload:$RG_PREFIX {q} || true" \\
|
fzf --bind "change:reload:$RG_PREFIX {q} || true" \\
|
||||||
--ansi --disabled --query "$INITIAL_QUERY"\fR
|
--ansi --disabled --query "$INITIAL_QUERY"\fR
|
||||||
|
|
||||||
|
\fBreload-sync(...)\fR is a synchronous version of \fBreload\fR that replaces
|
||||||
|
the list only when the command is complete. This is useful when the command
|
||||||
|
takes a while to produce the initial output and you don't want fzf to run
|
||||||
|
against an empty list while the command is running.
|
||||||
|
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fB# You can still filter and select entries from the initial list for 3 seconds
|
||||||
|
seq 100 | fzf --bind 'load:reload-sync(sleep 3; seq 1000)+unbind(load)'\fR
|
||||||
|
|
||||||
.SS PREVIEW BINDING
|
.SS PREVIEW BINDING
|
||||||
|
|
||||||
With \fBpreview(...)\fR action, you can specify multiple different preview
|
With \fBpreview(...)\fR action, you can specify multiple different preview
|
||||||
|
43
src/core.go
43
src/core.go
@ -213,15 +213,6 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
clearSelection := util.Once(false)
|
clearSelection := util.Once(false)
|
||||||
ticks := 0
|
ticks := 0
|
||||||
var nextCommand *string
|
var nextCommand *string
|
||||||
restart := func(command string) {
|
|
||||||
reading = true
|
|
||||||
clearCache = util.Once(true)
|
|
||||||
clearSelection = util.Once(true)
|
|
||||||
chunkList.Clear()
|
|
||||||
itemIndex = 0
|
|
||||||
header = make([]string, 0, opts.HeaderLines)
|
|
||||||
go reader.restart(command)
|
|
||||||
}
|
|
||||||
eventBox.Watch(EvtReadNew)
|
eventBox.Watch(EvtReadNew)
|
||||||
total := 0
|
total := 0
|
||||||
query := []rune{}
|
query := []rune{}
|
||||||
@ -236,6 +227,25 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
terminal.startChan <- fitpad{-1, -1}
|
terminal.startChan <- fitpad{-1, -1}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useSnapshot := false
|
||||||
|
var snapshot []*Chunk
|
||||||
|
var prevSnapshot []*Chunk
|
||||||
|
var count int
|
||||||
|
restart := func(command string) {
|
||||||
|
reading = true
|
||||||
|
clearCache = util.Once(true)
|
||||||
|
clearSelection = util.Once(true)
|
||||||
|
// We should not update snapshot if reload is triggered again while
|
||||||
|
// the previous reload is in progress
|
||||||
|
if useSnapshot && prevSnapshot != nil {
|
||||||
|
snapshot, count = chunkList.Snapshot()
|
||||||
|
}
|
||||||
|
chunkList.Clear()
|
||||||
|
itemIndex = 0
|
||||||
|
header = make([]string, 0, opts.HeaderLines)
|
||||||
|
go reader.restart(command)
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
delay := true
|
delay := true
|
||||||
ticks++
|
ticks++
|
||||||
@ -267,7 +277,13 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
} else {
|
} else {
|
||||||
reading = reading && evt == EvtReadNew
|
reading = reading && evt == EvtReadNew
|
||||||
}
|
}
|
||||||
snapshot, count := chunkList.Snapshot()
|
if useSnapshot && evt == EvtReadFin {
|
||||||
|
useSnapshot = false
|
||||||
|
prevSnapshot = nil
|
||||||
|
}
|
||||||
|
if !useSnapshot {
|
||||||
|
snapshot, count = chunkList.Snapshot()
|
||||||
|
}
|
||||||
total = count
|
total = count
|
||||||
terminal.UpdateCount(total, !reading, value.(*string))
|
terminal.UpdateCount(total, !reading, value.(*string))
|
||||||
if opts.Sync {
|
if opts.Sync {
|
||||||
@ -286,6 +302,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
case searchRequest:
|
case searchRequest:
|
||||||
sort = val.sort
|
sort = val.sort
|
||||||
command = val.command
|
command = val.command
|
||||||
|
if command != nil {
|
||||||
|
useSnapshot = val.sync
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if command != nil {
|
if command != nil {
|
||||||
if reading {
|
if reading {
|
||||||
@ -296,7 +315,9 @@ func Run(opts *Options, version string, revision string) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
snapshot, _ := chunkList.Snapshot()
|
if !useSnapshot {
|
||||||
|
snapshot, _ = chunkList.Snapshot()
|
||||||
|
}
|
||||||
reset := clearCache()
|
reset := clearCache()
|
||||||
matcher.Reset(snapshot, input(reset), true, !reading, sort, reset)
|
matcher.Reset(snapshot, input(reset), true, !reading, sort, reset)
|
||||||
delay = false
|
delay = false
|
||||||
|
@ -892,7 +892,7 @@ const (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
executeRegexp = regexp.MustCompile(
|
executeRegexp = regexp.MustCompile(
|
||||||
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-query|change-prompt|change-preview-window|change-preview|(?:re|un)bind|pos|put|transform-query)`)
|
`(?si)[:+](execute(?:-multi|-silent)?|reload(?:-sync)?|preview|change-query|change-prompt|change-preview-window|change-preview|(?:re|un)bind|pos|put|transform-query)`)
|
||||||
splitRegexp = regexp.MustCompile("[,:]+")
|
splitRegexp = regexp.MustCompile("[,:]+")
|
||||||
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
|
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
|
||||||
}
|
}
|
||||||
@ -1185,6 +1185,8 @@ func isExecuteAction(str string) actionType {
|
|||||||
switch prefix {
|
switch prefix {
|
||||||
case "reload":
|
case "reload":
|
||||||
return actReload
|
return actReload
|
||||||
|
case "reload-sync":
|
||||||
|
return actReloadSync
|
||||||
case "unbind":
|
case "unbind":
|
||||||
return actUnbind
|
return actUnbind
|
||||||
case "rebind":
|
case "rebind":
|
||||||
|
@ -335,6 +335,7 @@ const (
|
|||||||
actFirst
|
actFirst
|
||||||
actLast
|
actLast
|
||||||
actReload
|
actReload
|
||||||
|
actReloadSync
|
||||||
actDisableSearch
|
actDisableSearch
|
||||||
actEnableSearch
|
actEnableSearch
|
||||||
actSelect
|
actSelect
|
||||||
@ -353,6 +354,7 @@ type placeholderFlags struct {
|
|||||||
|
|
||||||
type searchRequest struct {
|
type searchRequest struct {
|
||||||
sort bool
|
sort bool
|
||||||
|
sync bool
|
||||||
command *string
|
command *string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2540,6 +2542,7 @@ func (t *Terminal) Loop() {
|
|||||||
}()
|
}()
|
||||||
for looping {
|
for looping {
|
||||||
var newCommand *string
|
var newCommand *string
|
||||||
|
var reloadSync bool
|
||||||
changed := false
|
changed := false
|
||||||
beof := false
|
beof := false
|
||||||
queryChanged := false
|
queryChanged := false
|
||||||
@ -3030,7 +3033,7 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case actReload:
|
case actReload, actReloadSync:
|
||||||
t.failed = nil
|
t.failed = nil
|
||||||
|
|
||||||
valid, list := t.buildPlusList(a.a, false)
|
valid, list := t.buildPlusList(a.a, false)
|
||||||
@ -3044,6 +3047,7 @@ func (t *Terminal) Loop() {
|
|||||||
if valid {
|
if valid {
|
||||||
command := t.replacePlaceholder(a.a, false, string(t.input), list)
|
command := t.replacePlaceholder(a.a, false, string(t.input), list)
|
||||||
newCommand = &command
|
newCommand = &command
|
||||||
|
reloadSync = a.t == actReloadSync
|
||||||
t.reading = true
|
t.reading = true
|
||||||
}
|
}
|
||||||
case actUnbind:
|
case actUnbind:
|
||||||
@ -3173,7 +3177,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 || newCommand != nil {
|
if changed || newCommand != nil {
|
||||||
t.eventBox.Set(EvtSearchNew, searchRequest{sort: t.sort, command: newCommand})
|
t.eventBox.Set(EvtSearchNew, searchRequest{sort: t.sort, sync: reloadSync, command: newCommand})
|
||||||
}
|
}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
t.reqBox.Set(event, nil)
|
t.reqBox.Set(event, nil)
|
||||||
|
@ -2161,6 +2161,15 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_includes lines[1], '4' }
|
tmux.until { |lines| assert_includes lines[1], '4' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_reload_sync
|
||||||
|
tmux.send_keys "seq 100 | #{FZF} --bind 'load:reload-sync(sleep 1; seq 1000)+unbind(load)'", :Enter
|
||||||
|
tmux.until { |lines| assert_equal 100, lines.item_count }
|
||||||
|
tmux.send_keys '00'
|
||||||
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
|
# After 1 second
|
||||||
|
tmux.until { |lines| assert_equal 10, lines.match_count }
|
||||||
|
end
|
||||||
|
|
||||||
def test_scroll_off
|
def test_scroll_off
|
||||||
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --scroll-off=3 --bind l:last", :Enter
|
||||||
tmux.until { |lines| assert_equal 1000, lines.item_count }
|
tmux.until { |lines| assert_equal 1000, lines.item_count }
|
||||||
|
Loading…
Reference in New Issue
Block a user