Compare commits

..

7 Commits

Author SHA1 Message Date
Junegunn Choi
5759d50d4a 0.13.2 2016-06-16 02:16:13 +09:00
Junegunn Choi
e455836cc9 Fix race condition where preview window is not properly cleared 2016-06-15 13:15:17 +09:00
Junegunn Choi
8a90f26c8a 0.13.1 2016-06-14 21:53:00 +09:00
Junegunn Choi
24e1fabf2e Do not process ANSI codes in --preview output at once
Close #598
2016-06-14 21:52:47 +09:00
Junegunn Choi
c39c039e15 [shell] Add $FZF_CTRL_T_OPTS and $FZF_ALT_C_OPTS
Close #596
2016-06-12 20:48:23 +09:00
Junegunn Choi
07f176f426 Merge pull request #595 from aykamko/speed-up-fzf-completion
Optimize fzf_default_completion binding
2016-06-12 11:56:49 +09:00
Aleks Kamko
19339e3a6d optimize fzf_default_completion binding 2016-06-11 15:19:16 -07:00
15 changed files with 74 additions and 54 deletions

View File

@@ -1,6 +1,14 @@
CHANGELOG
=========
0.13.2
------
- Fixed race condition where preview window is not properly cleared
0.13.1
------
- Fixed UI issue with large `--preview` output with many ANSI codes
0.13.0
------
- Added preview feature

View File

@@ -178,11 +178,14 @@ fish.
- `CTRL-T` - Paste the selected files and directories onto the command line
- Set `FZF_CTRL_T_COMMAND` to override the default command
- Set `FZF_CTRL_T_OPTS` to pass additional options
- `CTRL-R` - Paste the selected command from history onto the command line
- Sort is disabled by default to respect chronological ordering
- Press `CTRL-R` again to toggle sort
- Set `FZF_CTRL_R_OPTS` to pass additional options
- `ALT-C` - cd into the selected directory
- Set `FZF_ALT_C_COMMAND` to override the default command
- Set `FZF_ALT_C_OPTS` to pass additional options
If you're on a tmux session, fzf will start in a split pane. You may disable
this tmux integration by setting `FZF_TMUX` to 0, or change the height of the

View File

@@ -2,8 +2,8 @@
set -u
[[ "$@" =~ --pre ]] && version=0.13.0 pre=1 ||
version=0.13.0 pre=0
[[ "$@" =~ --pre ]] && version=0.13.2 pre=1 ||
version=0.13.2 pre=0
auto_completion=
key_bindings=

View File

@@ -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
THE SOFTWARE.
..
.TH fzf 1 "Jun 2016" "fzf 0.13.0" "fzf - a command-line fuzzy finder"
.TH fzf 1 "Jun 2016" "fzf 0.13.2" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder

View File

@@ -181,8 +181,11 @@ fzf-completion() {
fi
}
[ -z "$fzf_default_completion" ] &&
fzf_default_completion=$(bindkey '^I' | \grep -v undefined-key | awk '{print $2}')
[ -z "$fzf_default_completion" ] && {
binding=$(bindkey '^I')
[[ $binding =~ 'undefined-key' ]] || fzf_default_completion=$binding[(w)2]
unset binding
}
zle -N fzf-completion
bindkey '^I' fzf-completion

View File

@@ -5,7 +5,7 @@ __fzf_select__() {
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"}"
eval "$cmd" | fzf -m | while read -r item; do
eval "$cmd | fzf -m $FZF_CTRL_T_OPTS" | while read -r item; do
printf '%q ' "$item"
done
echo
@@ -26,7 +26,7 @@ __fzf_select_tmux__() {
height="-l $height"
fi
tmux split-window $height "cd $(printf %q "$PWD"); FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS") PATH=$(printf %q "$PATH") FZF_CTRL_T_COMMAND=$(printf %q "$FZF_CTRL_T_COMMAND") bash -c 'source \"${BASH_SOURCE[0]}\"; tmux send-keys -t $TMUX_PANE \"\$(__fzf_select__)\"'"
tmux split-window $height "cd $(printf %q "$PWD"); FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS") PATH=$(printf %q "$PATH") FZF_CTRL_T_COMMAND=$(printf %q "$FZF_CTRL_T_COMMAND") FZF_CTRL_T_OPTS=$(printf %q "$FZF_CTRL_T_OPTS") bash -c 'source \"${BASH_SOURCE[0]}\"; tmux send-keys -t $TMUX_PANE \"\$(__fzf_select__)\"'"
}
fzf-file-widget() {
@@ -43,7 +43,7 @@ __fzf_cd__() {
local cmd dir
cmd="${FZF_ALT_C_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"}"
dir=$(eval "$cmd" | $(__fzfcmd) +m) && printf 'cd %q' "$dir"
dir=$(eval "$cmd | $(__fzfcmd) +m $FZF_ALT_C_OPTS") && printf 'cd %q' "$dir"
}
__fzf_history__() (

View File

@@ -19,7 +19,7 @@ function fzf_key_bindings
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)" -m > $TMPDIR/fzf.result"
eval "$FZF_CTRL_T_COMMAND | "(__fzfcmd)" -m $FZF_CTRL_T_OPTS > $TMPDIR/fzf.result"
and for i in (seq 20); commandline -i (cat $TMPDIR/fzf.result | __fzf_escape) 2> /dev/null; and break; sleep 0.1; end
commandline -f repaint
rm -f $TMPDIR/fzf.result
@@ -37,7 +37,7 @@ function fzf_key_bindings
command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"
# Fish hangs if the command before pipe redirects (2> /dev/null)
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m > $TMPDIR/fzf.result"
eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)" +m $FZF_ALT_C_OPTS > $TMPDIR/fzf.result"
[ (cat $TMPDIR/fzf.result | wc -l) -gt 0 ]
and cd (cat $TMPDIR/fzf.result)
commandline -f repaint

