mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-15 14:53:47 -05:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b49f22cdf9 | ||
|
|
7e483b0c25 | ||
|
|
3cf9ae04c7 | ||
|
|
bf0cb4bfe2 | ||
|
|
773133c4ce | ||
|
|
ca0b3b6fd7 | ||
|
|
f4731c0514 | ||
|
|
34f16e5b7d | ||
|
|
83e9af6601 | ||
|
|
8bbf9335e1 | ||
|
|
159f30b37f | ||
|
|
2e3dc75425 | ||
|
|
7d3575b362 | ||
|
|
35d407021c | ||
|
|
076f49d447 | ||
|
|
0665fe0413 | ||
|
|
669a6fee40 | ||
|
|
8aab0fc189 | ||
|
|
5d6eb5bfd6 | ||
|
|
cf4711d878 | ||
|
|
21d664d670 | ||
|
|
ab182e276b | ||
|
|
96a3250152 | ||
|
|
f5746002fd | ||
|
|
e1e3339770 | ||
|
|
3a5086796d | ||
|
|
11300913a4 | ||
|
|
e65f14cbed | ||
|
|
6898849e3e | ||
|
|
2d61691bb2 | ||
|
|
eba9e04e2e | ||
|
|
33f32de690 | ||
|
|
93b8f61551 | ||
|
|
7f17a9d1b4 | ||
|
|
d34e4cf698 | ||
|
|
6b592137b9 | ||
|
|
d5e72bf55d | ||
|
|
5677e5e133 | ||
|
|
7a11a06cbd | ||
|
|
a5862d4b9c | ||
|
|
a50909e806 | ||
|
|
7c8f7d3f20 | ||
|
|
9078197446 | ||
|
|
0fe07cf9fe | ||
|
|
2216169ca1 | ||
|
|
50e989ca85 | ||
|
|
fa1fc3d855 | ||
|
|
bbe696e925 | ||
|
|
5d12f523a3 | ||
|
|
d295d20dc4 | ||
|
|
2ba10071c9 | ||
|
|
505dc0491b | ||
|
|
54a4ab0f26 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
bin
|
||||
src/fzf/fzf-*
|
||||
gopath
|
||||
bin/fzf
|
||||
target
|
||||
pkg
|
||||
Gemfile.lock
|
||||
.DS_Store
|
||||
doc/tags
|
||||
vendor
|
||||
|
||||
11
.travis.yml
11
.travis.yml
@@ -16,13 +16,6 @@ install:
|
||||
- sudo apt-get install -y zsh fish
|
||||
|
||||
script: |
|
||||
export GOPATH=~/go
|
||||
export FZF_BASE=$GOPATH/src/github.com/junegunn/fzf
|
||||
|
||||
mkdir -p $GOPATH/src/github.com/junegunn
|
||||
ln -s $(pwd) $FZF_BASE
|
||||
|
||||
cd $FZF_BASE/src && make test fzf/fzf-linux_amd64 install &&
|
||||
cd $FZF_BASE/bin && ln -sf fzf-linux_amd64 fzf-$(./fzf --version)-linux_amd64 &&
|
||||
cd $FZF_BASE && yes | ./install && rm -f fzf &&
|
||||
make test install &&
|
||||
./install --all &&
|
||||
tmux new "ruby test/test_go.rb > out && touch ok" && cat out && [ -e ok ]
|
||||
|
||||
11
BUILD.md
11
BUILD.md
@@ -13,19 +13,16 @@ Build instructions
|
||||
Makefile will set up and use its own `$GOPATH` under the project root.
|
||||
|
||||
```sh
|
||||
# Source files are located in src directory
|
||||
cd src
|
||||
|
||||
# Build fzf binary for your platform in src/fzf
|
||||
# Build fzf binary for your platform in target
|
||||
make
|
||||
|
||||
# Build fzf binary and copy it to bin directory
|
||||
make install
|
||||
|
||||
# Build 32-bit and 64-bit executables and tarballs
|
||||
# Build 32-bit and 64-bit executables and tarballs in target
|
||||
make release
|
||||
|
||||
# Make release archives for all supported platforms
|
||||
# Make release archives for all supported platforms in target
|
||||
make release-all
|
||||
```
|
||||
|
||||
@@ -35,7 +32,7 @@ Alternatively, you can build fzf directly with `go get` command without
|
||||
manually cloning the repository.
|
||||
|
||||
```sh
|
||||
go get -u github.com/junegunn/fzf/src/fzf
|
||||
go get -u github.com/junegunn/fzf
|
||||
```
|
||||
|
||||
Third-party libraries used
|
||||
|
||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,6 +1,27 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.16.8
|
||||
------
|
||||
- New `change` event and `top` action for `--bind`
|
||||
- `fzf --bind change:top`
|
||||
- Move cursor to the top result whenever the query string is changed
|
||||
- `fzf --bind ctrl-u:unix-word-rubout+top`
|
||||
- `top` combined with `unix-word-rubout`
|
||||
- Fixed inconsistent tiebreak scores when `--nth` is used
|
||||
- Proper display of tab characters in `--prompt`
|
||||
- Fixed not to `--cycle` on page-up/page-down to prevent overshoot
|
||||
- Git revision in `--version` output
|
||||
- Basic support for Cygwin environment
|
||||
- Many fixes in Vim plugin on Windows/Cygwin (thanks to @janlazo)
|
||||
|
||||
0.16.7
|
||||
------
|
||||
- Added support for `ctrl-alt-[a-z]` key chords
|
||||
- CTRL-Z (SIGSTOP) now works with fzf
|
||||
- fzf will export `$FZF_PREVIEW_WINDOW` so that the scripts can use it
|
||||
- Bug fixes and improvements in Vim plugin and shell extensions
|
||||
|
||||
0.16.6
|
||||
------
|
||||
- Minor bug fixes and improvements
|
||||
|
||||
138
Makefile
Normal file
138
Makefile
Normal file
@@ -0,0 +1,138 @@
|
||||
ifndef GOOS
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
GOOS := darwin
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
GOOS := linux
|
||||
else
|
||||
$(error "$$GOOS is not defined.")
|
||||
endif
|
||||
endif
|
||||
|
||||
MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
|
||||
ROOT_DIR := $(shell dirname $(MAKEFILE))
|
||||
GOPATH := $(ROOT_DIR)/gopath
|
||||
SRC_LINK := $(GOPATH)/src/github.com/junegunn/fzf/src
|
||||
VENDOR_LINK := $(GOPATH)/src/github.com/junegunn/fzf/vendor
|
||||
export GOPATH
|
||||
|
||||
GLIDE_YAML := glide.yaml
|
||||
GLIDE_LOCK := glide.lock
|
||||
SOURCES := $(wildcard *.go src/*.go src/*/*.go) $(SRC_LINK) $(VENDOR_LINK) $(GLIDE_LOCK) $(MAKEFILE)
|
||||
|
||||
REVISION := $(shell git log -n 1 --pretty=format:%h -- $(SOURCES))
|
||||
BUILD_FLAGS := -a -ldflags "-X main.revision=$(REVISION) -w -extldflags=$(LDFLAGS)" -tags "$(TAGS)"
|
||||
|
||||
BINARY32 := fzf-$(GOOS)_386
|
||||
BINARY64 := fzf-$(GOOS)_amd64
|
||||
BINARYARM5 := fzf-$(GOOS)_arm5
|
||||
BINARYARM6 := fzf-$(GOOS)_arm6
|
||||
BINARYARM7 := fzf-$(GOOS)_arm7
|
||||
BINARYARM8 := fzf-$(GOOS)_arm8
|
||||
VERSION := $(shell awk -F= '/version =/ {print $$2}' src/constants.go | tr -d "\" ")
|
||||
RELEASE32 := fzf-$(VERSION)-$(GOOS)_386
|
||||
RELEASE64 := fzf-$(VERSION)-$(GOOS)_amd64
|
||||
RELEASEARM5 := fzf-$(VERSION)-$(GOOS)_arm5
|
||||
RELEASEARM6 := fzf-$(VERSION)-$(GOOS)_arm6
|
||||
RELEASEARM7 := fzf-$(VERSION)-$(GOOS)_arm7
|
||||
RELEASEARM8 := fzf-$(VERSION)-$(GOOS)_arm8
|
||||
|
||||
# https://en.wikipedia.org/wiki/Uname
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
BINARY := $(BINARY64)
|
||||
else ifeq ($(UNAME_M),amd64)
|
||||
BINARY := $(BINARY64)
|
||||
else ifeq ($(UNAME_M),i686)
|
||||
BINARY := $(BINARY32)
|
||||
else ifeq ($(UNAME_M),i386)
|
||||
BINARY := $(BINARY32)
|
||||
else ifeq ($(UNAME_M),armv5l)
|
||||
BINARY := $(BINARYARM5)
|
||||
else ifeq ($(UNAME_M),armv6l)
|
||||
BINARY := $(BINARYARM6)
|
||||
else ifeq ($(UNAME_M),armv7l)
|
||||
BINARY := $(BINARYARM7)
|
||||
else
|
||||
$(error "Build on $(UNAME_M) is not supported, yet.")
|
||||
endif
|
||||
|
||||
all: target/$(BINARY)
|
||||
|
||||
target:
|
||||
mkdir -p $@
|
||||
|
||||
ifeq ($(GOOS),windows)
|
||||
release: target/$(BINARY32) target/$(BINARY64)
|
||||
cd target && cp -f $(BINARY32) fzf.exe && zip $(RELEASE32).zip fzf.exe
|
||||
cd target && cp -f $(BINARY64) fzf.exe && zip $(RELEASE64).zip fzf.exe
|
||||
cd target && rm -f fzf.exe
|
||||
else ifeq ($(GOOS),linux)
|
||||
release: target/$(BINARY32) target/$(BINARY64) target/$(BINARYARM5) target/$(BINARYARM6) target/$(BINARYARM7) target/$(BINARYARM8)
|
||||
cd target && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
|
||||
cd target && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
||||
cd target && cp -f $(BINARYARM5) fzf && tar -czf $(RELEASEARM5).tgz fzf
|
||||
cd target && cp -f $(BINARYARM6) fzf && tar -czf $(RELEASEARM6).tgz fzf
|
||||
cd target && cp -f $(BINARYARM7) fzf && tar -czf $(RELEASEARM7).tgz fzf
|
||||
cd target && cp -f $(BINARYARM8) fzf && tar -czf $(RELEASEARM8).tgz fzf
|
||||
cd target && rm -f fzf
|
||||
else
|
||||
release: target/$(BINARY32) target/$(BINARY64)
|
||||
cd target && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
|
||||
cd target && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
||||
cd target && rm -f fzf
|
||||
endif
|
||||
|
||||
release-all: clean test
|
||||
GOOS=darwin make release
|
||||
GOOS=linux make release
|
||||
GOOS=freebsd make release
|
||||
GOOS=openbsd make release
|
||||
GOOS=windows make release
|
||||
|
||||
$(SRC_LINK):
|
||||
mkdir -p $(shell dirname $(SRC_LINK))
|
||||
ln -sf $(ROOT_DIR)/src $(SRC_LINK)
|
||||
|
||||
$(VENDOR_LINK):
|
||||
mkdir -p $(shell dirname $(VENDOR_LINK))
|
||||
ln -sf $(ROOT_DIR)/vendor $(VENDOR_LINK)
|
||||
|
||||
vendor: $(GLIDE_YAML)
|
||||
go get -u github.com/Masterminds/glide && $(GOPATH)/bin/glide install && touch $@
|
||||
|
||||
test: $(SOURCES) vendor
|
||||
SHELL=/bin/sh GOOS= go test -v -tags "$(TAGS)" \
|
||||
github.com/junegunn/fzf/src \
|
||||
github.com/junegunn/fzf/src/algo \
|
||||
github.com/junegunn/fzf/src/tui \
|
||||
github.com/junegunn/fzf/src/util
|
||||
|
||||
install: bin/fzf
|
||||
|
||||
clean:
|
||||
rm -rf target
|
||||
|
||||
target/$(BINARY32): $(SOURCES) vendor
|
||||
GOARCH=386 go build $(BUILD_FLAGS) -o $@
|
||||
|
||||
target/$(BINARY64): $(SOURCES) vendor
|
||||
GOARCH=amd64 go build $(BUILD_FLAGS) -o $@
|
||||
|
||||
# https://github.com/golang/go/wiki/GoArm
|
||||
target/$(BINARYARM5): $(SOURCES) vendor
|
||||
GOARCH=arm GOARM=5 go build $(BUILD_FLAGS) -o $@
|
||||
|
||||
target/$(BINARYARM6): $(SOURCES) vendor
|
||||
GOARCH=arm GOARM=6 go build $(BUILD_FLAGS) -o $@
|
||||
|
||||
target/$(BINARYARM7): $(SOURCES) vendor
|
||||
GOARCH=arm GOARM=7 go build $(BUILD_FLAGS) -o $@
|
||||
|
||||
target/$(BINARYARM8): $(SOURCES) vendor
|
||||
GOARCH=arm64 go build $(BUILD_FLAGS) -o $@
|
||||
|
||||
bin/fzf: target/$(BINARY) | bin
|
||||
cp -f target/$(BINARY) bin/fzf
|
||||
|
||||
.PHONY: all release release-all test install clean
|
||||
155
README-VIM.md
Normal file
155
README-VIM.md
Normal file
@@ -0,0 +1,155 @@
|
||||
FZF Vim integration
|
||||
===================
|
||||
|
||||
This repository only enables basic integration with Vim. If you're looking for
|
||||
more, check out [fzf.vim](https://github.com/junegunn/fzf.vim) project.
|
||||
|
||||
(Note: To use fzf in GVim, an external terminal emulator is required.)
|
||||
|
||||
`:FZF[!]`
|
||||
---------
|
||||
|
||||
If you have set up fzf for Vim, `:FZF` command will be added.
|
||||
|
||||
```vim
|
||||
" Look for files under current directory
|
||||
:FZF
|
||||
|
||||
" Look for files under your home directory
|
||||
:FZF ~
|
||||
|
||||
" With options
|
||||
:FZF --no-sort --reverse --inline-info /tmp
|
||||
|
||||
" Bang version starts fzf in fullscreen mode
|
||||
:FZF!
|
||||
```
|
||||
|
||||
Similarly to [ctrlp.vim](https://github.com/kien/ctrlp.vim), use enter key,
|
||||
`CTRL-T`, `CTRL-X` or `CTRL-V` to open selected files in the current window,
|
||||
in new tabs, in horizontal splits, or in vertical splits respectively.
|
||||
|
||||
Note that the environment variables `FZF_DEFAULT_COMMAND` and
|
||||
`FZF_DEFAULT_OPTS` also apply here.
|
||||
|
||||
### Configuration
|
||||
|
||||
- `g:fzf_action`
|
||||
- Customizable extra key bindings for opening selected files in different ways
|
||||
- `g:fzf_layout`
|
||||
- Determines the size and position of fzf window (tmux pane or Neovim split)
|
||||
- `g:fzf_colors`
|
||||
- Customizes fzf colors to match the current color scheme
|
||||
- `g:fzf_history_dir`
|
||||
- Enables history feature
|
||||
- `g:fzf_launcher`
|
||||
- (Only in GVim) Terminal emulator to open fzf with
|
||||
- `g:Fzf_launcher` for function reference
|
||||
|
||||
#### Examples
|
||||
|
||||
```vim
|
||||
" This is the default extra key bindings
|
||||
let g:fzf_action = {
|
||||
\ 'ctrl-t': 'tab split',
|
||||
\ 'ctrl-x': 'split',
|
||||
\ 'ctrl-v': 'vsplit' }
|
||||
|
||||
" Default fzf layout
|
||||
" - down / up / left / right
|
||||
let g:fzf_layout = { 'down': '~40%' }
|
||||
|
||||
" In Neovim, you can set up fzf window using a Vim command
|
||||
let g:fzf_layout = { 'window': 'enew' }
|
||||
let g:fzf_layout = { 'window': '-tabnew' }
|
||||
let g:fzf_layout = { 'window': '10split enew' }
|
||||
|
||||
" Customize fzf colors to match your color scheme
|
||||
let g:fzf_colors =
|
||||
\ { 'fg': ['fg', 'Normal'],
|
||||
\ 'bg': ['bg', 'Normal'],
|
||||
\ 'hl': ['fg', 'Comment'],
|
||||
\ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
|
||||
\ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
|
||||
\ 'hl+': ['fg', 'Statement'],
|
||||
\ 'info': ['fg', 'PreProc'],
|
||||
\ 'prompt': ['fg', 'Conditional'],
|
||||
\ 'pointer': ['fg', 'Exception'],
|
||||
\ 'marker': ['fg', 'Keyword'],
|
||||
\ 'spinner': ['fg', 'Label'],
|
||||
\ 'header': ['fg', 'Comment'] }
|
||||
|
||||
" Enable per-command history.
|
||||
" CTRL-N and CTRL-P will be automatically bound to next-history and
|
||||
" previous-history instead of down and up. If you don't like the change,
|
||||
" explicitly bind the keys to down and up in your $FZF_DEFAULT_OPTS.
|
||||
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
||||
```
|
||||
|
||||
`fzf#run`
|
||||
---------
|
||||
|
||||
For more advanced uses, you can use `fzf#run([options])` function with the
|
||||
following options.
|
||||
|
||||
| Option name | Type | Description |
|
||||
| -------------------------- | ------------- | ---------------------------------------------------------------- |
|
||||
| `source` | string | External command to generate input to fzf (e.g. `find .`) |
|
||||
| `source` | list | Vim list as input to fzf |
|
||||
| `sink` | string | Vim command to handle the selected item (e.g. `e`, `tabe`) |
|
||||
| `sink` | funcref | Reference to function to process each selected item |
|
||||
| `sink*` | funcref | Similar to `sink`, but takes the list of output lines at once |
|
||||
| `options` | string/list | Options to fzf |
|
||||
| `dir` | string | Working directory |
|
||||
| `up`/`down`/`left`/`right` | number/string | Use tmux pane with the given size (e.g. `20`, `50%`) |
|
||||
| `window` (*Neovim only*) | string | Command to open fzf window (e.g. `vertical aboveleft 30new`) |
|
||||
| `launcher` | string | External terminal emulator to start fzf with (GVim only) |
|
||||
| `launcher` | funcref | Function for generating `launcher` string (GVim only) |
|
||||
|
||||
`options` entry can be either a string or a list. For simple cases, string
|
||||
should suffice, but prefer to use list type if you're concerned about escaping
|
||||
issues on different platforms.
|
||||
|
||||
```vim
|
||||
call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
|
||||
call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
|
||||
```
|
||||
|
||||
`fzf#wrap`
|
||||
----------
|
||||
|
||||
`fzf#wrap([name string,] [opts dict,] [fullscreen boolean])` is a helper
|
||||
function that decorates the options dictionary so that it understands
|
||||
`g:fzf_layout`, `g:fzf_action`, `g:fzf_colors`, and `g:fzf_history_dir` like
|
||||
`:FZF`.
|
||||
|
||||
```vim
|
||||
command! -bang MyStuff
|
||||
\ call fzf#run(fzf#wrap('my-stuff', {'dir': '~/my-stuff'}, <bang>0))
|
||||
```
|
||||
|
||||
GVim
|
||||
----
|
||||
|
||||
In GVim, you need an external terminal emulator to start fzf with. `xterm`
|
||||
command is used by default, but you can customize it with `g:fzf_launcher`.
|
||||
|
||||
```vim
|
||||
" This is the default. %s is replaced with fzf command
|
||||
let g:fzf_launcher = 'xterm -e bash -ic %s'
|
||||
|
||||
" Use urxvt instead
|
||||
let g:fzf_launcher = 'urxvt -geometry 120x30 -e sh -c %s'
|
||||
```
|
||||
|
||||
If you're running MacVim on OSX, I recommend you to use iTerm2 as the
|
||||
launcher. Refer to the [this wiki page][macvim-iterm2] to see how to set up.
|
||||
|
||||
[macvim-iterm2]: https://github.com/junegunn/fzf/wiki/On-MacVim-with-iTerm2
|
||||
|
||||
[License](LICENSE)
|
||||
------------------
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Junegunn Choi
|
||||
137
README.md
137
README.md
@@ -1,4 +1,4 @@
|
||||
<img src="https://raw.githubusercontent.com/junegunn/i/master/fzf.png" height="170" alt="fzf - a command-line fuzzy finder"> [](https://travis-ci.org/junegunn/fzf)
|
||||
<img src="https://raw.githubusercontent.com/junegunn/i/master/fzf.png" height="170" alt="fzf - a command-line fuzzy finder"> [](https://travis-ci.org/junegunn/fzf) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EKYAW9PGKPD2N)
|
||||
===
|
||||
|
||||
fzf is a general-purpose command-line fuzzy finder.
|
||||
@@ -15,6 +15,39 @@ Pros
|
||||
- Batteries included
|
||||
- Vim/Neovim plugin, key bindings and fuzzy auto-completion
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
* [Installation](#installation)
|
||||
* [Using git](#using-git)
|
||||
* [Using Homebrew](#using-homebrew)
|
||||
* [As Vim plugin](#as-vim-plugin)
|
||||
* [Windows](#windows)
|
||||
* [Upgrading fzf](#upgrading-fzf)
|
||||
* [Building fzf](#building-fzf)
|
||||
* [Usage](#usage)
|
||||
* [Using the finder](#using-the-finder)
|
||||
* [Layout](#layout)
|
||||
* [Search syntax](#search-syntax)
|
||||
* [Environment variables](#environment-variables)
|
||||
* [Options](#options)
|
||||
* [Examples](#examples)
|
||||
* [fzf-tmux script](#fzf-tmux-script)
|
||||
* [Key bindings for command line](#key-bindings-for-command-line)
|
||||
* [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh)
|
||||
* [Files and directories](#files-and-directories)
|
||||
* [Process IDs](#process-ids)
|
||||
* [Host names](#host-names)
|
||||
* [Environment variables / Aliases](#environment-variables--aliases)
|
||||
* [Settings](#settings)
|
||||
* [Supported commands](#supported-commands)
|
||||
* [Vim plugin](#vim-plugin)
|
||||
* [Tips](#tips)
|
||||
* [Respecting .gitignore, <code>.hgignore</code>, and <code>svn:ignore</code>](#respecting-gitignore-hgignore-and-svnignore)
|
||||
* [git ls-tree for fast traversal](#git-ls-tree-for-fast-traversal)
|
||||
* [Fish shell](#fish-shell)
|
||||
* [<a href="LICENSE">License</a>](#license)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
@@ -53,7 +86,7 @@ brew install fzf
|
||||
/usr/local/opt/fzf/install
|
||||
```
|
||||
|
||||
### Vim plugin
|
||||
### As Vim plugin
|
||||
|
||||
You can manually add the directory to `&runtimepath` as follows,
|
||||
|
||||
@@ -74,10 +107,18 @@ Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
|
||||
|
||||
### Windows
|
||||
|
||||
Pre-built binaries for Windows can be downloaded [here][bin]. However, other
|
||||
components of the project may not work on Windows. You might want to consider
|
||||
installing fzf on [Windows Subsystem for Linux][wsl] where everything runs
|
||||
flawlessly.
|
||||
Pre-built binaries for Windows can be downloaded [here][bin]. fzf is also
|
||||
available as a [Chocolatey package][choco].
|
||||
|
||||
[choco]: https://chocolatey.org/packages/fzf
|
||||
|
||||
```sh
|
||||
choco install fzf
|
||||
```
|
||||
|
||||
However, other components of the project may not work on Windows. You might
|
||||
want to consider installing fzf on [Windows Subsystem for Linux][wsl] where
|
||||
everything runs flawlessly.
|
||||
|
||||
[wsl]: https://blogs.msdn.microsoft.com/wsl/
|
||||
|
||||
@@ -90,6 +131,7 @@ method used.
|
||||
|
||||
- git: `cd ~/.fzf && git pull && ./install`
|
||||
- brew: `brew update; brew reinstall fzf`
|
||||
- chocolatey: `choco upgrade fzf`
|
||||
- vim-plug: `:PlugUpdate fzf`
|
||||
|
||||
Building fzf
|
||||
@@ -100,7 +142,7 @@ See [BUILD.md](BUILD.md).
|
||||
Usage
|
||||
-----
|
||||
|
||||
fzf will launch curses-based finder, read the list from STDIN, and write the
|
||||
fzf will launch interactive finder, read the list from STDIN, and write the
|
||||
selected item to STDOUT.
|
||||
|
||||
```sh
|
||||
@@ -332,79 +374,14 @@ On bash, fuzzy completion is enabled only for a predefined set of commands
|
||||
commands as well like follows.
|
||||
|
||||
```sh
|
||||
# There are also _fzf_path_completion and _fzf_dir_completion
|
||||
complete -F _fzf_file_completion -o default -o bashdefault doge
|
||||
complete -F _fzf_path_completion -o default -o bashdefault ag
|
||||
complete -F _fzf_dir_completion -o default -o bashdefault tree
|
||||
```
|
||||
|
||||
Usage as Vim plugin
|
||||
-------------------
|
||||
Vim plugin
|
||||
----------
|
||||
|
||||
This repository only enables basic integration with Vim. If you're looking for
|
||||
more, check out [fzf.vim](https://github.com/junegunn/fzf.vim) project.
|
||||
|
||||
(Note: To use fzf in GVim, an external terminal emulator is required.)
|
||||
|
||||
#### `:FZF[!]`
|
||||
|
||||
If you have set up fzf for Vim, `:FZF` command will be added.
|
||||
|
||||
```vim
|
||||
" Look for files under current directory
|
||||
:FZF
|
||||
|
||||
" Look for files under your home directory
|
||||
:FZF ~
|
||||
|
||||
" With options
|
||||
:FZF --no-sort --reverse --inline-info /tmp
|
||||
|
||||
" Bang version starts fzf in fullscreen mode
|
||||
:FZF!
|
||||
```
|
||||
|
||||
Similarly to [ctrlp.vim](https://github.com/kien/ctrlp.vim), use enter key,
|
||||
`CTRL-T`, `CTRL-X` or `CTRL-V` to open selected files in the current window,
|
||||
in new tabs, in horizontal splits, or in vertical splits respectively.
|
||||
|
||||
Note that the environment variables `FZF_DEFAULT_COMMAND` and
|
||||
`FZF_DEFAULT_OPTS` also apply here. Refer to [the wiki page][fzf-config] for
|
||||
customization.
|
||||
|
||||
[fzf-config]: https://github.com/junegunn/fzf/wiki/Configuring-Vim-plugin
|
||||
|
||||
#### `fzf#run`
|
||||
|
||||
For more advanced uses, you can use `fzf#run([options])` function with the
|
||||
following options.
|
||||
|
||||
| Option name | Type | Description |
|
||||
| -------------------------- | ------------- | ---------------------------------------------------------------- |
|
||||
| `source` | string | External command to generate input to fzf (e.g. `find .`) |
|
||||
| `source` | list | Vim list as input to fzf |
|
||||
| `sink` | string | Vim command to handle the selected item (e.g. `e`, `tabe`) |
|
||||
| `sink` | funcref | Reference to function to process each selected item |
|
||||
| `sink*` | funcref | Similar to `sink`, but takes the list of output lines at once |
|
||||
| `options` | string | Options to fzf |
|
||||
| `dir` | string | Working directory |
|
||||
| `up`/`down`/`left`/`right` | number/string | Use tmux pane with the given size (e.g. `20`, `50%`) |
|
||||
| `window` (*Neovim only*) | string | Command to open fzf window (e.g. `vertical aboveleft 30new`) |
|
||||
| `launcher` | string | External terminal emulator to start fzf with (GVim only) |
|
||||
| `launcher` | funcref | Function for generating `launcher` string (GVim only) |
|
||||
|
||||
Examples can be found on [the wiki
|
||||
page](https://github.com/junegunn/fzf/wiki/Examples-(vim)).
|
||||
|
||||
#### `fzf#wrap`
|
||||
|
||||
`fzf#wrap([name string,] [opts dict,] [fullscreen boolean])` is a helper
|
||||
function that decorates the options dictionary so that it understands
|
||||
`g:fzf_layout`, `g:fzf_action`, `g:fzf_colors`, and `g:fzf_history_dir` like
|
||||
`:FZF`.
|
||||
|
||||
```vim
|
||||
command! -bang MyStuff
|
||||
\ call fzf#run(fzf#wrap('my-stuff', {'dir': '~/my-stuff'}, <bang>0))
|
||||
```
|
||||
See [README-VIM.md](README-VIM.md).
|
||||
|
||||
Tips
|
||||
----
|
||||
@@ -450,9 +427,9 @@ export FZF_DEFAULT_COMMAND='
|
||||
#### Fish shell
|
||||
|
||||
It's [a known bug of fish](https://github.com/fish-shell/fish-shell/issues/1362)
|
||||
that it doesn't allow reading from STDIN in command substitution, which means
|
||||
simple `vim (fzf)` won't work as expected. The workaround is to use the `read`
|
||||
fish command:
|
||||
(will be fixed in 2.6.0) that it doesn't allow reading from STDIN in command
|
||||
substitution, which means simple `vim (fzf)` won't work as expected. The
|
||||
workaround is to use the `read` fish command:
|
||||
|
||||
```sh
|
||||
fzf | read -l result; and vim $result
|
||||
|
||||
19
bin/fzf-tmux
19
bin/fzf-tmux
@@ -121,7 +121,7 @@ args+=("--no-height")
|
||||
if tmux list-panes -F '#F' | grep -q Z; then
|
||||
zoomed=1
|
||||
original_window=$(tmux display-message -p "#{window_id}")
|
||||
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - \\\\; do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'")
|
||||
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'")
|
||||
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
|
||||
fi
|
||||
|
||||
@@ -134,7 +134,7 @@ fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
|
||||
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
|
||||
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
|
||||
cleanup() {
|
||||
rm -f $argsf $fifo1 $fifo2 $fifo3
|
||||
\rm -f $argsf $fifo1 $fifo2 $fifo3
|
||||
|
||||
# Remove temp window if we were zoomed
|
||||
if [[ -n "$zoomed" ]]; then
|
||||
@@ -144,8 +144,13 @@ cleanup() {
|
||||
kill-window -t $tmp_window \; \
|
||||
resize-pane -Z
|
||||
fi
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
exit 130
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT SIGINT SIGTERM
|
||||
trap 'cleanup 1' SIGUSR1
|
||||
trap 'cleanup' EXIT
|
||||
|
||||
envs="env TERM=$TERM "
|
||||
[[ -n "$FZF_DEFAULT_OPTS" ]] && envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
|
||||
@@ -164,18 +169,22 @@ for arg in "${args[@]}"; do
|
||||
opts="$opts \"$arg\""
|
||||
done
|
||||
|
||||
pppid=$$
|
||||
trap_set="trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM"
|
||||
trap_unset="trap - EXIT SIGINT SIGTERM"
|
||||
|
||||
if [[ -n "$term" ]] || [[ -t 0 ]]; then
|
||||
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" > $argsf
|
||||
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \;\
|
||||
set-window-option remain-on-exit off \;\
|
||||
split-window $opt "cd $(printf %q "$PWD");$envs bash $argsf" $swap \
|
||||
split-window $opt "$trap_set;cd $(printf %q "$PWD");$envs bash $argsf;$trap_unset" $swap \
|
||||
> /dev/null 2>&1
|
||||
else
|
||||
mkfifo $fifo1
|
||||
cat <<< "\"$fzf\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" > $argsf
|
||||
TMUX=$(echo $TMUX | cut -d , -f 1,2) tmux set-window-option synchronize-panes off \;\
|
||||
set-window-option remain-on-exit off \;\
|
||||
split-window $opt "$envs bash $argsf" $swap \
|
||||
split-window $opt "$trap_set;$envs bash $argsf;$trap_unset" $swap \
|
||||
> /dev/null 2>&1
|
||||
cat <&0 > $fifo1 &
|
||||
fi
|
||||
|
||||
182
doc/fzf.txt
Normal file
182
doc/fzf.txt
Normal file
@@ -0,0 +1,182 @@
|
||||
fzf.txt fzf Last change: April 28 2017
|
||||
FZF - TABLE OF CONTENTS *fzf* *fzf-toc*
|
||||
==============================================================================
|
||||
|
||||
FZF Vim integration
|
||||
:FZF[!]
|
||||
Configuration
|
||||
Examples
|
||||
fzf#run
|
||||
fzf#wrap
|
||||
GVim
|
||||
License
|
||||
|
||||
FZF VIM INTEGRATION *fzf-vim-integration*
|
||||
==============================================================================
|
||||
|
||||
This repository only enables basic integration with Vim. If you're looking for
|
||||
more, check out {fzf.vim}{1} project.
|
||||
|
||||
(Note: To use fzf in GVim, an external terminal emulator is required.)
|
||||
|
||||
{1} https://github.com/junegunn/fzf.vim
|
||||
|
||||
|
||||
:FZF[!]
|
||||
==============================================================================
|
||||
|
||||
*:FZF*
|
||||
|
||||
If you have set up fzf for Vim, `:FZF` command will be added.
|
||||
>
|
||||
" Look for files under current directory
|
||||
:FZF
|
||||
|
||||
" Look for files under your home directory
|
||||
:FZF ~
|
||||
|
||||
" With options
|
||||
:FZF --no-sort --reverse --inline-info /tmp
|
||||
|
||||
" Bang version starts fzf in fullscreen mode
|
||||
:FZF!
|
||||
<
|
||||
Similarly to {ctrlp.vim}{2}, use enter key, CTRL-T, CTRL-X or CTRL-V to open
|
||||
selected files in the current window, in new tabs, in horizontal splits, or in
|
||||
vertical splits respectively.
|
||||
|
||||
Note that the environment variables `FZF_DEFAULT_COMMAND` and
|
||||
`FZF_DEFAULT_OPTS` also apply here.
|
||||
|
||||
{2} https://github.com/kien/ctrlp.vim
|
||||
|
||||
|
||||
< Configuration >_____________________________________________________________~
|
||||
*fzf-configuration*
|
||||
|
||||
*g:fzf_action* *g:fzf_layout* *g:fzf_colors* *g:fzf_history_dir* *g:fzf_launcher*
|
||||
*g:Fzf_launcher*
|
||||
|
||||
- `g:fzf_action`
|
||||
- Customizable extra key bindings for opening selected files in different
|
||||
ways
|
||||
- `g:fzf_layout`
|
||||
- Determines the size and position of fzf window (tmux pane or Neovim split)
|
||||
- `g:fzf_colors`
|
||||
- Customizes fzf colors to match the current color scheme
|
||||
- `g:fzf_history_dir`
|
||||
- Enables history feature
|
||||
- `g:fzf_launcher`
|
||||
- (Only in GVim) Terminal emulator to open fzf with
|
||||
- `g:Fzf_launcher` for function reference
|
||||
|
||||
|
||||
Examples~
|
||||
*fzf-examples*
|
||||
>
|
||||
" This is the default extra key bindings
|
||||
let g:fzf_action = {
|
||||
\ 'ctrl-t': 'tab split',
|
||||
\ 'ctrl-x': 'split',
|
||||
\ 'ctrl-v': 'vsplit' }
|
||||
|
||||
" Default fzf layout
|
||||
" - down / up / left / right
|
||||
let g:fzf_layout = { 'down': '~40%' }
|
||||
|
||||
" In Neovim, you can set up fzf window using a Vim command
|
||||
let g:fzf_layout = { 'window': 'enew' }
|
||||
let g:fzf_layout = { 'window': '-tabnew' }
|
||||
let g:fzf_layout = { 'window': '10split enew' }
|
||||
|
||||
" Customize fzf colors to match your color scheme
|
||||
let g:fzf_colors =
|
||||
\ { 'fg': ['fg', 'Normal'],
|
||||
\ 'bg': ['bg', 'Normal'],
|
||||
\ 'hl': ['fg', 'Comment'],
|
||||
\ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
|
||||
\ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
|
||||
\ 'hl+': ['fg', 'Statement'],
|
||||
\ 'info': ['fg', 'PreProc'],
|
||||
\ 'prompt': ['fg', 'Conditional'],
|
||||
\ 'pointer': ['fg', 'Exception'],
|
||||
\ 'marker': ['fg', 'Keyword'],
|
||||
\ 'spinner': ['fg', 'Label'],
|
||||
\ 'header': ['fg', 'Comment'] }
|
||||
|
||||
" Enable per-command history.
|
||||
" CTRL-N and CTRL-P will be automatically bound to next-history and
|
||||
" previous-history instead of down and up. If you don't like the change,
|
||||
" explicitly bind the keys to down and up in your $FZF_DEFAULT_OPTS.
|
||||
let g:fzf_history_dir = '~/.local/share/fzf-history'
|
||||
<
|
||||
|
||||
FZF#RUN *fzf#run*
|
||||
==============================================================================
|
||||
|
||||
For more advanced uses, you can use `fzf#run([options])` function with the
|
||||
following options.
|
||||
|
||||
---------------------------+---------------+--------------------------------------------------------------
|
||||
Option name | Type | Description ~
|
||||
---------------------------+---------------+--------------------------------------------------------------
|
||||
`source` | string | External command to generate input to fzf (e.g. `find .` )
|
||||
`source` | list | Vim list as input to fzf
|
||||
`sink` | string | Vim command to handle the selected item (e.g. `e` , `tabe` )
|
||||
`sink` | funcref | Reference to function to process each selected item
|
||||
`sink*` | funcref | Similar to `sink` , but takes the list of output lines at once
|
||||
`options` | string/list | Options to fzf
|
||||
`dir` | string | Working directory
|
||||
`up` / `down` / `left` / `right` | number/string | Use tmux pane with the given size (e.g. `20` , `50%` )
|
||||
`window` (Neovim only) | string | Command to open fzf window (e.g. `vertical aboveleft 30new` )
|
||||
`launcher` | string | External terminal emulator to start fzf with (GVim only)
|
||||
`launcher` | funcref | Function for generating `launcher` string (GVim only)
|
||||
---------------------------+---------------+--------------------------------------------------------------
|
||||
|
||||
`options` entry can be either a string or a list. For simple cases, string
|
||||
should suffice, but prefer to use list type if you're concerned about escaping
|
||||
issues on different platforms.
|
||||
>
|
||||
call fzf#run({'options': '--reverse --prompt "C:\\Program Files\\"'})
|
||||
call fzf#run({'options': ['--reverse', '--prompt', 'C:\Program Files\']})
|
||||
<
|
||||
|
||||
FZF#WRAP *fzf#wrap*
|
||||
==============================================================================
|
||||
|
||||
`fzf#wrap([name string,] [opts dict,] [fullscreen boolean])` is a helper
|
||||
function that decorates the options dictionary so that it understands
|
||||
`g:fzf_layout`, `g:fzf_action`, `g:fzf_colors`, and `g:fzf_history_dir` like
|
||||
`:FZF`.
|
||||
>
|
||||
command! -bang MyStuff
|
||||
\ call fzf#run(fzf#wrap('my-stuff', {'dir': '~/my-stuff'}, <bang>0))
|
||||
<
|
||||
|
||||
GVIM *fzf-gvim*
|
||||
==============================================================================
|
||||
|
||||
In GVim, you need an external terminal emulator to start fzf with. `xterm`
|
||||
command is used by default, but you can customize it with `g:fzf_launcher`.
|
||||
>
|
||||
" This is the default. %s is replaced with fzf command
|
||||
let g:fzf_launcher = 'xterm -e bash -ic %s'
|
||||
|
||||
" Use urxvt instead
|
||||
let g:fzf_launcher = 'urxvt -geometry 120x30 -e sh -c %s'
|
||||
<
|
||||
If you're running MacVim on OSX, I recommend you to use iTerm2 as the
|
||||
launcher. Refer to the {this wiki page}{3} to see how to set up.
|
||||
|
||||
{3} https://github.com/junegunn/fzf/wiki/On-MacVim-with-iTerm2
|
||||
|
||||
|
||||
LICENSE *fzf-license*
|
||||
==============================================================================
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Junegunn Choi
|
||||
|
||||
==============================================================================
|
||||
vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
|
||||
38
glide.lock
generated
Normal file
38
glide.lock
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
hash: d68dd0bd779ac4ffca1e0c49ca38d85f90d5d68fa8e2d5d7db70a8ce8c662ec1
|
||||
updated: 2017-06-01T15:48:41.653745249-07:00
|
||||
imports:
|
||||
- name: github.com/gdamore/encoding
|
||||
version: b23993cbb6353f0e6aa98d0ee318a34728f628b9
|
||||
- name: github.com/gdamore/tcell
|
||||
version: 44772c121bb7838819d3ba4a7e84c0c2d617328e
|
||||
subpackages:
|
||||
- encoding
|
||||
- name: github.com/lucasb-eyer/go-colorful
|
||||
version: c900de9dbbc73129068f5af6a823068fc5f2308c
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
|
||||
- name: github.com/mattn/go-runewidth
|
||||
version: 14207d285c6c197daabb5c9793d63e7af9ab2d50
|
||||
- name: github.com/mattn/go-shellwords
|
||||
version: 02e3cf038dcea8290e44424da473dd12be796a8a
|
||||
- name: golang.org/x/crypto
|
||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||
subpackages:
|
||||
- ssh/terminal
|
||||
- name: golang.org/x/sys
|
||||
version: b90f89a1e7a9c1f6b918820b3daa7f08488c8594
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: 4ee4af566555f5fbe026368b75596286a312663a
|
||||
subpackages:
|
||||
- encoding
|
||||
- encoding/charmap
|
||||
- encoding/internal
|
||||
- encoding/internal/identifier
|
||||
- encoding/japanese
|
||||
- encoding/korean
|
||||
- encoding/simplifiedchinese
|
||||
- encoding/traditionalchinese
|
||||
- transform
|
||||
testImports: []
|
||||
16
glide.yaml
Normal file
16
glide.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
package: github.com/junegunn/fzf
|
||||
import:
|
||||
- package: github.com/mattn/go-isatty
|
||||
version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
|
||||
- package: github.com/mattn/go-runewidth
|
||||
version: 14207d285c6c197daabb5c9793d63e7af9ab2d50
|
||||
- package: github.com/mattn/go-shellwords
|
||||
version: 02e3cf038dcea8290e44424da473dd12be796a8a
|
||||
- package: github.com/gdamore/tcell
|
||||
version: 44772c121bb7838819d3ba4a7e84c0c2d617328e
|
||||
subpackages:
|
||||
- encoding
|
||||
- package: golang.org/x/crypto
|
||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||
subpackages:
|
||||
- ssh/terminal
|
||||
72
install
72
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.16.6
|
||||
version=0.16.8
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
@@ -72,7 +72,7 @@ ask() {
|
||||
check_binary() {
|
||||
echo -n " - Checking fzf executable ... "
|
||||
local output
|
||||
output=$("$fzf_base"/bin/fzf --version 2>&1)
|
||||
output=$("$fzf_base"/bin/fzf --version 2>&1 | awk '{print $1}')
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: $output"
|
||||
binary_error="Invalid binary"
|
||||
@@ -99,11 +99,23 @@ link_fzf_in_path() {
|
||||
}
|
||||
|
||||
try_curl() {
|
||||
command -v curl > /dev/null && curl -fL $1 | tar -xz
|
||||
command -v curl > /dev/null &&
|
||||
if [[ $1 =~ tgz$ ]]; then
|
||||
curl -fL $1 | tar -xzf -
|
||||
else
|
||||
local temp=${TMPDIR:-/tmp}/fzf.zip
|
||||
curl -fLo "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
|
||||
fi
|
||||
}
|
||||
|
||||
try_wget() {
|
||||
command -v wget > /dev/null && wget -O - $1 | tar -xz
|
||||
command -v wget > /dev/null &&
|
||||
if [[ $1 =~ tgz$ ]]; then
|
||||
wget -O - $1 | tar -xzf -
|
||||
else
|
||||
local temp=${TMPDIR:-/tmp}/fzf.zip
|
||||
wget -O "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
@@ -123,8 +135,8 @@ download() {
|
||||
|
||||
local url
|
||||
[[ "$version" =~ alpha ]] &&
|
||||
url=https://github.com/junegunn/fzf-bin/releases/download/alpha/${1}.tgz ||
|
||||
url=https://github.com/junegunn/fzf-bin/releases/download/$version/${1}.tgz
|
||||
url=https://github.com/junegunn/fzf-bin/releases/download/alpha/${1} ||
|
||||
url=https://github.com/junegunn/fzf-bin/releases/download/$version/${1}
|
||||
set -o pipefail
|
||||
if ! (try_curl $url || try_wget $url); then
|
||||
set +o pipefail
|
||||
@@ -146,18 +158,19 @@ archi=$(uname -sm)
|
||||
binary_available=1
|
||||
binary_error=""
|
||||
case "$archi" in
|
||||
Darwin\ *64) download fzf-$version-darwin_${binary_arch:-amd64} ;;
|
||||
Darwin\ *86) download fzf-$version-darwin_${binary_arch:-386} ;;
|
||||
Linux\ *64) download fzf-$version-linux_${binary_arch:-amd64} ;;
|
||||
Linux\ *86) download fzf-$version-linux_${binary_arch:-386} ;;
|
||||
Linux\ armv5*) download fzf-$version-linux_${binary_arch:-arm5} ;;
|
||||
Linux\ armv6*) download fzf-$version-linux_${binary_arch:-arm6} ;;
|
||||
Linux\ armv7*) download fzf-$version-linux_${binary_arch:-arm7} ;;
|
||||
Linux\ armv8*) download fzf-$version-linux_${binary_arch:-arm8} ;;
|
||||
FreeBSD\ *64) download fzf-$version-freebsd_${binary_arch:-amd64} ;;
|
||||
FreeBSD\ *86) download fzf-$version-freebsd_${binary_arch:-386} ;;
|
||||
OpenBSD\ *64) download fzf-$version-openbsd_${binary_arch:-amd64} ;;
|
||||
OpenBSD\ *86) download fzf-$version-openbsd_${binary_arch:-386} ;;
|
||||
Darwin\ *64) download fzf-$version-darwin_${binary_arch:-amd64}.tgz ;;
|
||||
Darwin\ *86) download fzf-$version-darwin_${binary_arch:-386}.tgz ;;
|
||||
Linux\ *64) download fzf-$version-linux_${binary_arch:-amd64}.tgz ;;
|
||||
Linux\ *86) download fzf-$version-linux_${binary_arch:-386}.tgz ;;
|
||||
Linux\ armv5*) download fzf-$version-linux_${binary_arch:-arm5}.tgz ;;
|
||||
Linux\ armv6*) download fzf-$version-linux_${binary_arch:-arm6}.tgz ;;
|
||||
Linux\ armv7*) download fzf-$version-linux_${binary_arch:-arm7}.tgz ;;
|
||||
Linux\ armv8*) download fzf-$version-linux_${binary_arch:-arm8}.tgz ;;
|
||||
FreeBSD\ *64) download fzf-$version-freebsd_${binary_arch:-amd64}.tgz ;;
|
||||
FreeBSD\ *86) download fzf-$version-freebsd_${binary_arch:-386}.tgz ;;
|
||||
OpenBSD\ *64) download fzf-$version-openbsd_${binary_arch:-amd64}.tgz ;;
|
||||
OpenBSD\ *86) download fzf-$version-openbsd_${binary_arch:-386}.tgz ;;
|
||||
CYGWIN*\ *64) download fzf-$version-windows_${binary_arch:-amd64}.zip ;;
|
||||
*) binary_available=0 binary_error=1 ;;
|
||||
esac
|
||||
|
||||
@@ -169,12 +182,12 @@ if [ -n "$binary_error" ]; then
|
||||
echo " - $binary_error !!!"
|
||||
fi
|
||||
if command -v go > /dev/null; then
|
||||
echo -n "Building binary (go get -u github.com/junegunn/fzf/src/fzf) ... "
|
||||
echo -n "Building binary (go get -u github.com/junegunn/fzf) ... "
|
||||
if [ -z "${GOPATH-}" ]; then
|
||||
export GOPATH="${TMPDIR:-/tmp}/fzf-gopath"
|
||||
mkdir -p "$GOPATH"
|
||||
fi
|
||||
if go get -u github.com/junegunn/fzf/src/fzf; then
|
||||
if go get -u github.com/junegunn/fzf; then
|
||||
echo "OK"
|
||||
cp "$GOPATH/bin/fzf" "$fzf_base/bin/"
|
||||
else
|
||||
@@ -297,6 +310,17 @@ append_line() {
|
||||
set +e
|
||||
}
|
||||
|
||||
create_file() {
|
||||
local file="$1"
|
||||
shift
|
||||
echo "Create $file:"
|
||||
for line in "$@"; do
|
||||
echo " $line"
|
||||
echo "$line" >> "$file"
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
if [ $update_config -eq 2 ]; then
|
||||
echo
|
||||
ask "Do you want to update your shell configuration files?"
|
||||
@@ -310,8 +334,15 @@ done
|
||||
|
||||
if [ $key_bindings -eq 1 ] && [ $has_fish -eq 1 ]; then
|
||||
bind_file=~/.config/fish/functions/fish_user_key_bindings.fish
|
||||
if [ ! -e "$bind_file" ]; then
|
||||
create_file "$bind_file" \
|
||||
'function fish_user_key_bindings' \
|
||||
' fzf_key_bindings' \
|
||||
'end'
|
||||
else
|
||||
append_line $update_config "fzf_key_bindings" "$bind_file"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $update_config -eq 1 ]; then
|
||||
echo 'Finished. Restart your shell or reload config file.'
|
||||
@@ -323,4 +354,3 @@ if [ $update_config -eq 1 ]; then
|
||||
echo
|
||||
fi
|
||||
echo 'For more information, see: https://github.com/junegunn/fzf'
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package main
|
||||
|
||||
import "github.com/junegunn/fzf/src"
|
||||
|
||||
var revision string
|
||||
|
||||
func main() {
|
||||
fzf.Run(fzf.ParseOptions())
|
||||
fzf.Run(fzf.ParseOptions(), revision)
|
||||
}
|
||||
@@ -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-tmux 1 "Mar 2017" "fzf 0.16.6" "fzf-tmux - open fzf in tmux split pane"
|
||||
.TH fzf-tmux 1 "Jun 2017" "fzf 0.16.8" "fzf-tmux - open fzf in tmux split pane"
|
||||
|
||||
.SH NAME
|
||||
fzf-tmux - open fzf in tmux split pane
|
||||
|
||||
@@ -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 "Mar 2017" "fzf 0.16.6" "fzf - a command-line fuzzy finder"
|
||||
.TH fzf 1 "Jun 2017" "fzf 0.16.8" "fzf - a command-line fuzzy finder"
|
||||
|
||||
.SH NAME
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -352,6 +352,9 @@ ncurses finder only after the input stream is complete.
|
||||
.RS
|
||||
e.g. \fBfzf --multi | fzf --sync\fR
|
||||
.RE
|
||||
.TP
|
||||
.B "--version"
|
||||
Display version information and exit
|
||||
|
||||
.SH ENVIRONMENT VARIABLES
|
||||
.TP
|
||||
@@ -436,6 +439,7 @@ e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
|
||||
.B AVAILABLE KEYS: (SYNONYMS)
|
||||
\fIctrl-[a-z]\fR
|
||||
\fIctrl-space\fR
|
||||
\fIctrl-alt-[a-z]\fR
|
||||
\fIalt-[a-z]\fR
|
||||
\fIalt-[0-9]\fR
|
||||
\fIf[1-12]\fR
|
||||
@@ -463,6 +467,11 @@ e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
|
||||
\fIdouble-click\fR
|
||||
or any single character
|
||||
|
||||
Additionally, a special event named \fIchange\fR is available which is
|
||||
triggered whenever the query string is changed.
|
||||
|
||||
e.g. \fBfzf --bind change:top\fR
|
||||
|
||||
\fBACTION: DEFAULT BINDINGS (NOTES):
|
||||
\fBabort\fR \fIctrl-c ctrl-g ctrl-q esc\fR
|
||||
\fBaccept\fR \fIenter double-click\fR
|
||||
@@ -509,6 +518,7 @@ e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
|
||||
\fBtoggle-preview-wrap\fR
|
||||
\fBtoggle-sort\fR
|
||||
\fBtoggle+up\fR \fIbtab (shift-tab)\fR
|
||||
\fBtop\fR (move to the top result)
|
||||
\fBunix-line-discard\fR \fIctrl-u\fR
|
||||
\fBunix-word-rubout\fR \fIctrl-w\fR
|
||||
\fBup\fR \fIctrl-k ctrl-p up\fR
|
||||
|
||||
197
plugin/fzf.vim
197
plugin/fzf.vim
@@ -26,10 +26,76 @@ if exists('g:loaded_fzf')
|
||||
endif
|
||||
let g:loaded_fzf = 1
|
||||
|
||||
let s:is_win = has('win32') || has('win64')
|
||||
if s:is_win && &shellslash
|
||||
set noshellslash
|
||||
let s:base_dir = expand('<sfile>:h:h')
|
||||
set shellslash
|
||||
else
|
||||
let s:base_dir = expand('<sfile>:h:h')
|
||||
endif
|
||||
if s:is_win
|
||||
function! s:fzf_call(fn, ...)
|
||||
let shellslash = &shellslash
|
||||
try
|
||||
set noshellslash
|
||||
return call(a:fn, a:000)
|
||||
finally
|
||||
let &shellslash = shellslash
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Use utf-8 for fzf.vim commands
|
||||
" Return array of shell commands for cmd.exe
|
||||
function! s:wrap_cmds(cmds)
|
||||
return ['@echo off', 'for /f "tokens=4" %%a in (''chcp'') do set origchcp=%%a', 'chcp 65001 > nul'] +
|
||||
\ (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) +
|
||||
\ ['chcp %origchcp% > nul']
|
||||
endfunction
|
||||
else
|
||||
function! s:fzf_call(fn, ...)
|
||||
return call(a:fn, a:000)
|
||||
endfunction
|
||||
|
||||
function! s:wrap_cmds(cmds)
|
||||
return a:cmds
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:shellesc_cmd(arg)
|
||||
let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g')
|
||||
let escaped = substitute(escaped, '%', '%%', 'g')
|
||||
let escaped = substitute(escaped, '"', '\\^&', 'g')
|
||||
let escaped = substitute(escaped, '\\\+\(\\^\)', '\\\\\1', 'g')
|
||||
return '^"'.substitute(escaped, '[^\\]\zs\\$', '\\\\', '').'^"'
|
||||
endfunction
|
||||
|
||||
function! fzf#shellescape(arg, ...)
|
||||
let shell = get(a:000, 0, &shell)
|
||||
if shell =~# 'cmd.exe$'
|
||||
return s:shellesc_cmd(a:arg)
|
||||
endif
|
||||
return s:fzf_call('shellescape', a:arg)
|
||||
endfunction
|
||||
|
||||
function! s:fzf_getcwd()
|
||||
return s:fzf_call('getcwd')
|
||||
endfunction
|
||||
|
||||
function! s:fzf_fnamemodify(fname, mods)
|
||||
return s:fzf_call('fnamemodify', a:fname, a:mods)
|
||||
endfunction
|
||||
|
||||
function! s:fzf_expand(fmt)
|
||||
return s:fzf_call('expand', a:fmt, 1)
|
||||
endfunction
|
||||
|
||||
function! s:fzf_tempname()
|
||||
return s:fzf_call('tempname')
|
||||
endfunction
|
||||
|
||||
let s:default_layout = { 'down': '~40%' }
|
||||
let s:layout_keys = ['window', 'up', 'down', 'left', 'right']
|
||||
let s:is_win = has('win32') || has('win64')
|
||||
let s:base_dir = expand('<sfile>:h:h')
|
||||
let s:fzf_go = s:base_dir.'/bin/fzf'
|
||||
let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
|
||||
let s:install = s:base_dir.'/install'
|
||||
@@ -44,7 +110,7 @@ function! s:fzf_exec()
|
||||
let s:exec = s:fzf_go
|
||||
elseif executable('fzf')
|
||||
let s:exec = 'fzf'
|
||||
elseif s:is_win
|
||||
elseif s:is_win && !has('win32unix')
|
||||
call s:warn('fzf executable not found.')
|
||||
call s:warn('Download fzf binary for Windows from https://github.com/junegunn/fzf-bin/releases/')
|
||||
call s:warn('and place it as '.s:base_dir.'\bin\fzf.exe')
|
||||
@@ -62,7 +128,7 @@ function! s:fzf_exec()
|
||||
throw 'fzf executable not found'
|
||||
endif
|
||||
endif
|
||||
return s:shellesc(s:exec)
|
||||
return fzf#shellescape(s:exec)
|
||||
endfunction
|
||||
|
||||
function! s:tmux_enabled()
|
||||
@@ -82,10 +148,6 @@ function! s:tmux_enabled()
|
||||
return s:tmux
|
||||
endfunction
|
||||
|
||||
function! s:shellesc(arg)
|
||||
return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
|
||||
endfunction
|
||||
|
||||
function! s:escape(path)
|
||||
let escaped_chars = '$%#''"'
|
||||
|
||||
@@ -133,7 +195,7 @@ function! s:has_any(dict, keys)
|
||||
endfunction
|
||||
|
||||
function! s:open(cmd, target)
|
||||
if stridx('edit', a:cmd) == 0 && fnamemodify(a:target, ':p') ==# expand('%:p')
|
||||
if stridx('edit', a:cmd) == 0 && s:fzf_fnamemodify(a:target, ':p') ==# s:fzf_expand('%:p')
|
||||
return
|
||||
endif
|
||||
execute a:cmd s:escape(a:target)
|
||||
@@ -148,11 +210,11 @@ function! s:common_sink(action, lines) abort
|
||||
if len(a:lines) > 1
|
||||
augroup fzf_swap
|
||||
autocmd SwapExists * let v:swapchoice='o'
|
||||
\| call s:warn('fzf: E325: swap file exists: '.expand('<afile>'))
|
||||
\| call s:warn('fzf: E325: swap file exists: '.s:fzf_expand('<afile>'))
|
||||
augroup END
|
||||
endif
|
||||
try
|
||||
let empty = empty(expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
||||
let empty = empty(s:fzf_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified
|
||||
let autochdir = &autochdir
|
||||
set noautochdir
|
||||
for item in a:lines
|
||||
@@ -202,6 +264,11 @@ function! s:validate_layout(layout)
|
||||
return a:layout
|
||||
endfunction
|
||||
|
||||
function! s:evaluate_opts(options)
|
||||
return type(a:options) == type([]) ?
|
||||
\ join(map(copy(a:options), 'fzf#shellescape(v:val)')) : a:options
|
||||
endfunction
|
||||
|
||||
" [name string,] [opts dict,] [fullscreen boolean]
|
||||
function! fzf#wrap(...)
|
||||
let args = ['', {}, 0]
|
||||
@@ -238,15 +305,16 @@ function! fzf#wrap(...)
|
||||
endif
|
||||
|
||||
" Colors: g:fzf_colors
|
||||
let opts.options = s:defaults() .' '. get(opts, 'options', '')
|
||||
let opts.options = s:defaults() .' '. s:evaluate_opts(get(opts, 'options', ''))
|
||||
|
||||
" History: g:fzf_history_dir
|
||||
if len(name) && len(get(g:, 'fzf_history_dir', ''))
|
||||
let dir = expand(g:fzf_history_dir)
|
||||
let dir = s:fzf_expand(g:fzf_history_dir)
|
||||
if !isdirectory(dir)
|
||||
call mkdir(dir, 'p')
|
||||
endif
|
||||
let opts.options = join(['--history', s:escape(dir.'/'.name), opts.options])
|
||||
let history = fzf#shellescape(dir.'/'.name)
|
||||
let opts.options = join(['--history', history, opts.options])
|
||||
endif
|
||||
|
||||
" Action: g:fzf_action
|
||||
@@ -262,19 +330,6 @@ function! fzf#wrap(...)
|
||||
return opts
|
||||
endfunction
|
||||
|
||||
function! fzf#shellescape(path)
|
||||
if s:is_win
|
||||
let shellslash = &shellslash
|
||||
try
|
||||
set noshellslash
|
||||
return shellescape(a:path)
|
||||
finally
|
||||
let &shellslash = shellslash
|
||||
endtry
|
||||
endif
|
||||
return shellescape(a:path)
|
||||
endfunction
|
||||
|
||||
function! fzf#run(...) abort
|
||||
try
|
||||
let oshell = &shell
|
||||
@@ -287,13 +342,16 @@ try
|
||||
set shell=sh
|
||||
endif
|
||||
|
||||
if has('nvim') && len(filter(range(1, bufnr('$')), 'bufname(v:val) =~# ";#FZF"'))
|
||||
call s:warn('FZF is already running!')
|
||||
if has('nvim')
|
||||
let running = filter(range(1, bufnr('$')), "bufname(v:val) =~# ';#FZF'")
|
||||
if len(running)
|
||||
call s:warn('FZF is already running (in buffer '.join(running, ', ').')!')
|
||||
return []
|
||||
endif
|
||||
endif
|
||||
let dict = exists('a:1') ? s:upgrade(a:1) : {}
|
||||
let temps = { 'result': tempname() }
|
||||
let optstr = get(dict, 'options', '')
|
||||
let temps = { 'result': s:fzf_tempname() }
|
||||
let optstr = s:evaluate_opts(get(dict, 'options', ''))
|
||||
try
|
||||
let fzf_exec = s:fzf_exec()
|
||||
catch
|
||||
@@ -301,13 +359,13 @@ try
|
||||
endtry
|
||||
|
||||
if has('nvim') && !has_key(dict, 'dir')
|
||||
let dict.dir = getcwd()
|
||||
let dict.dir = s:fzf_getcwd()
|
||||
endif
|
||||
|
||||
if !has_key(dict, 'source') && !empty($FZF_DEFAULT_COMMAND)
|
||||
let temps.source = tempname().(s:is_win ? '.bat' : '')
|
||||
call writefile((s:is_win ? ['@echo off'] : []) + split($FZF_DEFAULT_COMMAND, "\n"), temps.source)
|
||||
let dict.source = (empty($SHELL) ? &shell : $SHELL) . (s:is_win ? ' /c ' : ' ') . s:shellesc(temps.source)
|
||||
let temps.source = s:fzf_tempname().(s:is_win ? '.bat' : '')
|
||||
call writefile(s:wrap_cmds(split($FZF_DEFAULT_COMMAND, "\n")), temps.source)
|
||||
let dict.source = (empty($SHELL) ? &shell : $SHELL) . (s:is_win ? ' /c ' : ' ') . fzf#shellescape(temps.source)
|
||||
endif
|
||||
|
||||
if has_key(dict, 'source')
|
||||
@@ -316,9 +374,9 @@ try
|
||||
if type == 1
|
||||
let prefix = source.'|'
|
||||
elseif type == 3
|
||||
let temps.input = tempname()
|
||||
let temps.input = s:fzf_tempname()
|
||||
call writefile(source, temps.input)
|
||||
let prefix = (s:is_win ? 'type ' : 'cat ').s:shellesc(temps.input).'|'
|
||||
let prefix = (s:is_win ? 'type ' : 'cat ').fzf#shellescape(temps.input).'|'
|
||||
else
|
||||
throw 'Invalid source type'
|
||||
endif
|
||||
@@ -328,16 +386,17 @@ try
|
||||
|
||||
let prefer_tmux = get(g:, 'fzf_prefer_tmux', 0)
|
||||
let use_height = has_key(dict, 'down') &&
|
||||
\ !(has('nvim') || s:is_win || s:present(dict, 'up', 'left', 'right')) &&
|
||||
\ !(has('nvim') || s:is_win || has('win32unix') || s:present(dict, 'up', 'left', 'right')) &&
|
||||
\ executable('tput') && filereadable('/dev/tty')
|
||||
let use_term = has('nvim')
|
||||
let use_tmux = (!use_height && !use_term || prefer_tmux) && s:tmux_enabled() && s:splittable(dict)
|
||||
let use_term = has('nvim') && !s:is_win
|
||||
let use_tmux = (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:tmux_enabled() && s:splittable(dict)
|
||||
if prefer_tmux && use_tmux
|
||||
let use_height = 0
|
||||
let use_term = 0
|
||||
endif
|
||||
if use_height
|
||||
let optstr .= ' --height='.s:calc_size(&lines, dict.down, dict)
|
||||
let height = s:calc_size(&lines, dict.down, dict)
|
||||
let optstr .= ' --height='.height
|
||||
elseif use_term
|
||||
let optstr .= ' --no-height'
|
||||
endif
|
||||
@@ -381,7 +440,7 @@ function! s:fzf_tmux(dict)
|
||||
endif
|
||||
endfor
|
||||
return printf('LINES=%d COLUMNS=%d %s %s %s --',
|
||||
\ &lines, &columns, s:shellesc(s:fzf_tmux), size, (has_key(a:dict, 'source') ? '' : '-'))
|
||||
\ &lines, &columns, fzf#shellescape(s:fzf_tmux), size, (has_key(a:dict, 'source') ? '' : '-'))
|
||||
endfunction
|
||||
|
||||
function! s:splittable(dict)
|
||||
@@ -391,13 +450,13 @@ endfunction
|
||||
|
||||
function! s:pushd(dict)
|
||||
if s:present(a:dict, 'dir')
|
||||
let cwd = getcwd()
|
||||
let cwd = s:fzf_getcwd()
|
||||
if get(a:dict, 'prev_dir', '') ==# cwd
|
||||
return 1
|
||||
endif
|
||||
let a:dict.prev_dir = cwd
|
||||
execute 'lcd' s:escape(a:dict.dir)
|
||||
let a:dict.dir = getcwd()
|
||||
let a:dict.dir = s:fzf_getcwd()
|
||||
return 1
|
||||
endif
|
||||
return 0
|
||||
@@ -426,7 +485,7 @@ function! s:xterm_launcher()
|
||||
\ &columns, &lines/2, getwinposx(), getwinposy())
|
||||
endfunction
|
||||
unlet! s:launcher
|
||||
if s:is_win
|
||||
if s:is_win || has('win32unix')
|
||||
let s:launcher = '%s'
|
||||
else
|
||||
let s:launcher = function('s:xterm_launcher')
|
||||
@@ -450,7 +509,7 @@ function! s:execute(dict, command, use_height, temps) abort
|
||||
if has('unix') && !a:use_height
|
||||
silent! !clear 2> /dev/null
|
||||
endif
|
||||
let escaped = escape(substitute(a:command, '\n', '\\n', 'g'), '%#!')
|
||||
let escaped = (a:use_height || s:is_win) ? a:command : escape(substitute(a:command, '\n', '\\n', 'g'), '%#!')
|
||||
if has('gui_running')
|
||||
let Launcher = get(a:dict, 'launcher', get(g:, 'Fzf_launcher', get(g:, 'fzf_launcher', s:launcher)))
|
||||
let fmt = type(Launcher) == 2 ? call(Launcher, []) : Launcher
|
||||
@@ -459,7 +518,30 @@ function! s:execute(dict, command, use_height, temps) abort
|
||||
endif
|
||||
let command = printf(fmt, escaped)
|
||||
else
|
||||
let command = a:use_height ? a:command : escaped
|
||||
let command = escaped
|
||||
endif
|
||||
if s:is_win
|
||||
let batchfile = s:fzf_tempname().'.bat'
|
||||
call writefile(s:wrap_cmds(command), batchfile)
|
||||
let command = batchfile
|
||||
let a:temps.batchfile = batchfile
|
||||
if has('nvim')
|
||||
let s:dict = a:dict
|
||||
let s:temps = a:temps
|
||||
let fzf = {}
|
||||
function! fzf.on_exit(job_id, exit_status, event) dict
|
||||
let lines = s:collect(s:temps)
|
||||
call s:callback(s:dict, lines)
|
||||
endfunction
|
||||
let cmd = 'start /wait cmd /c '.command
|
||||
call jobstart(cmd, fzf)
|
||||
return []
|
||||
endif
|
||||
elseif has('win32unix') && $TERM !=# 'cygwin'
|
||||
let shellscript = s:fzf_tempname()
|
||||
call writefile([command], shellscript)
|
||||
let command = 'cmd.exe /C '.fzf#shellescape('set "TERM=" & start /WAIT sh -c '.shellscript)
|
||||
let a:temps.shellscript = shellscript
|
||||
endif
|
||||
if a:use_height
|
||||
let stdin = has_key(a:dict, 'source') ? '' : '< /dev/tty'
|
||||
@@ -476,7 +558,7 @@ function! s:execute_tmux(dict, command, temps) abort
|
||||
let command = a:command
|
||||
if s:pushd(a:dict)
|
||||
" -c '#{pane_current_path}' is only available on tmux 1.9 or above
|
||||
let command = 'cd '.s:escape(a:dict.dir).' && '.command
|
||||
let command = join(['cd', fzf#shellescape(a:dict.dir), '&&', command])
|
||||
endif
|
||||
|
||||
call system(command)
|
||||
@@ -672,19 +754,26 @@ let s:default_action = {
|
||||
|
||||
function! s:shortpath()
|
||||
let short = pathshorten(fnamemodify(getcwd(), ':~:.'))
|
||||
return empty(short) ? '~/' : short . (short =~ '/$' ? '' : '/')
|
||||
let slash = (s:is_win && !&shellslash) ? '\' : '/'
|
||||
return empty(short) ? '~'.slash : short . (short =~ slash.'$' ? '' : slash)
|
||||
endfunction
|
||||
|
||||
function! s:cmd(bang, ...) abort
|
||||
let args = copy(a:000)
|
||||
let opts = { 'options': '--multi ' }
|
||||
let opts = { 'options': ['--multi'] }
|
||||
if len(args) && isdirectory(expand(args[-1]))
|
||||
let opts.dir = substitute(substitute(remove(args, -1), '\\\(["'']\)', '\1', 'g'), '[/\\]*$', '/', '')
|
||||
let opts.options .= ' --prompt '.fzf#shellescape(opts.dir)
|
||||
else
|
||||
let opts.options .= ' --prompt '.fzf#shellescape(s:shortpath())
|
||||
if s:is_win && !&shellslash
|
||||
let opts.dir = substitute(opts.dir, '/', '\\', 'g')
|
||||
elseif has('win32unix')
|
||||
let opts.dir = fnamemodify(opts.dir, ':p')
|
||||
endif
|
||||
let opts.options .= ' '.join(args)
|
||||
let prompt = opts.dir
|
||||
else
|
||||
let prompt = s:shortpath()
|
||||
endif
|
||||
call extend(opts.options, ['--prompt', prompt])
|
||||
call extend(opts.options, args)
|
||||
call fzf#run(fzf#wrap('FZF', opts, a:bang))
|
||||
endfunction
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ _fzf_complete_telnet() {
|
||||
_fzf_complete_ssh() {
|
||||
_fzf_complete '+m' "$@" < <(
|
||||
cat <(cat ~/.ssh/config /etc/ssh/ssh_config 2> /dev/null | command grep -i '^host' | command grep -v '*') \
|
||||
<(command grep -oE '^[a-z0-9.,-]+' ~/.ssh/known_hosts | tr ',' '\n' | awk '{ print $1 " " $1 }') \
|
||||
<(command grep -oE '^[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | awk '{ print $1 " " $1 }') \
|
||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||
)
|
||||
@@ -304,7 +304,7 @@ done
|
||||
|
||||
# Directory
|
||||
for cmd in $d_cmds; do
|
||||
_fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o plusdirs"
|
||||
_fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
||||
done
|
||||
|
||||
unset _fzf_defc
|
||||
|
||||
@@ -117,7 +117,7 @@ _fzf_complete_telnet() {
|
||||
_fzf_complete_ssh() {
|
||||
_fzf_complete '+m' "$@" < <(
|
||||
command cat <(cat ~/.ssh/config /etc/ssh/ssh_config 2> /dev/null | command grep -i '^host' | command grep -v '*') \
|
||||
<(command grep -oE '^[a-z0-9.,-]+' ~/.ssh/known_hosts | tr ',' '\n' | awk '{ print $1 " " $1 }') \
|
||||
<(command grep -oE '^[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | awk '{ print $1 " " $1 }') \
|
||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||
)
|
||||
|
||||
127
src/Makefile
127
src/Makefile
@@ -1,127 +0,0 @@
|
||||
ifndef GOOS
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
GOOS := darwin
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
GOOS := linux
|
||||
else
|
||||
$(error "$$GOOS is not defined.")
|
||||
endif
|
||||
endif
|
||||
|
||||
SOURCES := $(wildcard *.go */*.go)
|
||||
ROOTDIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
BINDIR := $(shell dirname $(ROOTDIR))/bin
|
||||
GOPATH := $(shell dirname $(ROOTDIR))/gopath
|
||||
SRCDIR := $(GOPATH)/src/github.com/junegunn/fzf/src
|
||||
BINARY32 := fzf-$(GOOS)_386
|
||||
BINARY64 := fzf-$(GOOS)_amd64
|
||||
BINARYARM5 := fzf-$(GOOS)_arm5
|
||||
BINARYARM6 := fzf-$(GOOS)_arm6
|
||||
BINARYARM7 := fzf-$(GOOS)_arm7
|
||||
BINARYARM8 := fzf-$(GOOS)_arm8
|
||||
VERSION := $(shell awk -F= '/version =/ {print $$2}' constants.go | tr -d "\" ")
|
||||
RELEASE32 := fzf-$(VERSION)-$(GOOS)_386
|
||||
RELEASE64 := fzf-$(VERSION)-$(GOOS)_amd64
|
||||
RELEASEARM5 := fzf-$(VERSION)-$(GOOS)_arm5
|
||||
RELEASEARM6 := fzf-$(VERSION)-$(GOOS)_arm6
|
||||
RELEASEARM7 := fzf-$(VERSION)-$(GOOS)_arm7
|
||||
RELEASEARM8 := fzf-$(VERSION)-$(GOOS)_arm8
|
||||
export GOPATH
|
||||
|
||||
# https://en.wikipedia.org/wiki/Uname
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
BINARY := $(BINARY64)
|
||||
else ifeq ($(UNAME_M),amd64)
|
||||
BINARY := $(BINARY64)
|
||||
else ifeq ($(UNAME_M),i686)
|
||||
BINARY := $(BINARY32)
|
||||
else ifeq ($(UNAME_M),i386)
|
||||
BINARY := $(BINARY32)
|
||||
else ifeq ($(UNAME_M),armv5l)
|
||||
BINARY := $(BINARYARM5)
|
||||
else ifeq ($(UNAME_M),armv6l)
|
||||
BINARY := $(BINARYARM6)
|
||||
else ifeq ($(UNAME_M),armv7l)
|
||||
BINARY := $(BINARYARM7)
|
||||
else
|
||||
$(error "Build on $(UNAME_M) is not supported, yet.")
|
||||
endif
|
||||
|
||||
all: fzf/$(BINARY)
|
||||
|
||||
ifeq ($(GOOS),windows)
|
||||
release: fzf/$(BINARY32) fzf/$(BINARY64)
|
||||
cd fzf && cp -f $(BINARY32) fzf.exe && zip $(RELEASE32).zip fzf.exe
|
||||
cd fzf && cp -f $(BINARY64) fzf.exe && zip $(RELEASE64).zip fzf.exe
|
||||
cd fzf && rm -f fzf.exe
|
||||
else ifeq ($(GOOS),linux)
|
||||
release: fzf/$(BINARY32) fzf/$(BINARY64) fzf/$(BINARYARM5) fzf/$(BINARYARM6) fzf/$(BINARYARM7) fzf/$(BINARYARM8)
|
||||
cd fzf && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
|
||||
cd fzf && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
||||
cd fzf && cp -f $(BINARYARM5) fzf && tar -czf $(RELEASEARM5).tgz fzf
|
||||
cd fzf && cp -f $(BINARYARM6) fzf && tar -czf $(RELEASEARM6).tgz fzf
|
||||
cd fzf && cp -f $(BINARYARM7) fzf && tar -czf $(RELEASEARM7).tgz fzf
|
||||
cd fzf && cp -f $(BINARYARM8) fzf && tar -czf $(RELEASEARM8).tgz fzf
|
||||
cd fzf && rm -f fzf
|
||||
else
|
||||
release: fzf/$(BINARY32) fzf/$(BINARY64)
|
||||
cd fzf && cp -f $(BINARY32) fzf && tar -czf $(RELEASE32).tgz fzf
|
||||
cd fzf && cp -f $(BINARY64) fzf && tar -czf $(RELEASE64).tgz fzf
|
||||
cd fzf && rm -f fzf
|
||||
endif
|
||||
|
||||
release-all: clean test
|
||||
GOOS=darwin make release
|
||||
GOOS=linux make release
|
||||
GOOS=freebsd make release
|
||||
GOOS=openbsd make release
|
||||
GOOS=windows make release
|
||||
|
||||
$(SRCDIR):
|
||||
mkdir -p $(shell dirname $(SRCDIR))
|
||||
ln -s $(ROOTDIR) $(SRCDIR)
|
||||
|
||||
deps: $(SRCDIR) $(SOURCES)
|
||||
cd $(SRCDIR) && go get -tags "$(TAGS)"
|
||||
./deps
|
||||
|
||||
test: deps
|
||||
SHELL=/bin/sh GOOS= go test -v -tags "$(TAGS)" ./...
|
||||
|
||||
install: $(BINDIR)/fzf
|
||||
|
||||
uninstall:
|
||||
rm -f $(BINDIR)/fzf $(BINDIR)/$(BINARY)
|
||||
|
||||
clean:
|
||||
cd fzf && rm -f fzf-*
|
||||
|
||||
fzf/$(BINARY32): deps
|
||||
cd fzf && GOARCH=386 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARY32)
|
||||
|
||||
fzf/$(BINARY64): deps
|
||||
cd fzf && GOARCH=amd64 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARY64)
|
||||
|
||||
# https://github.com/golang/go/wiki/GoArm
|
||||
fzf/$(BINARYARM5): deps
|
||||
cd fzf && GOARCH=arm GOARM=5 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARYARM5)
|
||||
|
||||
fzf/$(BINARYARM6): deps
|
||||
cd fzf && GOARCH=arm GOARM=6 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARYARM6)
|
||||
|
||||
fzf/$(BINARYARM7): deps
|
||||
cd fzf && GOARCH=arm GOARM=7 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARYARM7)
|
||||
|
||||
fzf/$(BINARYARM8): deps
|
||||
cd fzf && GOARCH=arm64 go build -a -ldflags "-w -extldflags=$(LDFLAGS)" -tags "$(TAGS)" -o $(BINARYARM8)
|
||||
|
||||
$(BINDIR)/fzf: fzf/$(BINARY) | $(BINDIR)
|
||||
cp -f fzf/$(BINARY) $(BINDIR)
|
||||
cd $(BINDIR) && ln -sf $(BINARY) fzf
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $@
|
||||
|
||||
.PHONY: all deps release release-all test install uninstall clean
|
||||
106
src/README.md
106
src/README.md
@@ -1,106 +0,0 @@
|
||||
fzf in Go
|
||||
=========
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/700826/5725028/028ea834-9b93-11e4-9198-43088c3f295d.gif" height="463" alt="fzf in go">
|
||||
|
||||
This directory contains the source code for the new fzf implementation in
|
||||
[Go][go].
|
||||
|
||||
Upgrade from Ruby version
|
||||
-------------------------
|
||||
|
||||
The install script has been updated to download the right binary for your
|
||||
system. If you already have installed fzf, simply git-pull the repository and
|
||||
rerun the install script.
|
||||
|
||||
```sh
|
||||
cd ~/.fzf
|
||||
git pull
|
||||
./install
|
||||
```
|
||||
|
||||
Otherwise, follow [the instruction][install] as before. You can also install
|
||||
fzf using Homebrew if you prefer that way.
|
||||
|
||||
Motivations
|
||||
-----------
|
||||
|
||||
### No Ruby dependency
|
||||
|
||||
There have always been complaints about fzf being a Ruby script. To make
|
||||
matters worse, Ruby 2.1 removed ncurses binding from its standard libary.
|
||||
Because of the change, users running Ruby 2.1 or above are forced to build C
|
||||
extensions of curses gem to meet the requirement of fzf. The new Go version
|
||||
will be distributed as an executable binary so it will be much more accessible
|
||||
and should be easier to setup.
|
||||
|
||||
### Performance
|
||||
|
||||
Many people have been surprised to see how fast fzf is even when it was
|
||||
written in Ruby. It stays quite responsive even for 100k+ lines, which is
|
||||
well above the size of the usual input.
|
||||
|
||||
The new Go version, of course, is significantly faster than that. It has all
|
||||
the performance optimization techniques used in Ruby implementation and more.
|
||||
It also doesn't suffer from [GIL][gil], so the search performance scales
|
||||
proportional to the number of CPU cores. On my MacBook Pro (Mid 2012), the new
|
||||
version was shown to be an order of magnitude faster on certain cases. It also
|
||||
starts much faster though the difference may not be noticeable.
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
See [BUILD.md](../BUILD.md)
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
Unit tests can be run with `make test`. Integration tests are written in Ruby
|
||||
script that should be run on tmux.
|
||||
|
||||
```sh
|
||||
cd src
|
||||
|
||||
# Unit tests
|
||||
make test
|
||||
|
||||
# Integration tests
|
||||
ruby ../test/test_go.rb
|
||||
|
||||
# Build binary for the platform
|
||||
make
|
||||
|
||||
# Install the executable to ../bin directory
|
||||
make install
|
||||
|
||||
# Make release archives
|
||||
make release
|
||||
|
||||
# Make release archives for all supported platforms
|
||||
make release-all
|
||||
```
|
||||
|
||||
Third-party libraries used
|
||||
--------------------------
|
||||
|
||||
- ~[ncurses][ncurses]~
|
||||
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
|
||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||
- [tcell](https://github.com/gdamore/tcell)
|
||||
- Licensed under [Apache License 2.0](https://github.com/gdamore/tcell/blob/master/LICENSE)
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[install]: https://github.com/junegunn/fzf#installation
|
||||
[go]: https://golang.org/
|
||||
[gil]: http://en.wikipedia.org/wiki/Global_Interpreter_Lock
|
||||
[ncurses]: https://www.gnu.org/software/ncurses/
|
||||
[req]: http://golang.org/doc/install
|
||||
[tcell]: https://github.com/gdamore/tcell
|
||||
@@ -1,6 +1,7 @@
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/junegunn/fzf/src/util"
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
|
||||
const (
|
||||
// Current version
|
||||
version = "0.16.6"
|
||||
version = "0.16.8"
|
||||
|
||||
// Core
|
||||
coordinatorDelayMax time.Duration = 100 * time.Millisecond
|
||||
@@ -47,6 +48,18 @@ const (
|
||||
defaultJumpLabels string = "asdfghjklqwertyuiopzxcvbnm1234567890ASDFGHJKLQWERTYUIOPZXCVBNM`~;:,<.>/?'\"!@#$%^&*()[{]}-_=+"
|
||||
)
|
||||
|
||||
var defaultCommand string
|
||||
|
||||
func init() {
|
||||
if !util.IsWindows() {
|
||||
defaultCommand = `command find -L . -mindepth 1 \( -path '*/\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \) -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-`
|
||||
} else if os.Getenv("TERM") == "cygwin" {
|
||||
defaultCommand = `sh -c "command find -L . -mindepth 1 -path '*/\.*' -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-"`
|
||||
} else {
|
||||
defaultCommand = `dir /s/b`
|
||||
}
|
||||
}
|
||||
|
||||
// fzf events
|
||||
const (
|
||||
EvtReadNew util.EventType = iota
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package fzf
|
||||
|
||||
const (
|
||||
// Reader
|
||||
defaultCommand = `command find -L . -mindepth 1 \( -path '*/\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \) -prune -o -type f -print -o -type l -print 2> /dev/null | cut -b3-`
|
||||
)
|
||||
@@ -1,8 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package fzf
|
||||
|
||||
const (
|
||||
// Reader
|
||||
defaultCommand = `dir /s/b`
|
||||
)
|
||||
@@ -43,12 +43,16 @@ Matcher -> EvtHeader -> Terminal (update header)
|
||||
*/
|
||||
|
||||
// Run starts fzf
|
||||
func Run(opts *Options) {
|
||||
func Run(opts *Options, revision string) {
|
||||
sort := opts.Sort > 0
|
||||
sortCriteria = opts.Criteria
|
||||
|
||||
if opts.Version {
|
||||
if len(revision) > 0 {
|
||||
fmt.Printf("%s (%s)\n", version, revision)
|
||||
} else {
|
||||
fmt.Println(version)
|
||||
}
|
||||
os.Exit(exitOk)
|
||||
}
|
||||
|
||||
@@ -96,6 +100,7 @@ func Run(opts *Options) {
|
||||
chars, colors := ansiProcessor(data)
|
||||
return &Item{
|
||||
index: int32(index),
|
||||
trimLength: -1,
|
||||
text: chars,
|
||||
colors: colors}
|
||||
})
|
||||
@@ -111,6 +116,7 @@ func Run(opts *Options) {
|
||||
textRunes := joinTokens(trans)
|
||||
item := Item{
|
||||
index: int32(index),
|
||||
trimLength: -1,
|
||||
origText: &data,
|
||||
colors: nil}
|
||||
|
||||
|
||||
18
src/deps
18
src/deps
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "$GOPATH" ]; then
|
||||
echo '$GOPATH not defined'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
reset() (
|
||||
cd "$GOPATH/src/$1"
|
||||
export GIT_DIR="$(pwd)/.git"
|
||||
[ "$(git rev-parse HEAD)" = "$2" ] ||
|
||||
(git fetch && git reset --hard "$2")
|
||||
)
|
||||
|
||||
reset github.com/junegunn/go-isatty 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
|
||||
reset github.com/junegunn/go-runewidth 14207d285c6c197daabb5c9793d63e7af9ab2d50
|
||||
reset github.com/junegunn/go-shellwords 33bd8f1ebe16d6e5eb688cc885749a63059e9167
|
||||
reset golang.org/x/crypto abc5fa7ad02123a41f02bf1391c9760f7586e608
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
// Item represents each input line
|
||||
type Item struct {
|
||||
index int32
|
||||
trimLength int32
|
||||
text util.Chars
|
||||
origText *[]byte
|
||||
colors *[]ansiOffset
|
||||
@@ -18,6 +19,14 @@ func (item *Item) Index() int32 {
|
||||
return item.index
|
||||
}
|
||||
|
||||
func (item *Item) TrimLength() int32 {
|
||||
if item.trimLength >= 0 {
|
||||
return item.trimLength
|
||||
}
|
||||
item.trimLength = int32(item.text.TrimLength())
|
||||
return item.trimLength
|
||||
}
|
||||
|
||||
// Colors returns ansiOffsets of the Item
|
||||
func (item *Item) Colors() []ansiOffset {
|
||||
if item.colors == nil {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
"github.com/junegunn/fzf/src/util"
|
||||
|
||||
"github.com/junegunn/go-shellwords"
|
||||
"github.com/mattn/go-shellwords"
|
||||
)
|
||||
|
||||
const usage = `usage: fzf [options]
|
||||
@@ -86,6 +86,7 @@ const usage = `usage: fzf [options]
|
||||
--read0 Read input delimited by ASCII NUL characters
|
||||
--print0 Print output delimited by ASCII NUL characters
|
||||
--sync Synchronous search for multi-staged filtering
|
||||
--version Display version information and exit
|
||||
|
||||
Environment variables
|
||||
FZF_DEFAULT_COMMAND Default command to use when input is tty
|
||||
@@ -399,8 +400,10 @@ func parseKeyChords(str string, message string) map[int]string {
|
||||
chord = tui.BSpace
|
||||
case "ctrl-space":
|
||||
chord = tui.CtrlSpace
|
||||
case "change":
|
||||
chord = tui.Change
|
||||
case "alt-enter", "alt-return":
|
||||
chord = tui.AltEnter
|
||||
chord = tui.CtrlAltM
|
||||
case "alt-space":
|
||||
chord = tui.AltSpace
|
||||
case "alt-/":
|
||||
@@ -436,7 +439,9 @@ func parseKeyChords(str string, message string) map[int]string {
|
||||
case "f12":
|
||||
chord = tui.F12
|
||||
default:
|
||||
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
||||
if len(key) == 10 && strings.HasPrefix(lkey, "ctrl-alt-") && isAlphabet(lkey[9]) {
|
||||
chord = tui.CtrlAltA + int(lkey[9]) - 'a'
|
||||
} else if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
||||
chord = tui.CtrlA + int(lkey[5]) - 'a'
|
||||
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
|
||||
chord = tui.AltA + int(lkey[4]) - 'a'
|
||||
@@ -711,6 +716,8 @@ func parseKeymap(keymap map[int][]action, str string) {
|
||||
appendAction(actDown)
|
||||
case "up":
|
||||
appendAction(actUp)
|
||||
case "top":
|
||||
appendAction(actTop)
|
||||
case "page-up":
|
||||
appendAction(actPageUp)
|
||||
case "page-down":
|
||||
|
||||
@@ -125,14 +125,14 @@ func TestIrrelevantNth(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseKeys(t *testing.T) {
|
||||
pairs := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g,ALT-enter,alt-SPACE", "")
|
||||
pairs := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g,ctrl-alt-a,ALT-enter,alt-SPACE", "")
|
||||
check := func(i int, s string) {
|
||||
if pairs[i] != s {
|
||||
t.Errorf("%s != %s", pairs[i], s)
|
||||
}
|
||||
}
|
||||
if len(pairs) != 11 {
|
||||
t.Error(11)
|
||||
if len(pairs) != 12 {
|
||||
t.Error(12)
|
||||
}
|
||||
check(tui.CtrlZ, "ctrl-z")
|
||||
check(tui.AltZ, "alt-z")
|
||||
@@ -143,7 +143,8 @@ func TestParseKeys(t *testing.T) {
|
||||
check(tui.CtrlA+'g'-'a', "ctrl-G")
|
||||
check(tui.AltZ+'J', "J")
|
||||
check(tui.AltZ+'g', "g")
|
||||
check(tui.AltEnter, "ALT-enter")
|
||||
check(tui.CtrlAltA, "ctrl-alt-a")
|
||||
check(tui.CtrlAltM, "ALT-enter")
|
||||
check(tui.AltSpace, "alt-SPACE")
|
||||
|
||||
// Synonyms
|
||||
|
||||
@@ -299,20 +299,20 @@ func (p *Pattern) matchChunk(chunk *Chunk, space []*Result, slab *util.Slab) []*
|
||||
// MatchItem returns true if the Item is a match
|
||||
func (p *Pattern) MatchItem(item *Item, withPos bool, slab *util.Slab) (*Result, []Offset, *[]int) {
|
||||
if p.extended {
|
||||
if offsets, bonus, trimLen, pos := p.extendedMatch(item, withPos, slab); len(offsets) == len(p.termSets) {
|
||||
return buildResult(item, offsets, bonus, trimLen), offsets, pos
|
||||
if offsets, bonus, pos := p.extendedMatch(item, withPos, slab); len(offsets) == len(p.termSets) {
|
||||
return buildResult(item, offsets, bonus), offsets, pos
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
offset, bonus, trimLen, pos := p.basicMatch(item, withPos, slab)
|
||||
offset, bonus, pos := p.basicMatch(item, withPos, slab)
|
||||
if sidx := offset[0]; sidx >= 0 {
|
||||
offsets := []Offset{offset}
|
||||
return buildResult(item, offsets, bonus, trimLen), offsets, pos
|
||||
return buildResult(item, offsets, bonus), offsets, pos
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (p *Pattern) basicMatch(item *Item, withPos bool, slab *util.Slab) (Offset, int, int, *[]int) {
|
||||
func (p *Pattern) basicMatch(item *Item, withPos bool, slab *util.Slab) (Offset, int, *[]int) {
|
||||
input := p.prepareInput(item)
|
||||
if p.fuzzy {
|
||||
return p.iter(p.fuzzyAlgo, input, p.caseSensitive, p.normalize, p.forward, p.text, withPos, slab)
|
||||
@@ -320,11 +320,10 @@ func (p *Pattern) basicMatch(item *Item, withPos bool, slab *util.Slab) (Offset,
|
||||
return p.iter(algo.ExactMatchNaive, input, p.caseSensitive, p.normalize, p.forward, p.text, withPos, slab)
|
||||
}
|
||||
|
||||
func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Offset, int, int, *[]int) {
|
||||
func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Offset, int, *[]int) {
|
||||
input := p.prepareInput(item)
|
||||
offsets := []Offset{}
|
||||
var totalScore int
|
||||
var totalTrimLen int
|
||||
var allPos *[]int
|
||||
if withPos {
|
||||
allPos = &[]int{}
|
||||
@@ -332,16 +331,15 @@ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Of
|
||||
for _, termSet := range p.termSets {
|
||||
var offset Offset
|
||||
var currentScore int
|
||||
var trimLen int
|
||||
matched := false
|
||||
for _, term := range termSet {
|
||||
pfun := p.procFun[term.typ]
|
||||
off, score, tLen, pos := p.iter(pfun, input, term.caseSensitive, p.normalize, p.forward, term.text, withPos, slab)
|
||||
off, score, pos := p.iter(pfun, input, term.caseSensitive, p.normalize, p.forward, term.text, withPos, slab)
|
||||
if sidx := off[0]; sidx >= 0 {
|
||||
if term.inv {
|
||||
continue
|
||||
}
|
||||
offset, currentScore, trimLen = off, score, tLen
|
||||
offset, currentScore = off, score
|
||||
matched = true
|
||||
if withPos {
|
||||
if pos != nil {
|
||||
@@ -354,7 +352,7 @@ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Of
|
||||
}
|
||||
break
|
||||
} else if term.inv {
|
||||
offset, currentScore, trimLen = Offset{0, 0}, 0, 0
|
||||
offset, currentScore = Offset{0, 0}, 0
|
||||
matched = true
|
||||
continue
|
||||
}
|
||||
@@ -362,10 +360,9 @@ func (p *Pattern) extendedMatch(item *Item, withPos bool, slab *util.Slab) ([]Of
|
||||
if matched {
|
||||
offsets = append(offsets, offset)
|
||||
totalScore += currentScore
|
||||
totalTrimLen += trimLen
|
||||
}
|
||||
}
|
||||
return offsets, totalScore, totalTrimLen, allPos
|
||||
return offsets, totalScore, allPos
|
||||
}
|
||||
|
||||
func (p *Pattern) prepareInput(item *Item) []Token {
|
||||
@@ -375,7 +372,7 @@ func (p *Pattern) prepareInput(item *Item) []Token {
|
||||
|
||||
var ret []Token
|
||||
if len(p.nth) == 0 {
|
||||
ret = []Token{Token{text: &item.text, prefixLength: 0, trimLength: int32(item.text.TrimLength())}}
|
||||
ret = []Token{Token{text: &item.text, prefixLength: 0}}
|
||||
} else {
|
||||
tokens := Tokenize(item.text, p.delimiter)
|
||||
ret = Transform(tokens, p.nth)
|
||||
@@ -384,7 +381,7 @@ func (p *Pattern) prepareInput(item *Item) []Token {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Pattern) iter(pfun algo.Algo, tokens []Token, caseSensitive bool, normalize bool, forward bool, pattern []rune, withPos bool, slab *util.Slab) (Offset, int, int, *[]int) {
|
||||
func (p *Pattern) iter(pfun algo.Algo, tokens []Token, caseSensitive bool, normalize bool, forward bool, pattern []rune, withPos bool, slab *util.Slab) (Offset, int, *[]int) {
|
||||
for _, part := range tokens {
|
||||
if res, pos := pfun(caseSensitive, normalize, forward, *part.text, pattern, withPos, slab); res.Start >= 0 {
|
||||
sidx := int32(res.Start) + part.prefixLength
|
||||
@@ -394,8 +391,8 @@ func (p *Pattern) iter(pfun algo.Algo, tokens []Token, caseSensitive bool, norma
|
||||
(*pos)[idx] += int(part.prefixLength)
|
||||
}
|
||||
}
|
||||
return Offset{sidx, eidx}, res.Score, int(part.trimLength), pos
|
||||
return Offset{sidx, eidx}, res.Score, pos
|
||||
}
|
||||
}
|
||||
return Offset{-1, -1}, 0, -1, nil
|
||||
return Offset{-1, -1}, 0, nil
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ type Result struct {
|
||||
rank rank
|
||||
}
|
||||
|
||||
func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
|
||||
func buildResult(item *Item, offsets []Offset, score int) *Result {
|
||||
if len(offsets) > 1 {
|
||||
sort.Sort(ByOrder(offsets))
|
||||
}
|
||||
@@ -57,8 +57,7 @@ func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
|
||||
// Higher is better
|
||||
val = math.MaxUint16 - util.AsUint16(score)
|
||||
case byLength:
|
||||
// If offsets is empty, trimLen will be 0, but we don't care
|
||||
val = util.AsUint16(trimLen)
|
||||
val = util.AsUint16(int(item.TrimLength()))
|
||||
case byBegin, byEnd:
|
||||
if validOffsetFound {
|
||||
whitePrefixLen := 0
|
||||
@@ -72,7 +71,7 @@ func buildResult(item *Item, offsets []Offset, score int, trimLen int) *Result {
|
||||
if criterion == byBegin {
|
||||
val = util.AsUint16(minEnd - whitePrefixLen)
|
||||
} else {
|
||||
val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/trimLen)
|
||||
val = util.AsUint16(math.MaxUint16 - math.MaxUint16*(maxEnd-whitePrefixLen)/int(item.TrimLength()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestResultRank(t *testing.T) {
|
||||
sortCriteria = []criterion{byScore, byLength}
|
||||
|
||||
strs := [][]rune{[]rune("foo"), []rune("foobar"), []rune("bar"), []rune("baz")}
|
||||
item1 := buildResult(&Item{text: util.RunesToChars(strs[0]), index: 1}, []Offset{}, 2, 3)
|
||||
item1 := buildResult(&Item{text: util.RunesToChars(strs[0]), index: 1, trimLength: -1}, []Offset{}, 2)
|
||||
if item1.rank.points[0] != math.MaxUint16-2 || // Bonus
|
||||
item1.rank.points[1] != 3 || // Length
|
||||
item1.rank.points[2] != 0 || // Unused
|
||||
@@ -61,7 +61,7 @@ func TestResultRank(t *testing.T) {
|
||||
t.Error(item1.rank)
|
||||
}
|
||||
// Only differ in index
|
||||
item2 := buildResult(&Item{text: util.RunesToChars(strs[0])}, []Offset{}, 2, 3)
|
||||
item2 := buildResult(&Item{text: util.RunesToChars(strs[0])}, []Offset{}, 2)
|
||||
|
||||
items := []*Result{item1, item2}
|
||||
sort.Sort(ByRelevance(items))
|
||||
@@ -77,10 +77,10 @@ func TestResultRank(t *testing.T) {
|
||||
}
|
||||
|
||||
// Sort by relevance
|
||||
item3 := buildResult(&Item{index: 2}, []Offset{Offset{1, 3}, Offset{5, 7}}, 3, 0)
|
||||
item4 := buildResult(&Item{index: 2}, []Offset{Offset{1, 2}, Offset{6, 7}}, 4, 0)
|
||||
item5 := buildResult(&Item{index: 2}, []Offset{Offset{1, 3}, Offset{5, 7}}, 5, 0)
|
||||
item6 := buildResult(&Item{index: 2}, []Offset{Offset{1, 2}, Offset{6, 7}}, 6, 0)
|
||||
item3 := buildResult(&Item{index: 2}, []Offset{Offset{1, 3}, Offset{5, 7}}, 3)
|
||||
item4 := buildResult(&Item{index: 2}, []Offset{Offset{1, 2}, Offset{6, 7}}, 4)
|
||||
item5 := buildResult(&Item{index: 2}, []Offset{Offset{1, 3}, Offset{5, 7}}, 5)
|
||||
item6 := buildResult(&Item{index: 2}, []Offset{Offset{1, 2}, Offset{6, 7}}, 6)
|
||||
items = []*Result{item1, item2, item3, item4, item5, item6}
|
||||
sort.Sort(ByRelevance(items))
|
||||
if !(items[0] == item6 && items[1] == item5 &&
|
||||
|
||||
@@ -58,7 +58,9 @@ type Terminal struct {
|
||||
initDelay time.Duration
|
||||
inlineInfo bool
|
||||
prompt string
|
||||
promptLen int
|
||||
reverse bool
|
||||
fullscreen bool
|
||||
hscroll bool
|
||||
hscrollOff int
|
||||
wordRubout string
|
||||
@@ -132,7 +134,6 @@ func (a byTimeOrder) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
var _spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
|
||||
var _tabStop int
|
||||
|
||||
const (
|
||||
reqPrompt util.EventType = iota
|
||||
@@ -141,6 +142,7 @@ const (
|
||||
reqList
|
||||
reqJump
|
||||
reqRefresh
|
||||
reqReinit
|
||||
reqRedraw
|
||||
reqClose
|
||||
reqPrintQuery
|
||||
@@ -210,6 +212,8 @@ const (
|
||||
actExecute
|
||||
actExecuteSilent
|
||||
actExecuteMulti // Deprecated
|
||||
actSigStop
|
||||
actTop
|
||||
)
|
||||
|
||||
func toActions(types ...actionType) []action {
|
||||
@@ -246,6 +250,9 @@ func defaultKeymap() map[int][]action {
|
||||
keymap[tui.CtrlU] = toActions(actUnixLineDiscard)
|
||||
keymap[tui.CtrlW] = toActions(actUnixWordRubout)
|
||||
keymap[tui.CtrlY] = toActions(actYank)
|
||||
if !util.IsWindows() {
|
||||
keymap[tui.CtrlZ] = toActions(actSigStop)
|
||||
}
|
||||
|
||||
keymap[tui.AltB] = toActions(actBackwardWord)
|
||||
keymap[tui.SLeft] = toActions(actBackwardWord)
|
||||
@@ -295,7 +302,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
strongAttr = tui.AttrRegular
|
||||
}
|
||||
var renderer tui.Renderer
|
||||
if opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100 {
|
||||
fullscreen := opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100
|
||||
if fullscreen {
|
||||
if tui.HasFullscreenRenderer() {
|
||||
renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse)
|
||||
} else {
|
||||
@@ -332,11 +340,11 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
|
||||
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
|
||||
}
|
||||
return &Terminal{
|
||||
t := Terminal{
|
||||
initDelay: delay,
|
||||
inlineInfo: opts.InlineInfo,
|
||||
prompt: opts.Prompt,
|
||||
reverse: opts.Reverse,
|
||||
fullscreen: fullscreen,
|
||||
hscroll: opts.Hscroll,
|
||||
hscrollOff: opts.HscrollOff,
|
||||
wordRubout: wordRubout,
|
||||
@@ -381,6 +389,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
startChan: make(chan bool, 1),
|
||||
tui: renderer,
|
||||
initFunc: func() { renderer.Init() }}
|
||||
t.prompt, t.promptLen = t.processTabs([]rune(opts.Prompt), 0)
|
||||
return &t
|
||||
}
|
||||
|
||||
// Input returns current query string
|
||||
@@ -572,6 +582,7 @@ func (t *Terminal) resizeWindows() {
|
||||
pwidth += 1
|
||||
}
|
||||
t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, tui.BorderNone)
|
||||
os.Setenv("FZF_PREVIEW_HEIGHT", strconv.Itoa(h-2))
|
||||
}
|
||||
switch t.preview.position {
|
||||
case posUp:
|
||||
@@ -623,7 +634,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
|
||||
}
|
||||
|
||||
func (t *Terminal) placeCursor() {
|
||||
t.move(0, t.displayWidth([]rune(t.prompt))+t.displayWidth(t.input[:t.cx]), false)
|
||||
t.move(0, t.promptLen+t.displayWidth(t.input[:t.cx]), false)
|
||||
}
|
||||
|
||||
func (t *Terminal) printPrompt() {
|
||||
@@ -635,7 +646,7 @@ func (t *Terminal) printPrompt() {
|
||||
func (t *Terminal) printInfo() {
|
||||
pos := 0
|
||||
if t.inlineInfo {
|
||||
pos = t.displayWidth([]rune(t.prompt)) + t.displayWidth(t.input) + 1
|
||||
pos = t.promptLen + t.displayWidth(t.input) + 1
|
||||
if pos+len(" < ") > t.window.Width() {
|
||||
return
|
||||
}
|
||||
@@ -943,6 +954,7 @@ func (t *Terminal) printPreview() {
|
||||
}
|
||||
reader := bufio.NewReader(strings.NewReader(t.previewer.text))
|
||||
lineNo := -t.previewer.offset
|
||||
var ansi *ansiState
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
eof := err == io.EOF
|
||||
@@ -954,7 +966,7 @@ func (t *Terminal) printPreview() {
|
||||
break
|
||||
} else if lineNo > 0 {
|
||||
var fillRet tui.FillReturn
|
||||
extractColor(line, nil, func(str string, ansi *ansiState) bool {
|
||||
_, _, ansi = extractColor(line, ansi, func(str string, ansi *ansiState) bool {
|
||||
trimmed := []rune(str)
|
||||
if !t.preview.wrap {
|
||||
trimmed, _ = t.trimRight(trimmed, maxWidth-t.pwindow.X())
|
||||
@@ -1170,6 +1182,12 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Terminal) redraw() {
|
||||
t.tui.Clear()
|
||||
t.tui.Refresh()
|
||||
t.printAll()
|
||||
}
|
||||
|
||||
func (t *Terminal) executeCommand(template string, forcePlus bool, background bool) {
|
||||
valid, list := t.buildPlusList(template, forcePlus)
|
||||
if !valid {
|
||||
@@ -1181,12 +1199,10 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
t.tui.Pause()
|
||||
t.tui.Pause(true)
|
||||
cmd.Run()
|
||||
if t.tui.Resume() {
|
||||
t.tui.Clear()
|
||||
t.printAll()
|
||||
}
|
||||
t.tui.Resume(true)
|
||||
t.redraw()
|
||||
t.refresh()
|
||||
} else {
|
||||
cmd.Run()
|
||||
@@ -1227,7 +1243,7 @@ func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item
|
||||
}
|
||||
|
||||
func (t *Terminal) truncateQuery() {
|
||||
maxPatternLength := util.Max(1, t.window.Width()-t.displayWidth([]rune(t.prompt))-1)
|
||||
maxPatternLength := util.Max(1, t.window.Width()-t.promptLen-1)
|
||||
t.input, _ = t.trimRight(t.input, maxPatternLength)
|
||||
t.cx = util.Constrain(t.cx, 0, len(t.input))
|
||||
}
|
||||
@@ -1244,6 +1260,15 @@ func (t *Terminal) Loop() {
|
||||
t.reqBox.Set(reqQuit, nil)
|
||||
}()
|
||||
|
||||
contChan := make(chan os.Signal, 1)
|
||||
notifyOnCont(contChan)
|
||||
go func() {
|
||||
for {
|
||||
<-contChan
|
||||
t.reqBox.Set(reqReinit, nil)
|
||||
}
|
||||
}()
|
||||
|
||||
resizeChan := make(chan os.Signal, 1)
|
||||
notifyOnResize(resizeChan) // Non-portable
|
||||
go func() {
|
||||
@@ -1352,10 +1377,11 @@ func (t *Terminal) Loop() {
|
||||
t.printHeader()
|
||||
case reqRefresh:
|
||||
t.suppress = false
|
||||
case reqReinit:
|
||||
t.tui.Resume(t.fullscreen)
|
||||
t.redraw()
|
||||
case reqRedraw:
|
||||
t.tui.Clear()
|
||||
t.tui.Refresh()
|
||||
t.printAll()
|
||||
t.redraw()
|
||||
case reqClose:
|
||||
t.tui.Close()
|
||||
if t.output() {
|
||||
@@ -1567,25 +1593,28 @@ func (t *Terminal) Loop() {
|
||||
case actToggleDown:
|
||||
if t.multi && t.merger.Length() > 0 {
|
||||
toggle()
|
||||
t.vmove(-1)
|
||||
t.vmove(-1, true)
|
||||
req(reqList)
|
||||
}
|
||||
case actToggleUp:
|
||||
if t.multi && t.merger.Length() > 0 {
|
||||
toggle()
|
||||
t.vmove(1)
|
||||
t.vmove(1, true)
|
||||
req(reqList)
|
||||
}
|
||||
case actDown:
|
||||
t.vmove(-1)
|
||||
t.vmove(-1, true)
|
||||
req(reqList)
|
||||
case actUp:
|
||||
t.vmove(1)
|
||||
t.vmove(1, true)
|
||||
req(reqList)
|
||||
case actAccept:
|
||||
req(reqClose)
|
||||
case actClearScreen:
|
||||
req(reqRedraw)
|
||||
case actTop:
|
||||
t.vset(0)
|
||||
req(reqList)
|
||||
case actUnixLineDiscard:
|
||||
if t.cx > 0 {
|
||||
t.yanked = copySlice(t.input[:t.cx])
|
||||
@@ -1605,16 +1634,16 @@ func (t *Terminal) Loop() {
|
||||
t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
|
||||
t.cx += len(t.yanked)
|
||||
case actPageUp:
|
||||
t.vmove(t.maxItems() - 1)
|
||||
t.vmove(t.maxItems()-1, false)
|
||||
req(reqList)
|
||||
case actPageDown:
|
||||
t.vmove(-(t.maxItems() - 1))
|
||||
t.vmove(-(t.maxItems() - 1), false)
|
||||
req(reqList)
|
||||
case actHalfPageUp:
|
||||
t.vmove(t.maxItems() / 2)
|
||||
t.vmove(t.maxItems()/2, false)
|
||||
req(reqList)
|
||||
case actHalfPageDown:
|
||||
t.vmove(-(t.maxItems() / 2))
|
||||
t.vmove(-(t.maxItems() / 2), false)
|
||||
req(reqList)
|
||||
case actJump:
|
||||
t.jumping = jumpEnabled
|
||||
@@ -1654,6 +1683,15 @@ func (t *Terminal) Loop() {
|
||||
t.input = []rune(t.history.next())
|
||||
t.cx = len(t.input)
|
||||
}
|
||||
case actSigStop:
|
||||
p, err := os.FindProcess(os.Getpid())
|
||||
if err == nil {
|
||||
t.tui.Clear()
|
||||
t.tui.Pause(t.fullscreen)
|
||||
notifyStop(p)
|
||||
t.mutex.Unlock()
|
||||
return false
|
||||
}
|
||||
case actMouse:
|
||||
me := event.MouseEvent
|
||||
mx, my := me.X, me.Y
|
||||
@@ -1663,7 +1701,7 @@ func (t *Terminal) Loop() {
|
||||
if t.multi && me.Mod {
|
||||
toggle()
|
||||
}
|
||||
t.vmove(me.S)
|
||||
t.vmove(me.S, true)
|
||||
req(reqList)
|
||||
} else if t.hasPreviewWindow() && t.pwindow.Enclose(my, mx) {
|
||||
scrollPreview(-me.S)
|
||||
@@ -1671,7 +1709,7 @@ func (t *Terminal) Loop() {
|
||||
} else if t.window.Enclose(my, mx) {
|
||||
mx -= t.window.Left()
|
||||
my -= t.window.Top()
|
||||
mx = util.Constrain(mx-t.displayWidth([]rune(t.prompt)), 0, len(t.input))
|
||||
mx = util.Constrain(mx-t.promptLen, 0, len(t.input))
|
||||
if !t.reverse {
|
||||
my = t.window.Height() - my - 1
|
||||
}
|
||||
@@ -1717,6 +1755,11 @@ func (t *Terminal) Loop() {
|
||||
}
|
||||
t.truncateQuery()
|
||||
changed = string(previousInput) != string(t.input)
|
||||
if onChanges, prs := t.keymap[tui.Change]; changed && prs {
|
||||
if !doActions(onChanges, tui.Change) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if mapkey == tui.Rune {
|
||||
if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 && idx < t.maxItems() && idx < t.merger.Length() {
|
||||
@@ -1755,12 +1798,12 @@ func (t *Terminal) constrain() {
|
||||
t.offset = util.Max(0, t.offset)
|
||||
}
|
||||
|
||||
func (t *Terminal) vmove(o int) {
|
||||
func (t *Terminal) vmove(o int, allowCycle bool) {
|
||||
if t.reverse {
|
||||
o *= -1
|
||||
}
|
||||
dest := t.cy + o
|
||||
if t.cycle {
|
||||
if t.cycle && allowCycle {
|
||||
max := t.merger.Length() - 1
|
||||
if dest > max {
|
||||
if t.cy == max {
|
||||
|
||||
@@ -11,3 +11,11 @@ import (
|
||||
func notifyOnResize(resizeChan chan<- os.Signal) {
|
||||
signal.Notify(resizeChan, syscall.SIGWINCH)
|
||||
}
|
||||
|
||||
func notifyStop(p *os.Process) {
|
||||
p.Signal(syscall.SIGSTOP)
|
||||
}
|
||||
|
||||
func notifyOnCont(resizeChan chan<- os.Signal) {
|
||||
signal.Notify(resizeChan, syscall.SIGCONT)
|
||||
}
|
||||
|
||||
@@ -9,3 +9,11 @@ import (
|
||||
func notifyOnResize(resizeChan chan<- os.Signal) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func notifyStop(p *os.Process) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
func notifyOnCont(resizeChan chan<- os.Signal) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ type Range struct {
|
||||
type Token struct {
|
||||
text *util.Chars
|
||||
prefixLength int32
|
||||
trimLength int32
|
||||
}
|
||||
|
||||
// Delimiter for tokenizing the input
|
||||
@@ -81,7 +80,7 @@ func withPrefixLengths(tokens []util.Chars, begin int) []Token {
|
||||
prefixLength := begin
|
||||
for idx, token := range tokens {
|
||||
// NOTE: &tokens[idx] instead of &tokens
|
||||
ret[idx] = Token{&tokens[idx], int32(prefixLength), int32(token.TrimLength())}
|
||||
ret[idx] = Token{&tokens[idx], int32(prefixLength)}
|
||||
prefixLength += token.Length()
|
||||
}
|
||||
return ret
|
||||
@@ -242,7 +241,7 @@ func Transform(tokens []Token, withNth []Range) []Token {
|
||||
} else {
|
||||
prefixLength = 0
|
||||
}
|
||||
transTokens[idx] = Token{&merged, prefixLength, int32(merged.TrimLength())}
|
||||
transTokens[idx] = Token{&merged, prefixLength}
|
||||
}
|
||||
return transTokens
|
||||
}
|
||||
|
||||
@@ -48,22 +48,22 @@ func TestTokenize(t *testing.T) {
|
||||
// AWK-style
|
||||
input := " abc: def: ghi "
|
||||
tokens := Tokenize(util.RunesToChars([]rune(input)), Delimiter{})
|
||||
if tokens[0].text.ToString() != "abc: " || tokens[0].prefixLength != 2 || tokens[0].trimLength != 4 {
|
||||
if tokens[0].text.ToString() != "abc: " || tokens[0].prefixLength != 2 {
|
||||
t.Errorf("%s", tokens)
|
||||
}
|
||||
|
||||
// With delimiter
|
||||
tokens = Tokenize(util.RunesToChars([]rune(input)), delimiterRegexp(":"))
|
||||
if tokens[0].text.ToString() != " abc:" || tokens[0].prefixLength != 0 || tokens[0].trimLength != 4 {
|
||||
if tokens[0].text.ToString() != " abc:" || tokens[0].prefixLength != 0 {
|
||||
t.Errorf("%s", tokens)
|
||||
}
|
||||
|
||||
// With delimiter regex
|
||||
tokens = Tokenize(util.RunesToChars([]rune(input)), delimiterRegexp("\\s+"))
|
||||
if tokens[0].text.ToString() != " " || tokens[0].prefixLength != 0 || tokens[0].trimLength != 0 ||
|
||||
tokens[1].text.ToString() != "abc: " || tokens[1].prefixLength != 2 || tokens[1].trimLength != 4 ||
|
||||
tokens[2].text.ToString() != "def: " || tokens[2].prefixLength != 8 || tokens[2].trimLength != 4 ||
|
||||
tokens[3].text.ToString() != "ghi " || tokens[3].prefixLength != 14 || tokens[3].trimLength != 3 {
|
||||
if tokens[0].text.ToString() != " " || tokens[0].prefixLength != 0 ||
|
||||
tokens[1].text.ToString() != "abc: " || tokens[1].prefixLength != 2 ||
|
||||
tokens[2].text.ToString() != "def: " || tokens[2].prefixLength != 8 ||
|
||||
tokens[3].text.ToString() != "ghi " || tokens[3].prefixLength != 14 {
|
||||
t.Errorf("%s", tokens)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ const (
|
||||
)
|
||||
|
||||
func (r *FullscreenRenderer) Init() {}
|
||||
func (r *FullscreenRenderer) Pause() {}
|
||||
func (r *FullscreenRenderer) Pause(bool) {}
|
||||
func (r *FullscreenRenderer) Resume(bool) {}
|
||||
func (r *FullscreenRenderer) Clear() {}
|
||||
func (r *FullscreenRenderer) Refresh() {}
|
||||
func (r *FullscreenRenderer) Close() {}
|
||||
|
||||
func (r *FullscreenRenderer) Resume() bool { return false }
|
||||
func (r *FullscreenRenderer) DoesAutoWrap() bool { return false }
|
||||
func (r *FullscreenRenderer) IsOptimized() bool { return false }
|
||||
func (r *FullscreenRenderer) GetChar() Event { return Event{} }
|
||||
|
||||
@@ -251,8 +251,9 @@ func (r *LightRenderer) updateTerminalSize() {
|
||||
|
||||
func (r *LightRenderer) getch(nonblock bool) (int, bool) {
|
||||
b := make([]byte, 1)
|
||||
fd := r.fd()
|
||||
util.SetNonblock(r.ttyin, nonblock)
|
||||
_, err := r.ttyin.Read(b)
|
||||
_, err := util.Read(fd, b)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
@@ -344,9 +345,10 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
return Event{ESC, 0, nil}
|
||||
}
|
||||
*sz = 2
|
||||
if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 {
|
||||
return Event{int(CtrlAltA + r.buffer[1] - 1), 0, nil}
|
||||
}
|
||||
switch r.buffer[1] {
|
||||
case 13:
|
||||
return Event{AltEnter, 0, nil}
|
||||
case 32:
|
||||
return Event{AltSpace, 0, nil}
|
||||
case 47:
|
||||
@@ -521,8 +523,9 @@ func (r *LightRenderer) rmcup() {
|
||||
r.csi("?1049l")
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Pause() {
|
||||
func (r *LightRenderer) Pause(clear bool) {
|
||||
terminal.Restore(r.fd(), r.origState)
|
||||
if clear {
|
||||
if r.fullscreen {
|
||||
r.rmcup()
|
||||
} else {
|
||||
@@ -531,17 +534,24 @@ func (r *LightRenderer) Pause() {
|
||||
}
|
||||
r.flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Resume() bool {
|
||||
func (r *LightRenderer) Resume(clear bool) {
|
||||
terminal.MakeRaw(r.fd())
|
||||
if clear {
|
||||
if r.fullscreen {
|
||||
r.smcup()
|
||||
} else {
|
||||
r.rmcup()
|
||||
}
|
||||
r.flush()
|
||||
// Should redraw
|
||||
return true
|
||||
} else if !r.fullscreen && r.mouse {
|
||||
// NOTE: Resume(false) is only called on SIGCONT after SIGSTOP.
|
||||
// And It's highly likely that the offset we obtained at the beginning will
|
||||
// no longer be correct, so we simply disable mouse input.
|
||||
r.csi("?1000l")
|
||||
r.mouse = false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Clear() {
|
||||
|
||||
@@ -176,12 +176,11 @@ func initPairs(theme *ColorTheme) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Pause() {
|
||||
func (r *FullscreenRenderer) Pause(bool) {
|
||||
C.endwin()
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Resume() bool {
|
||||
return false
|
||||
func (r *FullscreenRenderer) Resume(bool) {
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Close() {
|
||||
@@ -353,7 +352,7 @@ func escSequence() Event {
|
||||
case C.ERR:
|
||||
return Event{ESC, 0, nil}
|
||||
case CtrlM:
|
||||
return Event{AltEnter, 0, nil}
|
||||
return Event{CtrlAltM, 0, nil}
|
||||
case '/':
|
||||
return Event{AltSlash, 0, nil}
|
||||
case ' ':
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"runtime"
|
||||
|
||||
// https://github.com/gdamore/tcell/pull/135
|
||||
"github.com/junegunn/tcell"
|
||||
"github.com/junegunn/tcell/encoding"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/gdamore/tcell/encoding"
|
||||
|
||||
"github.com/junegunn/go-runewidth"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
func HasFullscreenRenderer() bool {
|
||||
@@ -141,6 +141,9 @@ func (r *FullscreenRenderer) initScreen() {
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Init() {
|
||||
if os.Getenv("TERM") == "cygwin" {
|
||||
os.Setenv("TERM", "")
|
||||
}
|
||||
encoding.Register()
|
||||
|
||||
r.initScreen()
|
||||
@@ -221,61 +224,68 @@ func (r *FullscreenRenderer) GetChar() Event {
|
||||
// process keyboard:
|
||||
case *tcell.EventKey:
|
||||
alt := (ev.Modifiers() & tcell.ModAlt) > 0
|
||||
keyfn := func(r rune) int {
|
||||
if alt {
|
||||
return CtrlAltA - 'a' + int(r)
|
||||
}
|
||||
return CtrlA - 'a' + int(r)
|
||||
}
|
||||
switch ev.Key() {
|
||||
case tcell.KeyCtrlA:
|
||||
return Event{CtrlA, 0, nil}
|
||||
return Event{keyfn('a'), 0, nil}
|
||||
case tcell.KeyCtrlB:
|
||||
return Event{CtrlB, 0, nil}
|
||||
return Event{keyfn('b'), 0, nil}
|
||||
case tcell.KeyCtrlC:
|
||||
return Event{CtrlC, 0, nil}
|
||||
return Event{keyfn('c'), 0, nil}
|
||||
case tcell.KeyCtrlD:
|
||||
return Event{CtrlD, 0, nil}
|
||||
return Event{keyfn('d'), 0, nil}
|
||||
case tcell.KeyCtrlE:
|
||||
return Event{CtrlE, 0, nil}
|
||||
return Event{keyfn('e'), 0, nil}
|
||||
case tcell.KeyCtrlF:
|
||||
return Event{CtrlF, 0, nil}
|
||||
return Event{keyfn('f'), 0, nil}
|
||||
case tcell.KeyCtrlG:
|
||||
return Event{CtrlG, 0, nil}
|
||||
return Event{keyfn('g'), 0, nil}
|
||||
case tcell.KeyCtrlH:
|
||||
return Event{keyfn('h'), 0, nil}
|
||||
case tcell.KeyCtrlI:
|
||||
return Event{keyfn('i'), 0, nil}
|
||||
case tcell.KeyCtrlJ:
|
||||
return Event{CtrlJ, 0, nil}
|
||||
return Event{keyfn('j'), 0, nil}
|
||||
case tcell.KeyCtrlK:
|
||||
return Event{CtrlK, 0, nil}
|
||||
return Event{keyfn('k'), 0, nil}
|
||||
case tcell.KeyCtrlL:
|
||||
return Event{CtrlL, 0, nil}
|
||||
return Event{keyfn('l'), 0, nil}
|
||||
case tcell.KeyCtrlM:
|
||||
if alt {
|
||||
return Event{AltEnter, 0, nil}
|
||||
}
|
||||
return Event{CtrlM, 0, nil}
|
||||
return Event{keyfn('m'), 0, nil}
|
||||
case tcell.KeyCtrlN:
|
||||
return Event{CtrlN, 0, nil}
|
||||
return Event{keyfn('n'), 0, nil}
|
||||
case tcell.KeyCtrlO:
|
||||
return Event{CtrlO, 0, nil}
|
||||
return Event{keyfn('o'), 0, nil}
|
||||
case tcell.KeyCtrlP:
|
||||
return Event{CtrlP, 0, nil}
|
||||
return Event{keyfn('p'), 0, nil}
|
||||
case tcell.KeyCtrlQ:
|
||||
return Event{CtrlQ, 0, nil}
|
||||
return Event{keyfn('q'), 0, nil}
|
||||
case tcell.KeyCtrlR:
|
||||
return Event{CtrlR, 0, nil}
|
||||
return Event{keyfn('r'), 0, nil}
|
||||
case tcell.KeyCtrlS:
|
||||
return Event{CtrlS, 0, nil}
|
||||
return Event{keyfn('s'), 0, nil}
|
||||
case tcell.KeyCtrlT:
|
||||
return Event{CtrlT, 0, nil}
|
||||
return Event{keyfn('t'), 0, nil}
|
||||
case tcell.KeyCtrlU:
|
||||
return Event{CtrlU, 0, nil}
|
||||
return Event{keyfn('u'), 0, nil}
|
||||
case tcell.KeyCtrlV:
|
||||
return Event{CtrlV, 0, nil}
|
||||
return Event{keyfn('v'), 0, nil}
|
||||
case tcell.KeyCtrlW:
|
||||
return Event{CtrlW, 0, nil}
|
||||
return Event{keyfn('w'), 0, nil}
|
||||
case tcell.KeyCtrlX:
|
||||
return Event{CtrlX, 0, nil}
|
||||
return Event{keyfn('x'), 0, nil}
|
||||
case tcell.KeyCtrlY:
|
||||
return Event{CtrlY, 0, nil}
|
||||
return Event{keyfn('y'), 0, nil}
|
||||
case tcell.KeyCtrlZ:
|
||||
return Event{CtrlZ, 0, nil}
|
||||
return Event{keyfn('z'), 0, nil}
|
||||
case tcell.KeyCtrlSpace:
|
||||
return Event{CtrlSpace, 0, nil}
|
||||
case tcell.KeyBackspace, tcell.KeyBackspace2:
|
||||
case tcell.KeyBackspace2:
|
||||
if alt {
|
||||
return Event{AltBS, 0, nil}
|
||||
}
|
||||
@@ -301,8 +311,6 @@ func (r *FullscreenRenderer) GetChar() Event {
|
||||
case tcell.KeyPgDn:
|
||||
return Event{PgDn, 0, nil}
|
||||
|
||||
case tcell.KeyTab:
|
||||
return Event{Tab, 0, nil}
|
||||
case tcell.KeyBacktab:
|
||||
return Event{BTab, 0, nil}
|
||||
|
||||
@@ -359,13 +367,12 @@ func (r *FullscreenRenderer) GetChar() Event {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Pause() {
|
||||
func (r *FullscreenRenderer) Pause(bool) {
|
||||
_screen.Fini()
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Resume() bool {
|
||||
func (r *FullscreenRenderer) Resume(bool) {
|
||||
r.initScreen()
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *FullscreenRenderer) Close() {
|
||||
|
||||
@@ -75,7 +75,8 @@ const (
|
||||
F11
|
||||
F12
|
||||
|
||||
AltEnter
|
||||
Change
|
||||
|
||||
AltSpace
|
||||
AltSlash
|
||||
AltBS
|
||||
@@ -91,6 +92,8 @@ const ( // Reset iota
|
||||
AltE
|
||||
AltF
|
||||
AltZ = AltA + 'z' - 'a'
|
||||
CtrlAltA = AltZ + 1
|
||||
CtrlAltM = CtrlAltA + 'm' - 'a'
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -205,8 +208,8 @@ const (
|
||||
|
||||
type Renderer interface {
|
||||
Init()
|
||||
Pause()
|
||||
Resume() bool
|
||||
Pause(clear bool)
|
||||
Resume(clear bool)
|
||||
Clear()
|
||||
RefreshWindows(windows []Window)
|
||||
Refresh()
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/junegunn/go-isatty"
|
||||
"github.com/junegunn/go-runewidth"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
var _runeWidths = make(map[rune]int)
|
||||
|
||||
@@ -26,3 +26,8 @@ func IsWindows() bool {
|
||||
func SetNonblock(file *os.File, nonblock bool) {
|
||||
syscall.SetNonblock(int(file.Fd()), nonblock)
|
||||
}
|
||||
|
||||
// Read executes syscall.Read on file descriptor
|
||||
func Read(fd int, b []byte) (int, error) {
|
||||
return syscall.Read(int(fd), b)
|
||||
}
|
||||
|
||||
@@ -7,20 +7,16 @@ import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/junegunn/go-shellwords"
|
||||
"github.com/mattn/go-shellwords"
|
||||
)
|
||||
|
||||
// ExecCommand executes the given command with $SHELL
|
||||
func ExecCommand(command string) *exec.Cmd {
|
||||
shell := os.Getenv("SHELL")
|
||||
if len(shell) == 0 {
|
||||
shell = "cmd"
|
||||
}
|
||||
args, _ := shellwords.Parse(command)
|
||||
allArgs := make([]string, len(args)+1)
|
||||
allArgs[0] = "/c"
|
||||
copy(allArgs[1:], args)
|
||||
return exec.Command(shell, allArgs...)
|
||||
return exec.Command("cmd", allArgs...)
|
||||
}
|
||||
|
||||
// IsWindows returns true on Windows
|
||||
@@ -32,3 +28,8 @@ func IsWindows() bool {
|
||||
func SetNonblock(file *os.File, nonblock bool) {
|
||||
syscall.SetNonblock(syscall.Handle(file.Fd()), nonblock)
|
||||
}
|
||||
|
||||
// Read executes syscall.Read on file descriptor
|
||||
func Read(fd int, b []byte) (int, error) {
|
||||
return syscall.Read(syscall.Handle(fd), b)
|
||||
}
|
||||
|
||||
@@ -147,6 +147,24 @@ Execute (fzf#wrap):
|
||||
let opts = fzf#wrap({})
|
||||
Assert opts.options =~ '^--color=fg:'
|
||||
|
||||
Execute (fzf#shellescape with sh):
|
||||
AssertEqual '''''', fzf#shellescape('', 'sh')
|
||||
AssertEqual '''""''', fzf#shellescape('""', 'sh')
|
||||
AssertEqual '''foobar>''', fzf#shellescape('foobar>', 'sh')
|
||||
AssertEqual '''\"''', fzf#shellescape('\"', 'sh')
|
||||
AssertEqual '''echo ''\''''a''\'''' && echo ''\''''b''\''''''', fzf#shellescape('echo ''a'' && echo ''b''', 'sh')
|
||||
|
||||
Execute (fzf#shellescape with cmd.exe):
|
||||
AssertEqual '^"^"', fzf#shellescape('', 'cmd.exe')
|
||||
AssertEqual '^"\^"\^"^"', fzf#shellescape('""', 'cmd.exe')
|
||||
AssertEqual '^"foobar^>^"', fzf#shellescape('foobar>', 'cmd.exe')
|
||||
AssertEqual '^"\\\^"\\^"', fzf#shellescape('\\\\\\\\"\', 'cmd.exe')
|
||||
AssertEqual '^"echo ''a'' ^&^& echo ''b''^"', fzf#shellescape('echo ''a'' && echo ''b''', 'cmd.exe')
|
||||
|
||||
AssertEqual '^"C:\Program Files ^(x86^)\\^"', fzf#shellescape('C:\Program Files (x86)\', 'cmd.exe')
|
||||
AssertEqual '^"C:/Program Files ^(x86^)/^"', fzf#shellescape('C:/Program Files (x86)/', 'cmd.exe')
|
||||
" AssertEqual '^"%%USERPROFILE%%^", fzf#shellescape('%USERPROFILE%', 'cmd.exe')
|
||||
|
||||
Execute (Cleanup):
|
||||
unlet g:dir
|
||||
Restore
|
||||
|
||||
@@ -683,62 +683,10 @@ class TestGoFZF < TestBase
|
||||
]
|
||||
assert_equal output, `#{FZF} -fh < #{tempname}`.split($/)
|
||||
|
||||
output = %w[
|
||||
1234567:h
|
||||
12345:he
|
||||
1:hell
|
||||
123:hello
|
||||
]
|
||||
# Since 0.16.8, --nth doesn't affect --tiebreak
|
||||
assert_equal output, `#{FZF} -fh -n2 -d: < #{tempname}`.split($/)
|
||||
end
|
||||
|
||||
def test_tiebreak_length_with_nth_trim_length
|
||||
input = [
|
||||
"apple juice bottle 1",
|
||||
"apple ui bottle 2",
|
||||
"app ice bottle 3",
|
||||
"app ic bottle 4",
|
||||
]
|
||||
writelines tempname, input
|
||||
|
||||
# len(1)
|
||||
output = [
|
||||
"app ice bottle 3",
|
||||
"app ic bottle 4",
|
||||
"apple juice bottle 1",
|
||||
"apple ui bottle 2",
|
||||
]
|
||||
assert_equal output, `#{FZF} -fa -n1 < #{tempname}`.split($/)
|
||||
|
||||
# len(1 ~ 2)
|
||||
output = [
|
||||
"app ic bottle 4",
|
||||
"app ice bottle 3",
|
||||
"apple ui bottle 2",
|
||||
"apple juice bottle 1",
|
||||
]
|
||||
assert_equal output, `#{FZF} -fai -n1..2 < #{tempname}`.split($/)
|
||||
|
||||
# len(1) + len(2)
|
||||
output = [
|
||||
"app ic bottle 4",
|
||||
"app ice bottle 3",
|
||||
"apple ui bottle 2",
|
||||
"apple juice bottle 1",
|
||||
]
|
||||
assert_equal output, `#{FZF} -x -f"a i" -n1,2 < #{tempname}`.split($/)
|
||||
|
||||
# len(2)
|
||||
output = [
|
||||
"app ic bottle 4",
|
||||
"app ice bottle 3",
|
||||
"apple ui bottle 2",
|
||||
"apple juice bottle 1",
|
||||
]
|
||||
assert_equal output, `#{FZF} -fi -n2 < #{tempname}`.split($/)
|
||||
assert_equal output, `#{FZF} -fi -n2,1..2 < #{tempname}`.split($/)
|
||||
end
|
||||
|
||||
def test_invalid_cache
|
||||
tmux.send_keys "(echo d; echo D; echo x) | #{fzf '-q d'}", :Enter
|
||||
tmux.until { |lines| lines[-2].include? '2/3' }
|
||||
@@ -975,15 +923,15 @@ class TestGoFZF < TestBase
|
||||
tmux.until { |lines| lines[-10].start_with? '>' }
|
||||
tmux.send_keys :Down
|
||||
tmux.until { |lines| lines[-9].start_with? '>' }
|
||||
tmux.send_keys :PgUp
|
||||
tmux.send_keys :Up
|
||||
tmux.until { |lines| lines[-10].start_with? '>' }
|
||||
tmux.send_keys :PgUp
|
||||
tmux.until { |lines| lines[-3].start_with? '>' }
|
||||
tmux.until { |lines| lines[-10].start_with? '>' }
|
||||
tmux.send_keys :Up
|
||||
tmux.until { |lines| lines[-4].start_with? '>' }
|
||||
tmux.send_keys :PgDn
|
||||
tmux.until { |lines| lines[-3].start_with? '>' }
|
||||
tmux.send_keys :PgDn
|
||||
tmux.until { |lines| lines[-3].start_with? '>' }
|
||||
tmux.send_keys :Down
|
||||
tmux.until { |lines| lines[-10].start_with? '>' }
|
||||
end
|
||||
|
||||
@@ -1304,6 +1252,20 @@ class TestGoFZF < TestBase
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| lines[-2] == prompt && lines[-1] == '1' }
|
||||
end
|
||||
|
||||
def test_change_top
|
||||
tmux.send_keys %[seq 1000 | #{FZF} --bind change:top], :Enter
|
||||
tmux.until { |lines| lines.match_count == 1000 }
|
||||
tmux.send_keys :Up
|
||||
tmux.until { |lines| lines[-4] == '> 2' }
|
||||
tmux.send_keys 1
|
||||
tmux.until { |lines| lines[-3] == '> 1' }
|
||||
tmux.send_keys :Up
|
||||
tmux.until { |lines| lines[-4] == '> 10' }
|
||||
tmux.send_keys 1
|
||||
tmux.until { |lines| lines[-3] == '> 11' }
|
||||
tmux.send_keys :Enter
|
||||
end
|
||||
end
|
||||
|
||||
module TestShell
|
||||
|
||||
Reference in New Issue
Block a user