View File

@@ -8,7 +8,7 @@ __fsel() {
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | sed 1d | cut -b3-"}"
eval "$cmd" | $(__fzfcmd) -m | while read item; do
eval "$cmd | $(__fzfcmd) -m $FZF_CTRL_T_OPTS" | while read item; do
echo -n "${(q)item} "
done
echo
@@ -29,7 +29,7 @@ bindkey '^T' fzf-file-widget
fzf-cd-widget() {
local cmd="${FZF_ALT_C_COMMAND:-"command find -L . \\( -path '*/\\.*' -o -fstype 'dev' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | sed 1d | cut -b3-"}"
cd "${$(eval "$cmd" | $(__fzfcmd) +m):-.}"
cd "${$(eval "$cmd | $(__fzfcmd) +m $FZF_ALT_C_OPTS"):-.}"
zle reset-prompt
}
zle -N fzf-cd-widget

View File

@@ -36,7 +36,7 @@ func init() {
ansiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
}
func extractColor(str string, state *ansiState) (string, []ansiOffset, *ansiState) {
func extractColor(str string, state *ansiState, proc func(string, *ansiState) bool) (string, []ansiOffset, *ansiState) {
var offsets []ansiOffset
var output bytes.Buffer
@@ -46,7 +46,11 @@ func extractColor(str string, state *ansiState) (string, []ansiOffset, *ansiStat
idx := 0
for _, offset := range ansiRegex.FindAllStringIndex(str, -1) {
output.WriteString(str[idx:offset[0]])
prev := str[idx:offset[0]]
output.WriteString(prev)
if proc != nil && !proc(prev, state) {
break
}
newState := interpretCode(str[offset[0]:offset[1]], state)
if !newState.equals(state) {
@@ -77,6 +81,9 @@ func extractColor(str string, state *ansiState) (string, []ansiOffset, *ansiStat
(&offsets[len(offsets)-1]).offset[1] = int32(utf8.RuneCount(output.Bytes()))
}
}
if proc != nil {
proc(rest, state)
}
return output.String(), offsets, state
}

View File

@@ -17,7 +17,7 @@ func TestExtractColor(t *testing.T) {
var state *ansiState
clean := "\x1b[0m"
check := func(assertion func(ansiOffsets []ansiOffset, state *ansiState)) {
output, ansiOffsets, newState := extractColor(src, state)
output, ansiOffsets, newState := extractColor(src, state, nil)
state = newState
if output != "hello world" {
t.Errorf("Invalid output: {}", output)

View File

@@ -8,7 +8,7 @@ import (
const (
// Current version
version = "0.13.0"
version = "0.13.2"
// Core
coordinatorDelayMax time.Duration = 100 * time.Millisecond

View File

@@ -73,7 +73,7 @@ func Run(opts *Options) {
if opts.Theme != nil {
var state *ansiState
ansiProcessor = func(data []byte) ([]rune, []ansiOffset) {
trimmed, offsets, newState := extractColor(string(data), state)
trimmed, offsets, newState := extractColor(string(data), state, nil)
state = newState
return []rune(trimmed), offsets
}
@@ -81,7 +81,7 @@ func Run(opts *Options) {
// When color is disabled but ansi option is given,
// we simply strip out ANSI codes from the input
ansiProcessor = func(data []byte) ([]rune, []ansiOffset) {
trimmed, _, _ := extractColor(string(data), nil)
trimmed, _, _ := extractColor(string(data), nil, nil)
return []rune(trimmed), nil
}
}

View File

@@ -128,7 +128,7 @@ func (item *Item) AsString(stripAnsi bool) string {
func (item *Item) StringPtr(stripAnsi bool) *string {
if item.origText != nil {
if stripAnsi {
trimmed, _, _ := extractColor(string(*item.origText), nil)
trimmed, _, _ := extractColor(string(*item.origText), nil, nil)
return &trimmed
}
orig := string(*item.origText)

View File

@@ -82,6 +82,11 @@ type selectedItem struct {
type byTimeOrder []selectedItem
type previewRequest struct {
ok bool
str string
}
func (a byTimeOrder) Len() int {
return len(a)
}
@@ -556,7 +561,7 @@ func (t *Terminal) printHeader() {
if line >= max {
continue
}
trimmed, colors, newState := extractColor(lineStr, state)
trimmed, colors, newState := extractColor(lineStr, state, nil)
state = newState
item := &Item{
text: []rune(trimmed),
@@ -730,25 +735,13 @@ func (t *Terminal) printHighlighted(item *Item, bold bool, col1 int, col2 int, c
}
func (t *Terminal) printPreview() {
trimmed, ansiOffsets, _ := extractColor(t.previewTxt, nil)
var index int32
t.pwindow.Erase()
for _, o := range ansiOffsets {
b := o.offset[0]
e := o.offset[1]
if b > index {
if !t.pwindow.Fill(trimmed[index:b]) {
return
}
}
if !t.pwindow.CFill(trimmed[b:e], o.color.fg, o.color.bg, o.color.bold) {
return
}
index = e
}
if int(index) < len(trimmed) {
t.pwindow.Fill(trimmed[index:])
extractColor(t.previewTxt, nil, func(str string, ansi *ansiState) bool {
if ansi != nil && ansi.colored() {
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.bold)
}
return t.pwindow.Fill(str)
})
}
func processTabs(runes []rune, prefixWidth int) (string, int) {
@@ -920,21 +913,23 @@ func (t *Terminal) Loop() {
if t.hasPreviewWindow() {
go func() {
for {
focused := ""
request := previewRequest{false, ""}
t.previewBox.Wait(func(events *util.Events) {
for req, value := range *events {
switch req {
case reqPreviewEnqueue:
focused = value.(string)
request = value.(previewRequest)
}
}
events.Clear()
})
if len(focused) > 0 {
command := strings.Replace(t.preview.command, "{}", quoteEntry(focused), -1)
if request.ok {
command := strings.Replace(t.preview.command, "{}", quoteEntry(request.str), -1)
cmd := util.ExecCommand(command)
out, _ := cmd.CombinedOutput()
t.reqBox.Set(reqPreviewDisplay, string(out))
} else {
t.reqBox.Set(reqPreviewDisplay, "")
}
}
}()
@@ -948,7 +943,7 @@ func (t *Terminal) Loop() {
}
go func() {
focused := ""
focused := previewRequest{false, ""}
for {
t.reqBox.Wait(func(events *util.Events) {
defer events.Clear()
@@ -965,20 +960,18 @@ func (t *Terminal) Loop() {
case reqList:
t.printList()
cnt := t.merger.Length()
var currentFocus previewRequest
if cnt > 0 && cnt > t.cy {
currentFocus := t.current()
currentFocus = previewRequest{true, t.current()}
} else {
currentFocus = previewRequest{false, ""}
}
if currentFocus != focused {
focused = currentFocus
if t.isPreviewEnabled() {
t.previewBox.Set(reqPreviewEnqueue, focused)
}
}
} else {
if focused != "" && t.isPreviewEnabled() {
t.pwindow.Erase()
}
focused = ""
}
case reqJump:
if t.merger.Length() == 0 {
t.jumping = jumpDisabled
@@ -1088,7 +1081,7 @@ func (t *Terminal) Loop() {
t.resizeWindows()
cnt := t.merger.Length()
if t.previewing && cnt > 0 && cnt > t.cy {
t.previewBox.Set(reqPreviewEnqueue, t.current())
t.previewBox.Set(reqPreviewEnqueue, previewRequest{true, t.current()})
}
req(reqList, reqInfo)
}

View File

@@ -1229,14 +1229,20 @@ class TestGoFZF < TestBase
end
def test_preview
tmux.send_keys %[seq 1000 | #{FZF} --preview 'echo {{}-{}}' --bind ?:toggle-preview], :Enter
tmux.send_keys %[seq 1000 | sed s/^2$// | #{FZF} --preview 'sleep 0.2; echo {{}-{}}' --bind ?:toggle-preview], :Enter
tmux.until { |lines| lines[1].include?(' {1-1}') }
tmux.send_keys :Up
tmux.until { |lines| lines[1].include?(' {-}') }
tmux.send_keys '555'
tmux.until { |lines| lines[1].include?(' {555-555}') }
tmux.send_keys '?'
tmux.until { |lines| !lines[1].include?(' {555-555}') }
tmux.send_keys '?'
tmux.until { |lines| lines[1].include?(' {555-555}') }
tmux.send_keys :BSpace
tmux.until { |lines| lines[-2].start_with? ' 28/1000' }
tmux.send_keys 'foobar'
tmux.until { |lines| !lines[1].include?('{') }
end
def test_preview_hidden